docs: introduce versioned docs and migrate to fuma (#11166)

## Todos for release

- [x] Configure Env in docs project on vercel
- [x] Configure Root Path in the docs project on vercel
- [ ] Remove old CSP https://github.com/zitadel/website/pull/1592

## What we did

This pull request migrates the project documentation from the old
`docs/` directory to the new `apps/docs/` directory, introduces a new
documentation system built with Next.js and Fumadocs, and updates all
relevant references, configuration files, and documentation to reflect
this change. It also adds new configuration and ignore files for the new
documentation app, updates CI and linting to exclude the new docs from
certain checks, and revises the contributing guidelines accordingly.

**Documentation System Migration and New Docs App**

* Migrated all documentation from `docs/` to `apps/docs/`, and updated
all references in `README.md`, `CONTRIBUTING.md`, and other files to
point to the new location.
[[1]](diffhunk://#diff-eca12c0a30e25b4b46522ebf89465a03ba72a03f540796c979137931d8f92055L585-L640)
[[2]](diffhunk://#diff-eca12c0a30e25b4b46522ebf89465a03ba72a03f540796c979137931d8f92055L660-R607)
[[3]](diffhunk://#diff-eca12c0a30e25b4b46522ebf89465a03ba72a03f540796c979137931d8f92055L740-R687)
[[4]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L2-R3)
[[5]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L30-R30)
* Added a new Next.js/Fumadocs-based documentation app under
`apps/docs/`, including core app files, layouts, routing, search API,
and a comprehensive `README.md` with development and contribution
instructions.
[[1]](diffhunk://#diff-5a1b07344a2c1b4d3f37b23ff1388b62cd9f57dea3c1cd23d8a0412b7602b132R1-R74)
[[2]](diffhunk://#diff-462b9ad1eabbb7d1c29bb9c36e4931eb180fd7190900e6d2babf8f4d66ad1c28R1-R39)
[[3]](diffhunk://#diff-e16ae25660ded787b10ac35dea96d5ecaacf895dae0afc8a9bd4382dc79a8c87R1-R7)
[apps/docs/app/[[...slug]]/layout.tsxR1-R81](diffhunk://#diff-59e08acde4e805b7aeccef1dcf98f1d71dfc550777e6b9402085cee0e9fa4e0aR1-R81),
[apps/docs/app/[[...slug]]/page.tsxR1-R79](diffhunk://#diff-e5df3f80d0fa01e12d63d81f29c57a9d14e846c78fd3beabb2ef768e38fd9580R1-R79),
[[4]](diffhunk://#diff-389b34918e040cacaa87cd7201ffa462cc2b0b716736f537e3d3c660ac69353bR1-R7)
[[5]](diffhunk://#diff-d3b03416d1c457b19f1c27b26f2db741412df841b875c26ccadc36e4522247f4R1-R29)
[[6]](diffhunk://#diff-c8fb8339570a5305809be7c618e14705fd86390dc278e6ce8ba224a7bc8b0c3cR1-R25)

**Configuration and Tooling Updates**

* Updated `.github/workflows/codeql.yml`, `.golangci.yaml`, and
`.github/dependabot.yml` to properly handle the new docs app: excluded
`apps/docs` from certain checks, added npm dependency updates for the
docs app, and excluded generated content.
[[1]](diffhunk://#diff-12783128521e452af0cfac94b99b8d250413c516ec71fe6d97dbea666ff7ba27L8-R14)
[[2]](diffhunk://#diff-9917ddc9f1c3304218f7269265b746d997c5c0615478177b5fceecd33ef47cb5R5-R6)
[[3]](diffhunk://#diff-9917ddc9f1c3304218f7269265b746d997c5c0615478177b5fceecd33ef47cb5R126-R129)
[[4]](diffhunk://#diff-dd4fbda47e51f1e35defb9275a9cd9c212ecde0b870cba89ddaaae65c5f3cd28R89-R106)
* Updated `.devcontainer/devcontainer.json` to use the latest Go 1.25.3
version for consistency.

**Licensing and Miscellaneous**

* Added `apps/docs/` to the list of licensed directories in
`LICENSING.md`.

These changes ensure the documentation is now maintained in a modern,
scalable system and all project tooling is updated to support the new
structure.

---------

Co-authored-by: Federico Coppede <[email protected]>
This commit is contained in:
Florian Forster
2026-01-29 08:38:36 -08:00
committed by GitHub
co-authored by Federico Coppede
parent 7b6395c348
commit d8ea041c56
2826 changed files with 78739 additions and 23571 deletions
@@ -0,0 +1,11 @@
| Parameter | Description |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| grant_type | Must be `urn:ietf:params:oauth:grant-type:token-exchange` |
| subject_token | A token that represents the identity of the party on behalf of whom the request is being made. |
| subject_token_type | An identifier that indicates the type of the token in the subject_token parameter. |
| actor_token | Optional. A token that represents the identity of the acting party. In ZITADEL this the impersonator. |
| actor_token_type | An identifier that indicates the type of the token in the actor_token parameter. Required when actor_token is provided |
| requested_token_type | Optional. An identifier that indicates the type of the token requested. Defaults to access token if not provided. |
| scope | [Scopes](/apis/openidoauth/scopes) you would like to request from ZITADEL for the requested token. Scopes are space delimited, e.g. `openid email profile`. |
| audience | Optional. Must be a subset of the combined audiences from both subject and actor tokens. |
| resource | Currently not supported |
@@ -0,0 +1,9 @@
| Property | Description |
| ----------------- | ------------------------------------------------------------------------------------ |
| access_token | An `access_token` as opaque token or JWT for the subject user |
| token_type | Type of the `access_token`. Value can be `Bearer` or `N_A` |
| issued_token_type | [Token type](#token-types) of the returned token, matches the `requested_token_type` |
| refresh_token | A refresh token if the `offline_access` scope was requested |
| id_token | An ID Token of the subject user, only with `openid` scope |
| expires_in | Number of second until the expiration of the `access_token` |
| scope | Scopes of the `access_token`. These might differ from the provided `scope` parameter |
@@ -0,0 +1,9 @@
The following table provides a matrix of supported token type parameter and responses for Token Exchange.
| Identifier | subject_token | actor_token | requested_token_type |
| ------------------------------------------------ | ------------------------------------------------------------ | ------------- | -------------------- |
| `urn:ietf:params:oauth:token-type:access_token` | JWT or Opaque | JWT or Opaque | Opaque only |
| `urn:ietf:params:oauth:token-type:refresh_token` | Not allowed | Not allowed | Not allowed |
| `urn:ietf:params:oauth:token-type:id_token` | Allowed | Allowed | Allowed |
| `urn:ietf:params:oauth:token-type:jwt` | JWT signed by client, only in combination with `actor_token` | Not allowed | Access Token as JWT |
| `urn:zitadel:params:oauth:token-type:user_id` | user ID as string, only in combination with `actor_token` | Not allowed | Not allowed |
@@ -0,0 +1,72 @@
---
title: Authentication Methods in ZITADEL
sidebar_label: Authentication Methods
---
## Client Secret Basic
When using `client_secret_basic` on token or introspection endpoints, provide an`Authorization` header with a Basic auth value in the following form:
```markdown
Authorization: "Basic " + base64( formUrlEncode(client_id) + ":" + formUrlEncode(client_secret) )
```
Given the client_id `78366401571920522@amce` and client_secret `veryweaksecret!`, this would result in the following `Authorization` header:
`Basic NzgzNjY0MDE1NzE5MjA1MjIlNDBhbWNlOnZlcnl3ZWFrc2VjcmV0JTIx`
## JWT with Private Key
When using `private_key_jwt` (`urn:ietf:params:oauth:client-assertion-type:jwt-bearer`) for token or introspection endpoints, provide a JWT as assertion generated with the following structure and signed with a downloaded key:
---
Key JSON
| Key | Example | Description |
|:---------|:--------------------------------------------------------------------|:-------------------------------------------------------------------------------|
| type | `"application"` | The type of account, right now only application is valid |
| keyId | `"81693565968962154"` | This is unique ID of the key |
| key | `"-----BEGIN RSA PRIVATE KEY-----...-----END RSA PRIVATE KEY-----"` | The private key generated by ZITADEL, this can not be regenerated! |
| clientId | `78366401571920522@acme` | The client_id of the application, this is the same as the subject from tokens |
| appId | `78366403256846242` | The id of the application (just for completeness, not used for JWT) |
```json
{
"type": "application",
"keyId": "81693565968962154",
"key": "-----BEGIN RSA PRIVATE KEY-----...-----END RSA PRIVATE KEY-----",
"clientId": "78366401571920522@acme",
"appId": "78366403256846242"
}
```
---
JWT
| Claim | Example | Description |
|:------|:---------------------------|:----------------------------------------------------------------------------------------------------------------|
| aud | `"https://${CUSTOM_DOMAIN}"` | String or Array of intended audiences MUST include ZITADEL's issuing domain |
| exp | `1605183582` | Unix timestamp of the expiry |
| iat | `1605179982` | Unix timestamp of the creation singing time of the JWT, MUST NOT be older than 1h |
| iss | `"78366401571920522@acme"` | String which represents the requesting party (owner of the key), normally the `clientID` from the json key file |
| sub | `"78366401571920522@acme"` | The subject ID of the application, normally the `clientID` from the json key file |
```json
{
"iss": "78366401571920522@acme",
"sub": "78366401571920522@acme",
"aud": "https://${CUSTOM_DOMAIN}",
"exp": 1605183582,
"iat": 1605179982
}
```
> To identify your key, it is necessary that you provide a JWT with a `kid` header claim representing your keyId from the Key JSON:
>
> ```json
> {
> "alg": "RS256",
> "kid": "81693565968962154"
> }
> ```
@@ -0,0 +1,130 @@
---
title: ZITADEL OIDC Authentication Request Playground
sidebar_label: OIDC Playground
---
import { ExternalLink } from "lucide-react";
The OIDC Playground is for testing OpenID Authentication Requests, giving you more insight how OpenID Connect works and how you can customize ZITADEL behavior with different parameters.
An OpenID Connect (OIDC) [authentication request](https://openid.net/specs/openid-connect-core-1_0.html) is an OAuth 2.0 Authorization Request using additional parameters and scopes to request that the end-user be authenticated by ZITADEL.
<a href="https://zitadel.com/playgrounds/oidc" target="_blank" className="flex items-center gap-2 text-primary hover:underline">
Go to the OIDC Playground <ExternalLink className="h-4 w-4" />
</a>
## Why this OIDC playground?
Currently ZITADEL requires human users to authenticate trough the hosted login page.
Your application should initiate a login by issuing an authentication request and redirecting the user to the login page. You can customize the behavior of ZITADEL by providing additional parameters and scopes in the request.
This playground should help you to initially craft an authentication request and to explore the behavior of ZITADEL in more depth.
## Request parameters explained
Not all request parameters are available in the playground. Please refer to the full documentation of the [authorization endpoint](/apis/openidoauth/endpoints#authorization-endpoint).
### Your Domain
The <span className="text-yellow-500">Custom Domain</span> to your ZITADEL instance. Use the base-path, the playground will add the required path to the request.
### Required Parameters
<p>
<span className="text-green-500">Client ID</span> is the resource id of an
application. It's the application where you want your users to login. You can
find the resource id in the Console. When using organization grants, use the
client id from the origin organization.
</p>
<p>
<span className="text-blue-500">Redirect URI</span> be one of the
pre-configured redirect uris for your application. You must add the redirect
uri for your application, else you will receive an error.
</p>
<p>
<span className="text-orange-500">Response Type</span> defines whether a code,
id_token token or just id_token will be returned. Most use cases will need
code.
</p>
More in the <a href="/apis/openidoauth/endpoints#required-request-parameters">documentation</a> about required Parameters.
### Authentication methods
Depending on the authentication and authorization flow of your application you might need to append some information to the authentication request.
<span className="text-teal-600">Authentication method</span> "(none) PKCE" is recommended
for most application types. The playground appends automatically a code challenge
for PKCE flows.
You need to append a "Code Challenge" by providing a random <span className="text-teal-600">Code Verifier</span> that is being hashed and encoded in the request to the token endpoint, please see our [guide](/guides/integrate/login/oidc/oauth-recommended-flows#our-recommended-authorization-flows) for more details.
More in the [documentation](/apis/openidoauth/authn-methods) about authentication methods.
### Additional Parameters
<div>
<span className="text-cyan-500">Prompt</span> defines if and how the user
should be prompted on login. For example:
<ul>
<li>
<code>select_account</code>: user is prompted to select one of the
existing sessions or create a new one
</li>
<li>
<code>create</code>: present the register form
</li>
<li>
<code>login</code>: requires the user to re-authenticate
</li>
<li>
<code>none</code>: user must be authenticated without interaction, an
error is returned otherwise; use for silent-refresh
</li>
</ul>
</div>
<p>
<span className="text-rose-500">Login hint</span> must be a valid logon name
of a user. You can skip the account picker by providing the Login hint.
</p>
There are many more additional parameters. Please refer to the [documentation](/apis/openidoauth/endpoints#additional-parameters) about additional parameters.
## Standard Scopes
Used to request additional information from ZITADEL.
These scopes are defined in the OpenID Connect specification.
The `openid` scope is mandatory.
Not all scopes are available in the playground. Please refer to the full [documentation](/apis/openidoauth/scopes) for the exhaustive list of available standard and reserved scopes.
## Reserved Scopes
You can request additional information that is specific to ZITADEL or customize the behavior of ZITADEL by including reserved scopes.
Please refer to the [documentation](/apis/openidoauth/scopes#reserved-scopes) for a full list of available reserved scopes.
### Organization policies and branding
Enforce an organization's policies and branding as well as membership of the user by passing the scope `urn:zitadel:iam:org:id:{id}` with the required <span className="text-purple-500">Organization ID</span>.
Please refer to the full [guide on branding](/guides/manage/customize/branding).
### Get user metadata
Pass the scope `urn:zitadel:iam:user:metadata` to request a user's metadata.
Please refer to the full [guide on user-metadata](/guides/manage/customize/user-metadata) for further details.
### Access core apis
Calling the [core API](/apis/introduction) with the authenticated user, requires that the projectID of ZITADEL is included in the audience claim.
This can be achieved by adding the scope `urn:zitadel:iam:org:project:id:zitadel:aud` to your applications authorization request.
## How to use ZITADEL in your project
Please refer to our [guide](/guides/integrate/login/oidc/login-users) on how to login users.
OpenID Connect certified libraries should allow you to customize the parameters and define scopes for the authorization request. You can also continue by using one of our [example applications](/sdk-examples/introduction).
@@ -0,0 +1,118 @@
---
title: Claims in ZITADEL
sidebar_label: Claims
---
ZITADEL asserts claims on different places according to the corresponding specifications or project and clients settings.
Please check below the matrix for an overview where which scope is asserted.
| Claims | Userinfo | Introspection | ID Token | Access Token |
|:--------------------------------------------------|:---------------|-----------------------------------------|-------------------------------------------------|------------------------------------------------------|
| acr | No | No | Yes | No |
| act | No | After Token Exchange with `actor_token` | After Token Exchange with `actor_token` | When JWT and after Token Exchange with `actor_token` |
| address | When requested | When requested | When requested and response_type `id_token` | No |
| amr | No | No | Yes | No |
| aud | No | Yes | Yes | When JWT |
| auth_time | No | No | Yes | No |
| azp (client_id when Introspect) | No | Yes | Yes | When JWT |
| email | When requested | When requested | When requested and response_type `id_token` | No |
| email_verified | When requested | When requested | When requested and response_type `id_token` | No |
| exp | No | Yes | Yes | When JWT |
| family_name | When requested | When requested | When requested and response_type `id_token` | No |
| gender | When requested | When requested | When requested and response_type `id_token` | No |
| given_name | When requested | When requested | When requested and response_type `id_token` | No |
| iat | No | Yes | Yes | When JWT |
| iss | No | Yes | Yes | When JWT |
| jti | No | Yes | No | When JWT |
| locale | When requested | When requested | When requested and response_type `id_token` | No |
| name | When requested | When requested | When requested and response_type `id_token` | No |
| nbf | No | Yes | No | When JWT |
| nonce | No | No | When provided in the authorization request [^1] | No |
| phone | When requested | When requested | When requested and response_type `id_token` | No |
| phone_verified | When requested | When requested | When requested and response_type `id_token` | No |
| preferred_username (username when Introspect) | When requested | When requested | Yes | No |
| sid | No | No | Yes | No |
| sub | Yes | Yes | Yes | When JWT |
| urn:zitadel:iam:org:domain:primary:\{domainname} | When requested | When requested | When requested | When JWT and requested |
| urn:zitadel:iam:org:project:roles | When requested | When requested | When requested or configured | When JWT and requested or configured |
| urn:zitadel:iam:user:metadata | When requested | When requested | When requested | When JWT and requested |
| urn:zitadel:iam:user:resourceowner:id | When requested | When requested | When requested | When JWT and requested |
| urn:zitadel:iam:user:resourceowner:name | When requested | When requested | When requested | When JWT and requested |
| urn:zitadel:iam:user:resourceowner:primary_domain | When requested | When requested | When requested | When JWT and requested |
[^1]: The nonce can also be used to distinguish between an id_token and a logout_token as latter must never include a nonce.
## Standard Claims
| Claims | Example | Description |
|:-------------------|:---------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| acr | TBA | TBA |
| act | `{"iss": "${CUSTOM_DOMAIN}","sub": "259241944654282754"}` | JSON object describing the actor from the `actor_token` after [token exchange](/guides/integrate/token-exchange#actor-token) |
| address | `Lerchenfeldstrasse 3, 9014 St. Gallen` | TBA |
| amr | `pwd mfa` | Authentication Method References as defined in [RFC8176](https://tools.ietf.org/html/rfc8176) <br/> `password` value is deprecated, please check `pwd` |
| aud | `69234237810729019` | The audience of the token, by default all client id's and the project id are included |
| auth_time | `1311280969` | Unix time of the authentication |
| azp | `69234237810729234` | Client id of the client who requested the token |
| email | `[email protected]` | Email Address of the subject |
| email_verified | `true` | Boolean if the email was verified by ZITADEL |
| events | `{ "http://schemas.openid.net/event/backchannel-logout": {} }` | Security Events such as Back-Channel Logout |
| exp | `1311281970` | Time the token expires (as unix time) |
| family_name | `Runner` | Last name of the subject |
| family_name | `Runner` | Last name of the subject |
| gender | `other` | Gender of the subject |
| given_name | `Road` | First name of the subject |
| given_name | `Road` | First name of the subject |
| iat | `1311280970` | Time of the token was issued at (as unix time) |
| iss | `${CUSTOM_DOMAIN}` | Issuing domain of a token |
| jti | `69234237813329048` | Unique id of the token |
| locale | `en` | Language from the subject |
| name | `Road Runner` | The subjects full name |
| nbf | `1311280970` | Time the token must not be used before (as unix time) |
| nonce | `blQtVEJHNTF0WHhFQmhqZ0RqeHJsdzdkd2d...` | The nonce provided by the client |
| phone | `+41 79 XXX XX XX` | Phone number provided by the user |
| phone_verified | `true` | Boolean if the phone was verified by ZITADEL |
| preferred_username | `[email protected]` | ZITADEL's login name of the user. Consist of `username@primarydomain` |
| sid | `291693710356251044` | String identifier for a session. This represents a session of a user agent for a logged-in end-User. Different sid values are used to identify distinct sessions at an OP. |
| sub | `77776025198584418` | Subject ID of the user |
## Custom Claims
Custom claims are being inserted into user tokens in addition to the standard claims.
Your app can use custom claims to handle more complex scenarios, such as restricting access based on these claims.
You can add custom claims using the [complement token flow](/apis/actions/complement-token) of the [actions feature](/guides/manage/console/actions-overview).
Multiple examples of Actions that result in custom claims can be found in our [Marketplace for ZITADEL Actions](https://github.com/zitadel/actions).
### Static values as custom claim
```javascript reference
https://github.com/zitadel/actions/blob/de69b56f6d0463817953b59a52ffd6afc6a366fb/examples/add_claim.js#L9-L11
```
### Metadata as custom claim
```javascript reference
https://github.com/zitadel/actions/blob/main/examples/add_metadata.js#L9-L15
```
### Format roles claims
```javascript reference
https://github.com/zitadel/actions/blob/main/examples/custom_roles.js#L20-L33
```
## Reserved Claims
ZITADEL reserves some claims to assert certain data. Please check out the [reserved scopes](./scopes#reserved-scopes).
| Claims | Example | Description |
|:--------------------------------------------------|:---------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| urn:zitadel:iam:action:\{actionname}:log | `{"urn:zitadel:iam:action:appendCustomClaims:log": ["test log", "another test log"]}` | This claim is set during Actions as a log, e.g. if two custom claims with the same keys are set. |
| urn:zitadel:iam:org:domain:primary:\{domainname} | `{"urn:zitadel:iam:org:domain:primary": "acme.ch"}` | This claim represents the primary domain of the organization the user belongs to. |
| urn:zitadel:iam:org:project:roles | `{"urn:zitadel:iam:org:project:roles": [ {"user": {"id1": "acme.zitade.ch", "id2": "caos.ch"} } ] }` | When roles are asserted, ZITADEL does this by providing the `id` and `primaryDomain` below the role. This gives you the option to check in which organization a user has the role on the current project (where your client belongs to). |
| urn:zitadel:iam:org:project:\{projectid}:roles | `{"urn:zitadel:iam:org:project:id3:roles": [ {"user": {"id1": "acme.zitade.ch", "id2": "caos.ch"} } ] }` | When roles are asserted, ZITADEL does this by providing the `id` and `primaryDomain` below the role. This gives you the option to check in which organization a user has the role on a specific project. |
| urn:zitadel:iam:user:metadata | `{"urn:zitadel:iam:user:metadata": [ {"key": "VmFsdWU=" } ] }` | The metadata claim will include all metadata of a user. The values are base64 encoded. |
| urn:zitadel:iam:user:resourceowner:id | `{"urn:zitadel:iam:user:resourceowner:id": "orgid"}` | This claim represents the user's organization ID. |
| urn:zitadel:iam:user:resourceowner:name | `{"urn:zitadel:iam:user:resourceowner:name": "ACME"}` | This claim represents the user's organization's name. |
| urn:zitadel:iam:user:resourceowner:primary_domain | `{"urn:zitadel:iam:user:resourceowner:primary_domain": "acme.ch"}` | This claim represents the user's organization's primary domain. |
@@ -0,0 +1,689 @@
---
title: OpenID Connect Endpoints in ZITADEL
sidebar_label: OpenID Connect Endpoints
---
import TokenExchangeRequest from "./_token_exchange_request.mdx";
import TokenExchangeResponse from "./_token_exchange_response.mdx";
import TokenExchangeTypes from "./_token_exchange_types.mdx";
## OpenID Connect 1.0 Discovery
The OpenID Connect Discovery Endpoint is located within the issuer domain.
This would give us `${CUSTOM_DOMAIN}/.well-known/openid-configuration`.
**Link to spec.** [OpenID Connect Discovery 1.0 incorporating errata set 1](https://openid.net/specs/openid-connect-discovery-1_0.html)
## authorization_endpoint
`${CUSTOM_DOMAIN}/oauth/v2/authorize`
<Callout>
The authorization_endpoint is located with the login page, due to the need of accessing the same cookie domain
</Callout>
The authorization_endpoint is the starting point for all initial user authentications. The user agent (browser) will be redirected to this endpoint to
authenticate the user in exchange for an authorization_code (authorization code flow) or tokens (implicit flow).
<details>
<summary>Links to specs</summary>
<ul>
<li>
<a href="https://datatracker.ietf.org/doc/html/rfc6749#section-3.1">
Section 3.1 of OAuth2.0 (RFC6749)
</a>
</li>
<li>
<a href="https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint">
Section 3.1.2 of OpenID Connect Core 1.0 incorporating errata set 1
</a>
</li>
</ul>
</details>
### Required request parameters
| Parameter | Description |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| client_id | The id of your client as shown in Console. |
| redirect_uri | Callback uri of the authorization request where the code or tokens will be sent to. Must match exactly one of the preregistered in Console. |
| response_type | Determines whether a `code`, `id_token token` or just `id_token` will be returned. Most use cases will need `code`. See flow guide for more info. |
| scope | `openid` is required, see [Scopes](./scopes) for more possible values. Scopes are space delimited, e.g. `openid email profile` |
<Callout>
Following the [OIDC Core 1.0 specs](https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims) whenever an access_token is issued,
the id_token will not contain any claims of the scopes `profile`, `email`, `phone` and `address`.
Send the access_token to the [userinfo_endpoint](#userinfo-endpoint) or [introspection_endpoint](#introspection-endpoint) the retrieve these claims
or set the `id_token_userinfo_assertion` Option ("User Info inside ID Token" in Management Console) to true.
</Callout>
Depending on your authorization method you will have to provide additional parameters or headers:
<Tabs
groupId="token-auth-methods"
defaultValue="client_secret_basic"
values={[
{label: 'client_secret_basic', value: 'client_secret_basic'},
{label: 'client_secret_post', value: 'client_secret_post'},
{label: 'none (PKCE)', value: 'none'},
{label: 'private_key_jwt', value: 'private_key_jwt'},
]}
>
<Tab value="client_secret_basic">
no additional parameters required
</Tab>
<Tab value="client_secret_post">
no additional parameters required
</Tab>
<Tab value="none">
| Parameter | Description |
| --------------------- | ----------------------------------------------------- |
| code_challenge | The SHA-256 value of the generated `code_verifier` |
| code_challenge_method | Method used to generate the challenge, must be `S256` |
see PKCE guide for more information
</Tab>
<Tab value="private_key_jwt">
no additional parameters required
</Tab>
</Tabs>
### Additional parameters
| Parameter | Description |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id_token_hint | Valid `id_token` (of an existing session) used to identity the subject. **SHOULD** be provided when using prompt `none`. |
| login_hint | A valid logon name of a user. Will be used for username inputs or preselecting a user on `select_account`. Be sure to encode the hint correctly using url encoding (especially when using `+` or alike in the loginname) |
| max_age | Seconds since the last active successful authentication of the user |
| nonce | Random string value to associate the client session with the ID Token and for replay attacks mitigation. **MUST** be provided when using **implicit flow**. |
| prompt | If the Auth Server prompts the user for (re)authentication. <br />no prompt: the user will have to choose a session if more than one session exists<br />`none`: user must be authenticated without interaction, an error is returned otherwise <br />`login`: user must reauthenticate / provide a user name <br />`select_account`: user is prompted to select one of the existing sessions or create a new one <br />`create`: the registration form will be displayed to the user directly |
| state | Opaque value used to maintain state between the request and the callback. Used for Cross-Site Request Forgery (CSRF) mitigation as well, therefore highly **recommended**. |
| ui_locales | Spaces delimited list of preferred locales for the login UI, e.g. `de-CH de en`. If none is provided or matches the possible locales provided by the login UI, the `accept-language` header of the browser will be taken into account. |
| response_mode | The mechanism to be used for returning parameters to the application. See [response modes](#response-modes) for valid values. Invalid values are ignored. |
#### Response modes
ZITADEL supports the following `response_mode` values. When no response mode is requested, the response mode is chosen based on the configured Response Type of the application.
ZITADEL supports the following `response_mode` values. When no response mode is requested, the response mode is chosen based on the configured Response Type of the application.
As per [OpenID Connect Core 1.0, Section 3.1.2.1](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest):
> The use of this parameter is NOT RECOMMENDED when the Response Mode that would be requested is the default mode specified for the Response Type.
| Response Mode | Description |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| query | Encode the returned parameters in the URL query string. This is the default when the Response type is `code`, for example [Web applications](/guides/manage/console/applications-overview#web). |
| fragment | Encode the returned parameters in the URL fragment. This is the default when the Response Type is `id_token`, for example implicit [User Agent apps](/guides/manage/console/applications-overview#user-agent). This mode will not work for server-side applications, because fragments are never sent by the browser to the server. |
| form_post[^1] | ZITADEL serves a small JavaScript to the browser which will send the returned parameters to the `redirect_uri` using HTTP POST. This mode only works for server-side applications and user agents which support / allow JavaScript. |
[^1]: Implements [OAuth 2.0 Form Post Response Mode](https://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html)
### Successful code response
When your `response_type` was `code` and no error occurred, the following response will be returned:
| Property | Description |
| -------- | ----------------------------------------------------------------------------- |
| code | Opaque string which will be necessary to request tokens on the token endpoint |
| state | Unmodified `state` parameter from the request |
### Successful implicit response
When your `response_type` was either `id_token` or `id_token token` and no error occurred, the following response will be returned:
| Property | Description |
| ------------ | ------------------------------------------------------------------------------------- |
| access_token | Only returned if `response_type` included `token` |
| expires_in | Number of second until the expiration of the `access_token` |
| id_token | An `id_token` of the authorized user |
| token_type | Type of the `access_token`. Value is always `Bearer` |
| scope | Scopes of the `access_token`. These might differ from the provided `scope` parameter. |
| state | Unmodified `state` parameter from the request |
### Error response
Regardless of the authorization flow chosen, if an error occurs the following response will be returned to the redirect_uri.
<Callout>
If the redirect_uri is not provided, was not registered or anything other prevents the auth server form returning the response to the client,
the error will be display directly to the user on the auth server
</Callout>
| Property | Description |
| ----------------- | -------------------------------------------------------------------- |
| error | An OAuth / OIDC [error_type](#authorize-errors) |
| error_description | Description of the error type or additional information of the error |
| state | Unmodified `state` parameter from the request |
#### Possible errors
| error_type | Possible reason |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| invalid_request | The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. |
| invalid_scope | The requested scope is invalid. Typically the required `openid` value is missing. |
| unauthorized_client | The client is not authorized to request an access_token using this method. Check in Management Console that the requested `response_type` is allowed in your application configuration. |
| unsupported_response_type | The authorization server does not support the requested response_type. |
| server_error | The authorization server encountered an unexpected condition that prevented it from fulfilling the request. |
| interaction_required | The authorization server requires end-user interaction of some form to proceed. This error MAY be returned when the prompt parameter value in the Authentication Request is none, but the Authentication Request cannot be completed without displaying a user interface for end-user interaction. |
| login_required | The authorization server requires end-user authentication. This error MAY be returned when the prompt parameter value in the Authentication Request is none, but the Authentication Request cannot be completed without displaying a user interface for end-user authentication. |
## token_endpoint
`${CUSTOM_DOMAIN}/oauth/v2/token`
The token_endpoint will as the name suggests return various tokens (access, id and refresh) depending on the used `grant_type`.
When using [`authorization_code`](#authorization-code-grant-code-exchange) flow call this endpoint after receiving the code from the authorization_endpoint.
When using [`refresh_token`](#authorization-code-grant-code-exchange) or [`urn:ietf:params:oauth:grant-type:jwt-bearer` (JWT Profile)](#jwt-profile-grant) you will call this endpoint directly.
### Authorization code grant (Code Exchange)
As mention above, when using `authorization_code` grant, this endpoint will be your second request for authorizing a user with its user agent (browser).
#### Required request parameters
| Parameter | Description |
| ------------ | ------------------------------------------------------------------------------------------------------------- |
| code | Code that was issued from the authorization request. |
| grant_type | Must be `authorization_code` |
| redirect_uri | Callback uri where the code was be sent to. Must match exactly the redirect_uri of the authorization request. |
Depending on your authorization method you will have to provide additional parameters or headers:
<Tabs
groupId="token-auth-methods"
defaultValue="client_secret_basic"
values={[
{label: 'client_secret_basic', value: 'client_secret_basic'},
{label: 'client_secret_post', value: 'client_secret_post'},
{label: 'none (PKCE)', value: 'none'},
{label: 'private_key_jwt', value: 'private_key_jwt'},
]}
>
<Tab value="client_secret_basic">
Send your `client_id` and `client_secret` as Basic Auth Header. Check [Client Secret Basic Auth Method](./authn-methods#client-secret-basic) on how to build it correctly.
</Tab>
<Tab value="client_secret_post">
Send your `client_id` and `client_secret` as parameters in the body:
| Parameter | Description |
| ------------- | -------------------------------- |
| client_id | client_id of the application |
| client_secret | client_secret of the application |
</Tab>
<Tab value="none">
Send your `client_id` and `code_verifier` for us to recompute the `code_challenge` of the authorization request.
| Parameter | Description |
| ------------- | ------------------------------------------------------------ |
| client_id | client_id of the application |
| code_verifier | code_verifier previously used to generate the code_challenge |
</Tab>
<Tab value="private_key_jwt">
Send a client assertion as JWT for us to validate the signature against the registered public key.
| Parameter | Description |
| --------------------- | ------------------------------------------------------------------------------------------------------------ |
| client_assertion | JWT built and signed according to [Using JWTs for Client Authentication](./authn-methods#jwt-with-private-key) |
| client_assertion_type | Must be `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` |
</Tab>
</Tabs>
#### Successful code response
| Property | Description |
| ------------- | ------------------------------------------------------------------------------------- |
| access_token | An `access_token` as JWT or opaque token |
| expires_in | Number of second until the expiration of the `access_token` |
| id_token | An `id_token` of the authorized user |
| scope | Scopes of the `access_token`. These might differ from the provided `scope` parameter. |
| refresh_token | An opaque token. Only returned if `offline_access` scope was requested |
| token_type | Type of the `access_token`. Value is always `Bearer` |
### JWT profile grant
#### Required request parameters
| Parameter | Description |
| ---------- | ----------------------------------------------------------------------------------------------------------------------- |
| grant_type | Must be `urn:ietf:params:oauth:grant-type:jwt-bearer` |
| assertion | JWT built and signed according to [Using JWTs for Authorization Grants](./grant-types#using-jw-ts-as-authorization-grants) |
| scope | [Scopes](./scopes) you would like to request from ZITADEL. Scopes are space delimited, e.g. `openid email profile` |
```bash
curl --request POST \
--url ${CUSTOM_DOMAIN}/oauth/v2/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer \
--data assertion=eyJhbGciOiJSUzI1Ni...
```
#### Successful JWT profile response
| Property | Description |
| ------------ | ------------------------------------------------------------------------------------- |
| access_token | An `access_token` as JWT or opaque token |
| expires_in | Number of second until the expiration of the `access_token` |
| id_token | An `id_token` of the authorized service user |
| scope | Scopes of the `access_token`. These might differ from the provided `scope` parameter. |
| token_type | Type of the `access_token`. Value is always `Bearer` |
### Refresh token grant
To request a new `access_token` without user interaction, you can use the `refresh_token` grant.
See [offline_access Scope](./scopes#standard-scopes) for how to request a `refresh_token` in the authorization request.
#### Required request parameters
| Parameter | Description |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| grant_type | Must be `refresh_token` |
| refresh_token | The refresh_token previously issued in the last authorization_code or refresh_token request. |
| scope | [Scopes](./scopes) you would like to request from ZITADEL for the new access_token. Must be a subset of the scope originally requested by the corresponding auth request. When omitted, the scopes requested by the original auth request will be reused. Scopes are space delimited, e.g. `openid email profile` |
Depending on your authorization method you will have to provide additional parameters or headers:
<Tabs
groupId="token-auth-methods"
defaultValue="client_secret_basic"
values={[
{label: 'client_secret_basic', value: 'client_secret_basic'},
{label: 'client_secret_post', value: 'client_secret_post'},
{label: 'none (PKCE)', value: 'none'},
{label: 'private_key_jwt', value: 'private_key_jwt'},
]}
>
<Tab value="client_secret_basic">
Send your `client_id` and `client_secret` as Basic Auth Header. Check [Client Secret Basic Auth Method](./authn-methods#client-secret-basic) on how to build it correctly.
</Tab>
<Tab value="client_secret_post">
Send your `client_id` and `client_secret` as parameters in the body:
| Parameter | Description |
| ------------- | -------------------------------- |
| client_id | client_id of the application |
| client_secret | client_secret of the application |
</Tab>
<Tab value="none">
Send your `client_id` as parameter in the body. No authentication is required.
</Tab>
<Tab value="private_key_jwt">
Send a `client_assertion` as JWT for us to validate the signature against the registered public key.
| Parameter | Description |
| --------------------- | ------------------------------------------------------------------------------------------------------------ |
| client_assertion | JWT built and signed according to [Using JWTs for Client Authentication](./authn-methods#jwt-with-private-key) |
| client_assertion_type | Must be `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` |
</Tab>
</Tabs>
#### Successful refresh token response
| Property | Description |
| ------------- | ------------------------------------------------------------------------------------- |
| access_token | An `access_token` as JWT or opaque token |
| expires_in | Number of second until the expiration of the `access_token` |
| id_token | An `id_token` of the authorized user |
| scope | Scopes of the `access_token`. These might differ from the provided `scope` parameter. |
| refresh_token | An new opaque refresh_token. |
| token_type | Type of the `access_token`. Value is always `Bearer` |
### Client credentials grant
#### Required request parameters
| Parameter | Description |
| ---------- | ---------------------------------------------------------------------------------------------------------- |
| grant_type | Must be `client_credentials` |
| scope | [Scopes](./scopes) you would like to request from ZITADEL. Scopes are space delimited, e.g. `openid profile` |
Additionally, you need to authenticate your client by either sending `client_id` and `client_secret` as Basic Auth Header.
Check [Client Secret Basic Auth Method](./authn-methods#client-secret-basic) on how to build it correctly.
```bash
curl --request POST \
--url ${CUSTOM_DOMAIN}/oauth/v2/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Basic ${BASIC_AUTH}' \
--data grant_type=client_credentials \
--data scope=openid profile
```
Or you can also send your `client_id` and `client_secret` as parameters in the body:
| Parameter | Description |
| ------------- | -------------------------------- |
| client_id | client_id of the application |
| client_secret | client_secret of the application |
```bash
curl --request POST \
--url ${CUSTOM_DOMAIN}/oauth/v2/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data grant_type=client_credentials \
--data client_id=${CLIENT_ID} \
--data client_secret=${CLIENT_SECRET} \
--data scope=openid profile
```
#### Successful client credentials response
| Property | Description |
| ------------ | ------------------------------------------------------------------------------------- |
| access_token | An `access_token` as JWT or opaque token |
| expires_in | Number of second until the expiration of the `access_token` |
| scope | Scopes of the `access_token`. These might differ from the provided `scope` parameter. |
| token_type | Type of the `access_token`. Value is always `Bearer` |
### Token Exchange grant
The Token Exchange grant implements [RFC 8693, OAuth 2.0 Token Exchange](https://www.rfc-editor.org/rfc/rfc8693) and can be used to exchange tokens to a different scope, audience or subject. Changing the subject of an authenticated token is called impersonation or delegation. ZITADEL also provides a [token exchange guide](/guides/integrate/token-exchange) with more details on using the Token Exchange Grant.
<Callout>
Token Exchange is currently an experimental [beta](https://help.zitadel.com/zitadel-software-release-cycle#beta) feature. Be sure to enable it on the [feature API](/guides/integrate/token-exchange#feature-api) before using it.
</Callout>
#### Request parameters
<TokenExchangeRequest components={props.components} />
Depending on your authorization method you will have to provide additional parameters or headers:
<Tabs
groupId="token-auth-methods"
defaultValue="client_secret_basic"
values={[
{label: 'client_secret_basic', value: 'client_secret_basic'},
{label: 'client_secret_post', value: 'client_secret_post'},
{label: 'none', value: 'none'},
{label: 'private_key_jwt', value: 'private_key_jwt'},
]}
>
<Tab value="client_secret_basic">
Send your `client_id` and `client_secret` as Basic Auth Header. Check [Client Secret Basic Auth Method](./authn-methods#client-secret-basic) on how to build it correctly.
</Tab>
<Tab value="client_secret_post">
Send your `client_id` and `client_secret` as parameters in the body:
| Parameter | Description |
| ------------- | -------------------------------- |
| client_id | client_id of the application |
| client_secret | client_secret of the application |
</Tab>
<Tab value="none">
Send your `client_id` as parameter in the body. No authentication is required.
</Tab>
<Tab value="private_key_jwt">
Send a `client_assertion` as JWT for us to validate the signature against the registered public key.
| Parameter | Description |
| --------------------- | ------------------------------------------------------------------------------------------------------------ |
| client_assertion | JWT built and signed according to [Using JWTs for Client Authentication](./authn-methods#jwt-with-private-key) |
| client_assertion_type | Must be `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` |
</Tab>
</Tabs>
#### Successful token exchange response
<TokenExchangeResponse components={props.components} />
#### Token types
<TokenExchangeTypes components={props.components} />
### Error response
| error_type | Possible reason |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| invalid_request | The request is missing a required parameter, includes an unsupported parameter value (other than grant type), repeats a parameter, includes multiple credentials, utilizes more than one mechanism for authenticating the client, or is otherwise malformed. |
| invalid_scope | The requested scope is invalid, unknown, malformed, or exceeds the scope granted by the resource owner. |
| unauthorized_client | The authenticated client is not authorized to use this authorization grant type. |
| unsupported_grant_type | The authorization grant type is not supported by the authorization server. |
| server_error | The authorization server encountered an unexpected condition that prevented it from fulfilling the request. |
| invalid_grant | The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. |
| invalid_client | Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method). |
## introspection_endpoint
`${CUSTOM_DOMAIN}/oauth/v2/introspect`
This endpoint enables clients to validate an `acccess_token`, either opaque or JWT. Unlike client side JWT validation,
this endpoint will check if the token is not revoked (by client or logout).
| Parameter | Description |
| --------- | --------------- |
| token | An access token |
Depending on your authorization method you will have to provide additional parameters or headers:
<Tabs
groupId="introspect-auth-methods"
defaultValue="client_secret_basic"
values={[
{label: 'client_secret_basic', value: 'client_secret_basic'},
{label: 'private_key_jwt', value: 'private_key_jwt'},
]}
>
<Tab value="client_secret_basic">
Send your `client_id` and `client_secret` as Basic Auth Header. Check [Client Secret Basic Auth Method](./authn-methods#client-secret-basic) on how to build it correctly.
```bash
curl --request POST \
--url ${CUSTOM_DOMAIN}/oauth/v2/introspect \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Basic {your_basic_auth_header}' \
--data token=VjVxyCZmRmWYqd3_F5db9Pb9mHR5fqzhn...
```
</Tab>
<Tab value="private_key_jwt">
Send a `client_assertion` as JWT for us to validate the signature against the registered public key.
| Parameter | Description |
| --------------------- | ----------------------------------------------------------------------------------------------------------- |
| client_assertion | JWT built and signed according to [Using JWTs for Client Authentication](./authn-methods#client-secret-basic) |
| client_assertion_type | must be `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` |
```bash
curl --request POST \
--url ${CUSTOM_DOMAIN}/oauth/v2/introspect \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
--data client_assertion=eyJhbGciOiJSUzI1Ni... \
--data token=VjVxyCZmRmWYqd3_F5db9Pb9mHR5fqzhn...
```
</Tab>
</Tabs>
### Successful introspection response
Upon successful authorization of the client a response with the boolean `active` is returned, indicating if the provided token
is active and the requesting client is part of the token audience.
If `active` is **true**, further information will be provided:
| Property | Description |
| ---------- | --------------------------------------------------------------------- |
| aud | The audience of the token |
| client_id | The client_id of the application the token was issued to |
| exp | Time the token expires (as unix time) |
| iat | Time of the token was issued at (as unix time) |
| iss | Issuer of the token |
| jti | Unique id of the token |
| nbf | Time the token must not be used before (as unix time) |
| scope | Space delimited list of scopes granted to the token |
| token_type | Type of the inspected token. Value is always `Bearer` |
| username | ZITADEL's login name of the user. Consist of `username@primarydomain` |
Additionally and depending on the granted scopes, information about the authorized user is provided.
Check the [Claims](./claims) page if a specific claims might be returned and for detailed description.
### Error response
If the authorization fails, an HTTP 401 with `invalid_client` will be returned.
## userinfo_endpoint
`${CUSTOM_DOMAIN}/oidc/v1/userinfo`
This endpoint will return information about the authorized user.
Send the `access_token` of the **user** (not the client) as Bearer Token in the `authorization` header:
```bash
curl --request GET \
--url ${CUSTOM_DOMAIN}/oidc/v1/userinfo
--header 'Authorization: Bearer dsfdsjk29fm2as...'
```
### Successful userinfo response
If the `access_token` is valid, the information about the user depending on the granted scopes is returned.
Check the [Claims](./claims) page if a specific claims might be returned and for detailed description.
### Error response
If the token is invalid or expired, an HTTP 401 will be returned.
## revocation_endpoint
`${CUSTOM_DOMAIN}/oauth/v2/revoke`
This endpoint enables clients to revoke an `access_token` or `refresh_token` they have been granted.
<Callout>
If you revoke an `access_token` only the specific token will be revoked. When revoking a `refresh_token`,
the corresponding `access_token` will be revoked as well.
</Callout>
| Parameter | Description |
| --------- | -------------------------------- |
| token | An access token or refresh token |
Depending on your authorization method you will have to provide additional parameters or headers:
<Tabs
groupId="token-auth-methods"
defaultValue="client_secret_basic"
values={[
{label: 'client_secret_basic', value: 'client_secret_basic'},
{label: 'client_secret_post', value: 'client_secret_post'},
{label: 'none (PKCE)', value: 'none'},
{label: 'private_key_jwt', value: 'private_key_jwt'},
]}
>
<Tab value="client_secret_basic">
Send your `client_id` and `client_secret` as Basic Auth Header. Check [Client Secret Basic Auth Method](./authn-methods#client-secret-basic) on how to construct a request correctly.
</Tab>
<Tab value="client_secret_post">
Send your `client_id` and `client_secret` as parameters in the body:
| Parameter | Description |
| ------------- | -------------------------------- |
| client_id | client_id of the application |
| client_secret | client_secret of the application |
</Tab>
<Tab value="none">
Send your `client_id` as parameters in the body:
| Parameter | Description |
| --------- | ---------------------------- |
| client_id | client_id of the application |
</Tab>
<Tab value="private_key_jwt">
Send a `client_assertion` as JWT for ZITADEL to verify the signature against the registered public key.
| Parameter | Description |
| --------------------- | ------------------------------------------------------------------------------------------------------------- |
| client_assertion | JWT created and signed according to [Using JWTs for Client Authentication](./authn-methods#client-secret-basic) |
| client_assertion_type | must be `urn:ietf:params:oauth:client-assertion-type:jwt-bearer` |
```bash
curl --request POST \
--url ${CUSTOM_DOMAIN}/oauth/v2/revoke \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
--data client_assertion=eyJhbGciOiJSUzI1Ni... \
--data token=VjVxyCZmRmWYqd3_F5db9Pb9mHR5fqzhn...
```
</Tab>
</Tabs>
## end_session_endpoint
`${CUSTOM_DOMAIN}/oidc/v1/end_session`
The endpoint has to be opened in the user agent (browser) to terminate the user sessions.
No parameters are needed apart from the user agent cookie, but you can provide the following to customize the behavior:
| Parameter | Description |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id_token_hint | the id_token that was previously issued to the client |
| client_id | client_id of the application |
| post_logout_redirect_uri | Callback uri of the logout where the user (agent) will be redirected to. Must match exactly one of the preregistered in Console. |
| state | Opaque value used to maintain state between the request and the callback |
| logout_hint | A valid login name of a user. Will be used to select the user to logout. Only supported when using the login UI V2. |
| ui_locales | Spaces delimited list of preferred locales for the login UI, e.g. `de-CH de en`. If none is provided or matches the possible locales provided by the login UI, the `accept-language` header of the browser will be taken into account. |
The `post_logout_redirect_uri` will be checked against the previously registered uris of the client provided by the `azp` claim of the `id_token_hint` or the `client_id` parameter.
If both parameters are provided, they must be equal.
If neither an `id_token_hint` nor a `client_id` parameter is provided, the `post_logout_redirect_uri` will be ignored.
## jwks_uri
`${CUSTOM_DOMAIN}/oauth/v2/keys`
The endpoint returns a JSON Web Key Set (JWKS) containing the public keys that can be used to locally validate JWTs you received from ZITADEL.
The alternative would be to validate tokens with the [introspection endpoint](#introspection-endpoint).
### Key rotation
Keys are automatically rotated on a regular basis or on demand, meaning keys can change in irregular intervals.
ZITADEL ensures that a proper `kid` is set with each key.
<Callout title="Keys rotate without prior notice">
Be aware that these keys can be rotated without any prior notice.
</Callout>
### Caching
You can optimize performance of your clients by caching the response from the keys endpoint.
We recommend to regularly update the cached response, since the [keys can be rotated without prior notice](#key-rotation).
You could also combine caching with a risk-based on-demand refresh when a critical operation is executed.
Without caching you will call this endpoint on each request.
This might result in being rate limited for a large number of requests that come from the same backend.
## OAuth 2.0 metadata
**ZITADEL** does not yet provide a OAuth 2.0 Metadata endpoint but instead provides a [OpenID Connect Discovery Endpoint](https://openid.net/specs/openid-connect-discovery-1_0.html).
@@ -0,0 +1,134 @@
---
title: Grant Types in ZITADEL
sidebar_label: Grant Types
---
For a list of supported or unsupported `Grant Types` please have a look at the table below.
| Grant Type | Supported |
| :---------------------------------------------------- | :-------- |
| Authorization Code | yes |
| Authorization Code with PKCE | yes |
| Client Credentials | yes |
| Device Authorization | yes |
| Implicit | yes |
| JSON Web Token (JWT) Profile | yes |
| Refresh Token | yes |
| Resource Owner Password Credentials | no |
| Security Assertion Markup Language (SAML) 2.0 Profile | no |
| Token Exchange | yes |
## Authorization Code
**Link to spec.** [The OAuth 2.0 Authorization Framework Section 1.3.1](https://tools.ietf.org/html/rfc6749#section-1.3.1)
## Proof Key for Code Exchange
**Link to spec.** [Proof Key for Code Exchange by OAuth Public Clients](https://tools.ietf.org/html/rfc7636)
## Implicit
**Link to spec.** [The OAuth 2.0 Authorization Framework Section 1.3.2](https://tools.ietf.org/html/rfc6749#section-1.3.2)
## Client Credentials
**Link to spec.** [The OAuth 2.0 Authorization Framework Section 1.3.4](https://tools.ietf.org/html/rfc6749#section-1.3.4)
## Refresh Token
**Link to spec.** [The OAuth 2.0 Authorization Framework Section 1.5](https://tools.ietf.org/html/rfc6749#section-1.5)
## JSON Web Token (JWT) Profile
**Link to spec.** [JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants](https://tools.ietf.org/html/rfc7523)
### Using JWTs as Authorization Grants
Our service user work with the JWT profile to authenticate them against ZITADEL.
1. Create or use an existing service user
2. Create a new key and download it
3. Generate a JWT with the structure below and sign it with the downloaded key
4. Send the JWT Base64 encoded to ZITADEL's token endpoint
5. Use the received access token
---
Key JSON
| Key | Example | Description |
| :----- | :------------------------------------------------------------------ | :----------------------------------------------------------------- |
| type | `"serviceaccount"` | The type of account, right now only serviceaccount is valid |
| keyId | `"81693565968772648"` | This is unique ID of the key |
| key | `"-----BEGIN RSA PRIVATE KEY-----...-----END RSA PRIVATE KEY-----"` | The private key generated by ZITADEL, this can not be regenerated! |
| userId | `78366401571647008` | The service users ID, this is the same as the subject from tokens |
```json
{
"type": "serviceaccount",
"keyId": "81693565968772648",
"key": "-----BEGIN RSA PRIVATE KEY-----...-----END RSA PRIVATE KEY-----",
"userId": "78366401571647008"
}
```
---
JWT
| Claim | Example | Description |
| :---- | :------------------------- | :------------------------------------------------------------------------------------------------------------ |
| aud | `"https://${CUSTOM_DOMAIN}"` | String or Array of intended audiences MUST include ZITADEL's issuing domain |
| exp | `1605183582` | Unix timestamp of the expiry |
| iat | `1605179982` | Unix timestamp of the creation singing time of the JWT, MUST NOT be older than 1h |
| iss | `"77479219772321307"` | String which represents the requesting party (owner of the key), normally the `userId` from the json key file |
| sub | `"77479219772321307"` | The subject ID of the service user, normally the `userId` from the json key file |
```json
{
"iss": "77479219772321307",
"sub": "77479219772321307",
"aud": "https://${CUSTOM_DOMAIN}",
"exp": 1605183582,
"iat": 1605179982
}
```
> To identify your key, it is necessary that you provide a JWT with a `kid` header claim representing your keyId from the Key JSON:
>
> ```json
> {
> "alg": "RS256",
> "kid": "81693565968772648"
> }
> ```
---
See [JWT Profile Grant on Token Endpoint](./endpoints#token-endpoint) for usage.
### Using JWTs for Client Authentication
See how to build a [JWT for client authentication](./authn-methods#jwt-with-private-key) from the downloaded key.
Find out how to use it on the [token endpoint](./endpoints#token-endpoint) or the [introspection endpoint](./endpoints#introspection-endpoint).
## Token Exchange
**Link to spec.** [OAuth 2.0 Token Exchange](https://tools.ietf.org/html/rfc8693)
## Device Authorization
**Link to spec.** [OAuth 2.0 Device Authorization Grant](https://tools.ietf.org/html/rfc8628)
## Security Assertion Markup Language (SAML) 2.0 Profile
**Link to spec.** [Security Assertion Markup Language (SAML) 2.0 Profile for OAuth 2.0 Client Authentication and Authorization Grants](https://tools.ietf.org/html/rfc7522)
## Not Supported Grant Types
### Resource Owner Password Credentials
> Due to growing security concerns we do not support this grant type. With OAuth 2.1 it looks like this grant will be removed.
**Link to spec.** [The OAuth 2.0 Authorization Framework Section 1.3.3](https://tools.ietf.org/html/rfc6749#section-1.3.3)
@@ -0,0 +1,34 @@
---
title: Scopes in ZITADEL
sidebar_label: Scopes
---
ZITADEL supports the usage of scopes as way of requesting information from the IAM and also instruct ZITADEL to do certain operations.
## Standard Scopes
| Scopes | Description |
| :------------- | ------------------------------------------------------------------------------ |
| openid | When using openid connect this is a mandatory scope |
| profile | Optional scope to request the profile of the subject |
| email | Optional scope to request the email of the subject |
| address | Optional scope to request the address of the subject |
| phone | Optional scope to request the phone of the subject |
| offline_access | Optional scope to request a refresh_token (only possible when using code flow) |
## Reserved Scopes
In addition to the standard compliant scopes we utilize the following scopes.
| Scopes | Example | Description |
| :------------------------------------------------ | :----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `urn:zitadel:iam:org:project:role:{rolekey}` | `urn:zitadel:iam:org:project:role:user` | By using this scope a client can request the claim `urn:zitadel:iam:org:project:roles` to be asserted when possible. As an alternative approach you can enable all roles to be asserted from the [project](/guides/manage/console/roles#authorizations) a client belongs to. |
| `urn:zitadel:iam:org:projects:roles` | `urn:zitadel:iam:org:projects:roles` | By using this scope a client can request the claim `urn:zitadel:iam:org:project:{projectid}:roles` to be asserted for each requested project. All projects of the token audience, requested by the `urn:zitadel:iam:org:project:id:{projectid}:aud` scopes will be used. |
| `urn:zitadel:iam:org:id:{id}` | `urn:zitadel:iam:org:id:178204173316174381` | When requesting this scope **ZITADEL** will enforce that the user is a member of the selected organization. If the organization does not exist a failure is displayed. It will assert the `urn:zitadel:iam:user:resourceowner` claims. |
| `urn:zitadel:iam:org:domain:primary:{domainname}` | `urn:zitadel:iam:org:domain:primary:acme.ch` | When requesting this scope **ZITADEL** will enforce that the user is a member of the selected organization and the username is suffixed by the provided domain. If the organization does not exist a failure is displayed |
| `urn:zitadel:iam:org:roles:id:{orgID}` | `urn:zitadel:iam:org:roles:id:178204173316174381` | This scope can be used one or more times to limit the granted organization IDs in the returned roles. Unknown organization IDs are ignored. When this scope is not used, all granted organizations are returned inside the roles. |
| `urn:zitadel:iam:org:project:id:{projectid}:aud` | `urn:zitadel:iam:org:project:id:69234237810729019:aud` | By adding this scope, the requested project id will be added to the audience of the access token |
| `urn:zitadel:iam:org:project:id:zitadel:aud` | `urn:zitadel:iam:org:project:id:zitadel:aud` | By adding this scope, the ZITADEL project id will be added to the audience of the access token |
| `urn:zitadel:iam:user:metadata` | `urn:zitadel:iam:user:metadata` | By adding this scope, the metadata of the user will be included in the token. The values are base64 encoded. |
| `urn:zitadel:iam:user:resourceowner` | `urn:zitadel:iam:user:resourceowner` | By adding this scope: id, name and primary_domain of the user's organization will be included in the token. |
| `urn:zitadel:iam:org:idp:id:{idp_id}` | `urn:zitadel:iam:org:idp:id:76625965177954913` | By adding this scope the user will directly be redirected to the identity provider to authenticate. Make sure you also send the primary domain scope if a custom login policy is configured. Otherwise the system will not be able to identify the identity provider. |