A modern, production-ready example demonstrating how to implement Zitadel authentication in a SvelteKit application using OpenID Connect (OIDC) flow.
This project showcases how to integrate Zitadel's authentication service into a SvelteKit application using the OIDC flow with PKCE (Proof Key for Code Exchange) for enhanced security. PKCE eliminates the need for client secrets in public clients, provides additional protection against authorization code interception attacks, and follows OAuth 2.1 security best practices.
Why PKCE?
- β No client secrets stored in browser/app
- β Protection against code interception attacks
- β Perfect for Single Page Applications (SPAs)
- β Enhanced security for mobile and public clients
- β OAuth 2.1 compliant
- Node.js 18+
- pnpm
- Just command runner
- Docker (or Podman, Orbstack) and Docker Compose
- A Zitadel instance (local via docker-compose or cloud)
- Frontend: SvelteKit, TypeScript, Auth.js
- Authentication: Zitadel
- Task Runner: Just
- Local Services: Docker Compose
- Educational Resource: Learn how to properly implement OIDC authentication in SvelteKit
- Production-Ready: Follow security best practices and modern development patterns
- Fully Typed: Complete TypeScript support for better developer experience
- Well-Documented: Extensive comments and documentation throughout the codebase
- β OIDC Authentication Flow
- β Protected Routes
- β Session Management
- β Automatic Token Refresh
- β Logout Handling
- β User Profile Display
- β TypeScript Support
- β Responsive UI
git clone https://github.com/robots4life/zitadel-sveltekit.git
cd zitadel-sveltekit
just install
just start-zitadel
cp .env.example .env.local
5. Configure your environment variables (see Configuration)
just dev
- Start Zitadel locally:
just zitadel-up
- Access Zitadel Console: http://localhost:8080
- Default credentials:
zitadel-admin@zitadel.localhost
/Password1!
- Default credentials:
Create a .env.local
file with the following variables:
# Zitadel Configuration (Local)
ZITADEL_ISSUER=http://localhost:8080
ZITADEL_CLIENT_ID=your-client-id
# Zitadel Configuration (Cloud - Alternative)
# ZITADEL_ISSUER=https://your-instance.zitadel.cloud
# ZITADEL_CLIENT_ID=your-client-id
# Application URLs
PUBLIC_BASE_URL=http://localhost:5173
REDIRECT_URI=http://localhost:5173/auth/callback
# Session Secret (for server-side session storage)
AUTH_SECRET=your-super-secret-jwt-secret
- Create a Project in your Zitadel console
- Add an Application with the following settings:
- Application Type:
Single Page Application (SPA)
orNative
- Authentication Method:
PKCE
- Redirect URIs:
http://localhost:5173/auth/callback
- Post Logout URIs:
http://localhost:5173
- Grant Types:
Authorization Code
- Response Types:
Code
- Important: No client secret is generated or needed
- Application Type:
This project uses Just for task automation. Run just --list
to see all available commands.
# Install dependencies
just install
# Start development server
just dev
# Run development server with host binding
just dev-host
# Build the application
just build
# Preview production build
just preview
# Start Zitadel locally
just zitadel-up
# Stop Zitadel
just zitadel-down
# View Zitadel logs
just zitadel-logs
# Restart Zitadel services
just zitadel-restart
# Reset Zitadel data (β οΈ destroys all data)
just zitadel-reset
# Run type checking
just check
# Format code
just format
# Run linting
just lint
# Clean build artifacts
just clean
To Be Done (TBD) Repository Layout
- Login Initiation: User clicks login, app generates PKCE code challenge
- Authorization: User redirected to Zitadel with code challenge
- User Authentication: User authenticates with Zitadel
- Callback: Zitadel redirects back with authorization code
- Token Exchange: App exchanges code + code verifier for tokens
- Session Creation: User session established (no client secret needed)
- Protected Access: User can access protected routes
PKCE Benefits:
- No client secret required (perfect for SPAs and public clients)
- Protection against authorization code interception
- Enhanced security for mobile and browser-based applications
Work In Progress (WIP)
- No Client Secrets: Eliminates secret storage in public clients
- Code Interception Protection: Dynamic code verifiers prevent replay attacks
- OAuth 2.1 Compliance: Follows latest security recommendations
- Perfect Forward Secrecy: Each authentication flow uses unique codes
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature
- Commit changes:
git commit -m 'Add amazing feature'
- Push to branch:
git push origin feature/amazing-feature
- Open a Pull Request
- Follow TypeScript best practices
- Add tests for new features
- Update documentation as needed
- Ensure responsive design
- Test authentication flows thoroughly
This project is licensed under the MIT License - see the LICENSE file for details.
Zitadel not accessible
# Check if services are running
just zitadel-logs
# Restart services
just zitadel-restart
Authentication redirect issues
- Ensure redirect URIs match exactly in Zitadel console
- Check that
PUBLIC_BASE_URL
is correct in.env.local
CORS errors
- Verify Zitadel allowed origins include
http://localhost:5173