ZitAuth is a centralized authentication gateway that transforms Zitadel integration from a headache into a breeze. One clean API for all your appsβweb, mobile, and backend services.
Why ZitAuth? Because your applications shouldn't care about JWT validation, JWKS endpoints, or OIDC flows. They should just work.
π Bulletproof User Authentication: OIDC login with PKCE flow
π€ Effortless Service-to-Service Auth: M2M authentication with JWT Bearer Grant
β
One-Stop Token Validation: Single /validate
endpoint handles all tokens
π― Ready-to-Run Examples: Complete user and machine authentication simulation
π Dual Implementation: Available in both Python (FastAPI) and Node.js (Express)
graph TD
subgraph "Clients"
User[π€ User]
M2MClient[π» Backend Service]
end
subgraph "Application Layer"
SPA[π SPA / Web App]
ProtectedAPI[π¦ Your Protected API]
end
subgraph "Authentication Core"
ZitAuth[π‘οΈ ZitAuth Gateway]
Zitadel[π Zitadel IdP]
end
%% User Authentication Flow
User -- "Initiates Login" --> SPA
SPA -- "Redirects for Auth" --> ZitAuth
ZitAuth -- "Handles OIDC Flow" --> Zitadel
Zitadel -- "Authenticates & Issues Code" --> ZitAuth
ZitAuth -- "Exchanges Code for Token" --> Zitadel
ZitAuth -- "Returns Token" --> SPA
SPA -- "Calls API with User Token" --> ProtectedAPI
%% M2M Authentication Flow
M2MClient -- "Requests M2M Token" --> ZitAuth
ZitAuth -- "Handles JWT Bearer Grant" --> Zitadel
Zitadel -- "Issues M2M Token" --> ZitAuth
ZitAuth -- "Returns Token" --> M2MClient
M2MClient -- "Calls API with M2M Token" --> ProtectedAPI
%% Centralized Validation
ProtectedAPI -- "Validates Token via Gateway" --> ZitAuth
- Docker and Docker Compose
- Python 3.11+
pip
for installing dependencies
-
The provided
docker-compose.yaml
file is used to set up a local Zitadel instance. You can also use Zitadel hosted on the cloud.docker compose up -d
-
You can access the Zitadel Console at
http://localhost:8080
.
-
After starting Zitadel, log in to the console at http://localhost:8080 using these creds:
- username:
[email protected]
- password:
Password1!
- username:
-
You will need to create a project and application within it (for mobile login flow) and a service user (for m2m flow) and configure login policies (Required for User registration)
Click to expand Zitadel Application Configuration
- In your project, create a new application of type
User Agent
- Select authentication method as
PKCE
, toggle theDevelopment mode
to allow redirect to http - Configure the redirect URI as
http://localhost:8000/api/v1/callback
- After creating the application, Go to the
Token Settings
tab and selectAuth Token Type
asJWT
- Go to the URLs tab and note down the urls and client id in .env file
Click to expand Zitadel Service User Configuration
- In the Zitadel console, go to
Users
Section and then to theService Users
tab - Click on the
New
button - Fill basic details and select the
Access Token Type
asJWT
- Go to
Keys
tab and add a new key of typeJSON
and download it - Save this file to a secure location in your project. Provide the path to this file in the
.env
file
Click to expand Zitadel Login Policy Configuration
- Navigate to the `Default Settings` setion on the zitadel console - Go to the `External Links` tab in the `Other` subsection - Configure `Link to Terms of Service` and `Link to Privacy Policy` (or use any dummy links for demo) - Click `Save` - In your project, create a new application of type
-
Create the
.env
file by copying the example:cp .env.example .env
-
Edit the
.env
file:- Fill the environment variables with the information received from the step before
-
Install dependencies:
pip install -r requirements.txt
The start.sh
script is provided to run the main components. You can inspect the script to see the individual commands.
Start the main services:
# This will start ZitAuth on port 8000
uvicorn python.main:app --reload --port 8000
Note - For running in nodejs, refer to nodejs/README.md
-
Start the SPA app:
uvicorn examples.spa_app.main:app --reload --port 3001
-
Open your browser and navigate to the SPA at
http://127.0.0.1:3001
-
Click the "Login via ZitAuth" button. You will be redirected to the Zitadel login page
-
Log in with a user or register one
-
After a successful login, you will be redirected back to the SPA, and the status will show "Access token received"
-
Click
Call Protected Endpoint
andFetch User Info
to test the authenticated API calls
-
Ensure the main services (ZitAuth Gateway and SPA App) are running
-
In a new terminal, run the M2M simulation script:
python examples/m2m_sim.py
-
Observe the logs. The script will request an M2M token, receive it, and use it to successfully call the protected API
.
βββ docker-compose.yaml # Sets up Zitadel server locally
βββ examples/
β βββ m2m_sim.py # Simulates the local service (M2M)
β βββ spa_app/ # Simulates the mobile/web application
βββ python/
β βββ client.py # The core ZitadelClient abstraction layer
β βββ main.py # The FastAPI service (ZitAuth Gateway)
β βββ utils.py # Helper functions
βββ .env.example # Template for environment variables
βββ requirements.txt # Python dependencies
βββ start.sh # Helper script to run services
Click to expand API Endpoint Documentation
Initiates the OIDC user login flow. This endpoint is intended to be used by a browser, which will be redirected.
- Description: Starts the user authentication process by redirecting the user to the Zitadel login page
- Request: No parameters or headers required
- Response (Success):
HTTP 302 Found
: A redirect to the Zitadel authorization endpoint
- Response (Error):
HTTP 400 Bad Request
: If there is an internal error generating the login URL
Handles the OIDC callback from Zitadel after a user authenticates. This endpoint is used by the browser as part of the redirect flow.
- Description: Zitadel redirects the user's browser to this endpoint after a successful login. The endpoint exchanges the received authorization
code
for an access token - Request:
- Query Parameters:
Parameter Description code
The authorization code issued by Zitadel state
The unique state string used to prevent CSRF attacks
- Query Parameters:
- Response (Success):
HTTP 302 Found
: Redirects the user's browser back to the SPA (SPA_ORIGIN
), with theaccess_token
included in the URL hash fragment
- Response (Error):
HTTP 400 Bad Request
: If thestate
is invalid, expired, or the token exchange fails
Issues a machine-to-machine (M2M) access token using a pre-configured service account.
- Description: Allows a trusted backend service to acquire an access token by handling the JWT Bearer Grant flow on behalf of the service
- Request: No parameters or headers required. The service authenticates itself by its ability to call this endpoint
- Response (Success):
HTTP 200 OK
- Body (JSON):
{ "access_token": "ey..." }
- Response (Error):
HTTP 500 Internal Server Error
: If the service account file is misconfigured or Zitadel rejects the request
Validates an access token and returns the authentication status.
- Description: A centralized endpoint for any service to delegate token validation. It checks the token's signature against Zitadel's public keys using its JWKS endpoint
- Request:
- Headers:
Header Description Authorization
Required. The bearer token. Must be in the format Bearer <token>
- Headers:
- Response (Success):
HTTP 200 OK
- Body (JSON): The decoded claims (payload) of the JWT
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022, "exp": 1516242622, "iss": "http://localhost:8080" }
- Response (Error):
HTTP 400 Bad Request
: If theAuthorization
header is missing or malformedHTTP 401 Unauthorized
: If the token is invalid (expired, bad signature, etc.)
Fetches the user profile from Zitadel's userinfo endpoint using a valid access token.
- Description: Acts as a secure proxy to Zitadel's userinfo endpoint
- Request:
- Headers:
Header Description Authorization
Required. The bearer token. Must be in the format Bearer <token>
- Headers:
- Response (Success):
HTTP 200 OK
- Body (JSON): The user profile information
{ "userinfo": { "sub": "1234567890", "name": "John Doe", "email": "[email protected]", "email_verified": true } }
- Response (Error):
HTTP 400 Bad Request
: If theAuthorization
header is missingHTTP 401 Unauthorized
: If the access token is invalid or does not have the required scopes