This project demonstrates how to implement authentication with Zitadel in a full-stack application using:
- Frontend: React with TanStack Router and Bun
- Backend: Golang with Echo
├── frontend/ # React application with TanStack Router
│ ├── src/
│ │ ├── context/ # Authentication context and provider
│ │ ├── routes/ # Application routes and pages
│ │ ├── components/ # Reusable UI components
│ │ ├── services/ # API client and services
│ │ └── main.tsx # Application entry point
│ └── .env # Frontend environment variables
├── backend/ # Golang Echo server
│ ├── middleware/ # Authentication middleware
│ ├── main.go # Server entry point
│ └── .env # Backend environment variables
- Create a Zitadel account at https://zitadel.com/
- Create a new project in Zitadel
- Set up an application:
- Type: Web
- Redirect URIs:
http://localhost:5173/callback - Allowed Origins:
http://localhost:5173 - Grant Types: Authorization Code with PKCE
- Create an API resource (for the backend authorization)
- Note your Client ID and API Audience values
VITE_ZITADEL_DOMAIN=http://localhost:8080
VITE_ZITADEL_CLIENT_ID=your_client_id
VITE_API_URL=http://localhost:8000/api
VITE_API_AUDIENCE=your_api_audience
ZITADEL_DOMAIN=http://localhost:8080
JWT_AUDIENCE=your_api_audience
JWT_ISSUER=http://localhost:8080
PORT=8000
-
Navigate to the backend directory:
cd backend -
Install dependencies:
go mod download -
Run the server:
go run main.goThe backend will start on port 8000 by default.
-
Navigate to the frontend directory:
cd frontend -
Install dependencies:
bun install -
Start the development server:
bun devThe frontend will start on port 5173 by default.
- The user visits the application and clicks "Login"
- The frontend redirects to Zitadel's authorization endpoint with:
- PKCE code challenge for security
- Client ID, redirect URI, and requested scopes
- User authenticates with Zitadel
- Zitadel redirects back to
/callbackwith an authorization code - The frontend exchanges the code for access and ID tokens
- Tokens are stored in local storage
- Protected routes use the AuthProvider context to check authentication status
- API calls to the backend include the access token in the Authorization header
- The backend validates the JWT token using Zitadel's JWKS endpoint
- Frontend: Routes with the
_authenticatedprefix are protected and require a valid session - Backend: Endpoints under the
/apipath (except/api/health) require valid JWT authentication
- AuthProvider: Manages authentication state, token storage and renewal
- Protected Routes: Routes that require authentication
- API Client: Makes authenticated requests to the backend
- JWT Middleware: Validates tokens and extracts user information
- Protected API Endpoints: Only accessible with valid authentication
To modify the application:
- Frontend routes are defined in the
src/routesdirectory - Backend endpoints are defined in
main.go - Authentication logic is in
frontend/src/context/AuthProvider.tsxandbackend/middleware/middleware.go
- Check browser console for frontend errors
- Verify backend logs for authentication issues
- Ensure Zitadel configuration matches your .env files
- Confirm that redirect URIs match exactly between code and Zitadel configuration
Feel free to submit issues or pull requests for improvements or bug fixes.