Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/zitadel/zitadel/llms.txt

Use this file to discover all available pages before exploring further.

OpenID Connect (OIDC) is an identity layer built on top of OAuth 2.0 that enables clients to verify the identity of users and obtain basic profile information. ZITADEL provides full support for the OpenID Connect 1.0 standard, making it the recommended protocol for modern applications.

Why use OIDC?

OIDC offers several advantages over older protocols like SAML:
  • Modern and flexible: Built for web, mobile, and single-page applications
  • JSON-based: Uses JWT tokens instead of XML, making it easier to parse and debug
  • Better mobile support: Designed with native apps and custom URL schemes in mind
  • Simpler implementation: Less complex than SAML while providing the same security guarantees
  • Industry standard: Widely adopted by modern identity providers and applications

OIDC Discovery

ZITADEL supports OpenID Connect Discovery, allowing applications to automatically configure themselves. The discovery endpoint is available at:
https://${CUSTOM_DOMAIN}/.well-known/openid-configuration
This endpoint returns metadata about ZITADEL’s OIDC implementation, including supported endpoints, grant types, response types, and more.

Authentication Flows

ZITADEL supports multiple OIDC authentication flows to accommodate different application types:

Authorization Code Flow

The Authorization Code Flow is recommended for server-side applications. It provides the highest level of security by keeping tokens away from the user agent. Flow steps:
  1. Redirect user to the authorization endpoint
  2. User authenticates and grants consent
  3. Authorization code is returned to your application
  4. Exchange the code for tokens at the token endpoint
Authorization endpoint:
GET https://${CUSTOM_DOMAIN}/oauth/v2/authorize
Required parameters:
ParameterDescription
client_idYour application’s client ID
redirect_uriCallback URI registered in ZITADEL
response_typeSet to code for authorization code flow
scopeSpace-delimited scopes, must include openid
Example authorization request:
GET /oauth/v2/authorize?
  client_id=170086824411201793@yourapp&
  redirect_uri=https://yourapp.example.com/auth/callback&
  response_type=code&
  scope=openid%20email%20profile&
  code_challenge=9az09PjcfuENS7oDK7jUd2xAWRb-B3N7Sr3kDoWECOY&
  code_challenge_method=S256&
  state=random_state_value
Token endpoint:
POST https://${CUSTOM_DOMAIN}/oauth/v2/token
Token exchange request:
curl -X POST "https://${CUSTOM_DOMAIN}/oauth/v2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=YOUR_AUTHORIZATION_CODE" \
  -d "redirect_uri=https://yourapp.example.com/auth/callback" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "code_verifier=YOUR_CODE_VERIFIER"

Authorization Code Flow with PKCE

Proof Key for Code Exchange (PKCE) is an extension to the authorization code flow designed for public clients (mobile and single-page applications) that cannot securely store a client secret. Additional parameters:
ParameterDescription
code_challengeSHA-256 hash of the code_verifier
code_challenge_methodMust be S256
code_verifierRandom string used in token exchange
Example with PKCE:
// Generate code verifier and challenge
const codeVerifier = generateRandomString(128);
const codeChallenge = await sha256(codeVerifier);

// Authorization request
window.location.href = `https://${CUSTOM_DOMAIN}/oauth/v2/authorize?
  client_id=${CLIENT_ID}&
  redirect_uri=${REDIRECT_URI}&
  response_type=code&
  scope=openid%20profile%20email&
  code_challenge=${codeChallenge}&
  code_challenge_method=S256&
  state=${state}`;

// Token exchange (after receiving code)
const response = await fetch('https://${CUSTOM_DOMAIN}/oauth/v2/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'authorization_code',
    code: authorizationCode,
    redirect_uri: REDIRECT_URI,
    client_id: CLIENT_ID,
    code_verifier: codeVerifier
  })
});

Implicit Flow

The implicit flow returns tokens directly from the authorization endpoint. This flow is deprecated and should only be used for legacy applications. Response types:
  • id_token: Returns only an ID token
  • id_token token: Returns both ID token and access token

Refresh Token Flow

Refresh tokens allow applications to obtain new access tokens without requiring user interaction.
curl -X POST "https://${CUSTOM_DOMAIN}/oauth/v2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=YOUR_REFRESH_TOKEN" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"

Application Types

ZITADEL supports different OIDC application types, each with specific configurations:

Web Applications

  • Type: OIDC_APP_TYPE_WEB
  • Auth method: client_secret_basic, client_secret_post, or private_key_jwt
  • Recommended flow: Authorization code flow
  • Example: Server-side rendered applications, traditional web apps

User Agent Applications

  • Type: OIDC_APP_TYPE_USER_AGENT
  • Auth method: none (PKCE required)
  • Recommended flow: Authorization code flow with PKCE
  • Example: Single-page applications (React, Vue, Angular)

Native Applications

  • Type: OIDC_APP_TYPE_NATIVE
  • Auth method: none (PKCE required)
  • Recommended flow: Authorization code flow with PKCE
  • Example: Mobile apps (iOS, Android), desktop applications

Authentication Methods

ZITADEL supports multiple authentication methods for client authentication:

Client Secret Basic

curl -X POST "https://${CUSTOM_DOMAIN}/oauth/v2/token" \
  -u "CLIENT_ID:CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=YOUR_CODE" \
  -d "redirect_uri=YOUR_REDIRECT_URI"

Client Secret Post

curl -X POST "https://${CUSTOM_DOMAIN}/oauth/v2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=YOUR_CODE" \
  -d "redirect_uri=YOUR_REDIRECT_URI" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"

Private Key JWT

Use asymmetric keys for client authentication, providing enhanced security.
const jwt = require('jsonwebtoken');
const privateKey = fs.readFileSync('private-key.pem');

const assertion = jwt.sign(
  {
    iss: CLIENT_ID,
    sub: CLIENT_ID,
    aud: `https://${CUSTOM_DOMAIN}/oauth/v2/token`,
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + 3600
  },
  privateKey,
  { algorithm: 'RS256' }
);

const response = await fetch(`https://${CUSTOM_DOMAIN}/oauth/v2/token`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'authorization_code',
    code: authorizationCode,
    redirect_uri: REDIRECT_URI,
    client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
    client_assertion: assertion
  })
});

None (PKCE)

No client authentication, relies on PKCE for security. See Authorization Code Flow with PKCE above.

Scopes and Claims

Standard Scopes

ScopeClaims Returned
openidRequired for OIDC, returns sub claim
profilename, family_name, given_name, middle_name, nickname, preferred_username, profile, picture, website, gender, birthdate, zoneinfo, locale, updated_at
emailemail, email_verified
phonephone_number, phone_number_verified
addressaddress (structured claim)
offline_accessEnables refresh token issuance

ZITADEL-Specific Scopes

ScopeDescription
urn:zitadel:iam:org:project:id:${PROJECT_ID}:audAdds project ID to audience
urn:zitadel:iam:org:id:${ORG_ID}Filters users by organization
urn:zitadel:iam:user:metadataReturns user metadata
urn:zitadel:iam:user:resourceownerReturns resource owner information

Token Types

ZITADEL supports two access token types:

Bearer Token (Opaque)

  • Type: OIDC_TOKEN_TYPE_BEARER
  • Format: Opaque reference token
  • Use case: When you want to use the introspection endpoint
  • Validation: Must be validated via introspection endpoint

JWT Token

  • Type: OIDC_TOKEN_TYPE_JWT
  • Format: Self-contained JSON Web Token
  • Use case: Stateless validation, microservices
  • Validation: Can be validated locally using ZITADEL’s public keys

Key Endpoints

Authorization Endpoint

GET https://${CUSTOM_DOMAIN}/oauth/v2/authorize
Initiates the authentication flow.

Token Endpoint

POST https://${CUSTOM_DOMAIN}/oauth/v2/token
Exchanges authorization codes for tokens or refreshes access tokens.

UserInfo Endpoint

GET https://${CUSTOM_DOMAIN}/oidc/v1/userinfo
Returns claims about the authenticated user.
curl -X GET "https://${CUSTOM_DOMAIN}/oidc/v1/userinfo" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Introspection Endpoint

POST https://${CUSTOM_DOMAIN}/oauth/v2/introspect
Validates and retrieves information about a token.
curl -X POST "https://${CUSTOM_DOMAIN}/oauth/v2/introspect" \
  -u "CLIENT_ID:CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=YOUR_TOKEN"

Revocation Endpoint

POST https://${CUSTOM_DOMAIN}/oauth/v2/revoke
Revokes access or refresh tokens.
curl -X POST "https://${CUSTOM_DOMAIN}/oauth/v2/revoke" \
  -u "CLIENT_ID:CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=YOUR_TOKEN"

End Session Endpoint

GET https://${CUSTOM_DOMAIN}/oidc/v1/end_session
Logs out the user and ends their session.
GET /oidc/v1/end_session?
  id_token_hint=YOUR_ID_TOKEN&
  post_logout_redirect_uri=https://yourapp.example.com/logged-out

Advanced Features

Token Customization

ZITADEL provides several options to customize token behavior:
  • ID Token Role Assertion: Include roles in ID token claims
  • Access Token Role Assertion: Include roles in access token (JWT only)
  • ID Token UserInfo Assertion: Include profile claims in ID token even when access token is issued
  • Clock Skew: Compensate for time differences between servers

Back-Channel Logout

ZITADEL supports OIDC Back-Channel Logout, allowing applications to be notified when a user’s session is terminated. Configuration: Set the back_channel_logout_uri in your OIDC application configuration:
{
  "back_channel_logout_uri": "https://yourapp.example.com/auth/backchannel-logout"
}

Response Modes

ModeDescription
queryParameters in URL query string (default for code response type)
fragmentParameters in URL fragment (default for id_token response type)
form_postParameters sent via HTTP POST

Configuration Example

Here’s how to create an OIDC application in ZITADEL:

    Best Practices

    1. Always use HTTPS: Never use OIDC over unencrypted connections
    2. Validate state parameter: Protect against CSRF attacks
    3. Use PKCE for public clients: Essential for mobile and SPA applications
    4. Implement proper token storage: Use secure storage mechanisms
    5. Validate tokens: Always verify JWT signatures and claims
    6. Use short-lived access tokens: Reduce the impact of token theft
    7. Implement token refresh: Use refresh tokens for long-lived sessions
    8. Handle errors gracefully: Provide clear error messages to users
    9. Use appropriate scopes: Request only the scopes you need
    10. Monitor token usage: Track and audit token issuance and usage

    Troubleshooting

    Common Issues

    Invalid redirect_uri
    • Ensure the redirect URI exactly matches one registered in ZITADEL
    • Check for trailing slashes and URL encoding
    Invalid client credentials
    • Verify client_id and client_secret are correct
    • Check authentication method matches application configuration
    Token validation fails
    • Ensure you’re using the correct public keys from the JWKS endpoint
    • Verify the token hasn’t expired
    • Check the audience claim matches your client_id
    PKCE validation fails
    • Ensure code_verifier matches the code_challenge
    • Use S256 challenge method, not plain

    Resources