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,4 @@
<Callout type="warn" title="Don't Lock Yourself Out of Your Instance">
Before you change your Zitadel configuration, we highly recommend you to create a service user with a personal access token (PAT) and the IAM_OWNER role.
In case something breaks, you can use this PAT to revert your changes or fix the configuration so you can use a login UI again.
</Callout>
@@ -0,0 +1,199 @@
---
title: Login users into your application with a hosted login UI
sidebar_label: Hosted Login UI
---
import PreventLockout from './_prevent-lockout.mdx';
ZITADEL provides a hosted single-sign-on page to securely sign-in users to your applications.
ZITADEL's hosted login page serves as a centralized authentication interface provided for applications that integrate ZITADEL.
As a developer, understanding the hosted login page is essential for seamlessly integrating authentication into your application.
## Centralized authentication endpoint
ZITADEL's hosted login page acts as a centralized authentication endpoint where users are redirected to authenticate themselves.
When users attempt to access a protected resource within your application, you can redirect them to the hosted login page to authenticate using their login methods and credentials or through Single-sign-on (SSO).
After successful authentication, the user will be redirected back to the originating application.
## Security and compliance
ZITADEL's hosted login page prioritizes security and compliance with industry standards and regulations.
It employs best practices for securing authentication processes, such as encryption, token-based authentication, and adherence to protocols like OAuth 2.0, [OpenID Connect](/guides/integrate/login/oidc), and [SAML](/guides/integrate/login/saml).
We make sure to harden the login UI and minimize the attack surface.
One of the measures we apply is setting the necessary security heads thus minimizing the risk of common vulnerabilities in login pages, such as XSS vulnerabilities.
Put your current login to the test and compare the results with our hosted login page.
Tools like [Mozilla's Observatory](https://observatory.mozilla.org/) can give you a good first impression about the security posture.
## Developer-friendly integration
Integrating the hosted login page into your application is straightforward, thanks to ZITADEL's developer-friendly documentation, SDKs, and APIs. Developers can easily implement authentication flows, handle authentication callbacks, and customize the user experience to seamlessly integrate authentication with their application's workflow.
Overall, ZITADEL's hosted login page simplifies the authentication process for developers by providing a secure, customizable, and developer-friendly authentication interface. By leveraging this centralized authentication endpoint, developers can enhance their application's security, user experience, and compliance with industry standards and regulations.
## Key features of the hosted login
### Flexible usernames
Different login name formats can be used on ZITADEL's hosted login page to select a user.
Login methods can be a user's username, containing the username and an [organization domain](/guides/manage/console/organizations-overview#usernames-and-domains), their email addresses, or their phone numbers.
By default, all of these login methods are allowed and can be adjusted by [Managers](/concepts/structure/managers) to meet their requirements.
### Support for multiple authentication methods
The hosted login page supports various authentication methods, including traditional username/password authentication, social login options, multi-factor authentication (MFA), and passkey authentication method.
The second factor (2FA) and multi-factor authentication methods (MFA) available in ZITADEL include OTP via an authenticator app, TOTP via SMS, OTP via email, and U2F.
Developers can configure the authentication methods offered on the login page based on their application's security and usability requirements.
### Enterprise single-sign-on
![Screenshot of ZITADEL management console showing different identity provider templates](../../../../public/img/guides/integrate/login/login-external-idp-templates.png)
With the hosted login page from ZITADEL developers will get the best support for multi-tenancy single-sign-on with third-party identity providers.
ZITADEL acts as an [identity broker](/concepts/features/identity-brokering) between your applications and different external identity providers, reducing the implementation effort for developers.
External Identity providers can be configured for the whole instance or for each organization that represents a group of users such as a B2B customer or organizational unit.
ZITADEL offers various [identity provider templates](/guides/integrate/identity-providers/introduction) to integrate providers such as [Okta](/guides/integrate/identity-providers/okta-oidc), [Entra ID](/guides/integrate/identity-providers/azure-ad-oidc) or on-premise [LDAP](/guides/integrate/identity-providers/ldap).
### Multi-tenancy authentication
ZITADEL simplifies multi-tenancy authentication by securely managing authentication for multiple tenants, called [Organizations](/guides/manage/console/organizations-overview), within a single [instance](/guides/manage/console/organizations-overview), within a single [instance](/concepts/structure/instance).
Key features include:
1. **Secure Tenant Isolation**: Ensures robust security measures to prevent unauthorized access between tenants, maintaining data privacy and compliance. [Managers](/concepts/structure/managers) for an organization have only access to data and configuration within their Organization.
2. **Custom Authentication Configurations**: Allows tailored [authentication settings](/guides/manage/console/default-settings#login-behavior-and-access), [branding](/guides/manage/customize/branding), and policies for each tenant.
3. **Centralized Management**: Provides [centralized administration](/guides/manage/console/managers) for efficient management across all tenants.
4. **Scalability and Flexibility**: Scales seamlessly to accommodate growing organizations of all sizes.
5. **Domain Discovery**: Starting on a central login page, route users to their tenant based on their email address or other user attributes. Authentication settings will be applied automatically based on the organization's policies, this includes routing users seamlessly to third party identity providers like [Entra ID](/guides/integrate/identity-providers/azure-ad-oidc).
### Customization options
While the hosted login page provides a default authentication interface out-of-the-box, ZITADEL offers [customization options](/guides/manage/customize/branding) to tailor the login page to match your application's branding and user experience requirements.
Developers can customize elements such as logos, colors, and messaging to ensure a seamless integration with their application's user interface.
<Callout type="info">
Customization and Branding
The login page can be changed by customizing different branding aspects and you can define a Custom Domain for the login (eg, login.acme.com).
By default, the displayed branding is defined [based on the user's domain](/guides/solution-scenarios/domain-discovery). In case you want to show the branding of a specific organization by default, you need to either pass a primary domain scope (`urn:zitadel:iam:org:domain:primary:{domainname}`) with the authorization request, or define the behavior on your Project's settings.
</Callout>
### Fast account switching
The hosted login page remembers users who have previously authenticated.
In case a user has used multiple accounts, for example, a private account and a work account, to authenticate, then all accounts will be shown on the Account Picker.
Users can still login with a different user that is not on the list.
This allows users to quickly switch between users and provide a better user experience.
<Callout>
This behavior can be changed with the authorization request. Please refer to our [guide](/guides/integrate/login/oidc/login-users).
</Callout>
### Self-service for users
ZITADEL's hosted login page offers [many self-service flows](/concepts/features/selfservice) that allow users to set up authentication methods or recover their login information.
Developers use the self-service functionalities to reduce manual tasks and improve user experience.
Key features include:
### Password reset
Unauthenticated users can request a password reset after providing the loginname during the login flow.
- User selects reset password
- An email will be sent to the verified email address
- User opens a link and has to provide a new password
#### Prompt users to set up multifactor authentication
Users are automatically prompted to provide a second factor, when
- Instance or organization [login policy](/guides/manage/console/default-settings#login-behavior-and-access) is set
- Requested by the client
- A multi-factor is set up for the user
When a multi-factor is required, but not set up, then the user is requested to set up an additional factor.
<Callout title="Disabling multifactor prompt">
You can disable the prompt, in case multifactor authentication is not enforced by setting the [**Multifactor Init Lifetime**](/guides/manage/console/default-settings#login-lifetimes) to 0.
</Callout>
#### Enroll passkeys
Users can select a button to initiate passkey login or use a fall-back method (ie. login with username/password), if available.
The [passkeys](/concepts/features/passkeys) login flow follows the FIDO2 / WebAuthN standard.
With the introduction of passkeys the gesture can be provided on ANY of the user's devices.
This is not strictly the device where the login flow is being executed (e.g., on a mobile device).
The user experience depends mainly on the operating system and browser.
## Hosted Login Version 2
We have worked on a new, self-hostable implementation of our hosted MIT licensed login built with Next.js and leveraging our [Session API](/guides/integrate/login/login-users#zitade-ls-session-api).
This solution empowers you to easily customize the login experience to perfectly match your brand and needs.
### Limitations
For the first implementation we have excluded the following features:
- Generic JWT IDP
- LDAP IDP
- Device Authorization Grants
- Force MFA on external authenticated users
- Passkey/U2F Setup
- As passkey and u2f is bound to a domain, it is important to notice, that setting up the authentication possibility in the ZITADEL management console (Self-service), will not work if the login runs on a different domain
- Custom Login Texts
### Important Changes
- **Create Users:** The new TypeScript Login app is built with the session and the user V2 API, the users V2 API does have some differences to the v1 API, so make sure you create users through the new API.
- **External IDPs:** If you want to use external identity provider login, such as Login with Google or Apple. You can follow our existing setup guides, just make sure to use the following redirect url: `${CUSTOM_DOMAIN}/idps/callback`
- **Passkey/U2F:** Those authentication methods are bound to a domain. As your new login runs on a different domain than the previous login, existing passkey authentication (fingerprint, face id, etc.) can’t be used. Also when they are managed through the management console of ZITADEL, they are added on a different domain.
<br />
*Note: If you run the login on a subdomain of your current instance, this problem
can be avoided. E.g myinstance.zitadel.cloud and login.myinstance.zitadel.cloud*
#### Step-by-step Guide
**Using the new login:** To use the new login without changing your current setup, the easiest way is to visit `https://${CUSTOM_DOMAIN}/ui/v2/login` on your Zitadel Cloud Custom Domain.
You can also activate the v2 login for your apps, so users are redirected to `/ui/v2/login` for authentication.
**Customizing the new login:** The easiest way to actually customizing it is to fork the https://github.com/zitadel/zitadel repo and use the [Deploy with Vercel button](https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#login-deploy) to run your login code on Vercel.
<Tabs queryString="deployment">
<Tab value="zitadel_cloud" label="Activate Zitadel Cloud V2 Login" default="true">
Zitadel Cloud provides the V2 login at the fixed path `/ui/v2/login`.
<PreventLockout components={props.components} />
To activate it to authenticate on your Zitadel Cloud apps, you can follow one of these steps:
1. Enable the new login on your application configuration. Leave the field **Custom base URL for the new Login UI** empty to use the default. With these settings, Zitadel will automatically redirect you to the new login if you call the old one.
![](../../../../public/img/guides/integrate/login/login-v2-app-config.png)
2. Enable the [loginV2 feature](/reference/api/feature/zitadel.feature.v2.FeatureService.SetInstanceFeatures) on the instance. Leave the base URI empty to use the default. If you enable this feature, the login will be used for every application configured in your Zitadel instance. (Example: https://your-zitadel-instance.zitadel.cloud/ui/v2/login)
</Tab>
<Tab value="vercel_custom" label="Vercel Custom Login Deployment">
The simplest way to deploy the new login for yourself is by using the [Deploy with Vercel button](https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#login-deploy) to deploy the login directly to your Vercel.
1. [Create a service user](/guides/integrate/service-users/personal-access-token#create-a-service-user-with-a-pat) with a PAT in your instance
2. Give the user IAM_LOGIN_CLIENT Permissions in the default settings (CUSTOM_DOMAIN/ui/console/instance?id=organizations)
Note: [Zitadel Manager Guide](/guides/manage/console/managers)
3. Deploy login to Vercel: You can do so by directly clicking the [“Deploy” button](https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#login-deploy) in our [repository](https://github.com/zitadel/zitadel)
4. If you have used the deploy button in the steps before, you will automatically be asked for this step. Enter the environment variables in Vercel
- PAT
- ZITADEL_API_URL (Example: https://my-domain.zitadel.cloud, no trailing slash)
5. Add the domain where your login UI is hosted to the [Trusted Domains](https://zitadel.com/docs/apis/resources/admin/admin-service-add-instance-trusted-domain) in Zitadel. (Example: my-new-zitadel-login.vercel.app)
6. Use the new login in your application.
<PreventLockout components={props.components} />
You have three different options on how to achieve this:
1. Enable the new login on your application configuration and add the URL of your login UI. With these settings, Zitadel will automatically redirect you to the new login if you call the old one.
![](../../../../public/img/guides/integrate/login/login-v2-app-config.png)
2. Enable the [loginV2 feature](/reference/api/feature/zitadel.feature.v2.FeatureService.SetInstanceFeatures) on the instance and add the URL of your login. If you enable this feature, the login will be used for every application configured in your Zitadel instance. (Example: https://my-new-zitadel-login.vercel.app)
3. Change the issuer in the code of your application to the new domain of your login
7. Enforce users to have their email verified. By setting `EMAIL_VERIFICATION` to `true` in your environment variables, your users will be enforced to verify their email address before they can log in.
</Tab>
</Tabs>
@@ -0,0 +1,17 @@
---
title: "Login users with ZITADEL"
description: "Sign-in users and application with ZITADEL. In this section you will find resources on how to authenticate your users by using the hosted login via OpenID Connect and SAML. Follow our dedicated guides to build your custom login user interface, if you want to customize the login behavior further."
---
{/* THIS FILE IS AUTO-GENERATED FROM SIDEBAR-DATA.
ANY MANUAL CHANGES WILL BE OVERWRITTEN.
*/}
import { Card, Cards } from 'fumadocs-ui/components/card';
import { FileText, Folder, Link as LinkIcon } from 'lucide-react';
<Cards>
<Card title="Login Users" href="/guides/integrate/login/login-users" icon={<FileText />} />
<Card title="Hosted Login" href="/guides/integrate/login/hosted-login" icon={<FileText />} />
<Card title="Logout" href="/guides/integrate/login/oidc/logout" icon={<FileText />} />
</Cards>
@@ -0,0 +1,127 @@
---
title: Log users into your application with different authentication options
sidebar_label: Authentication Options
---
ZITADEL is a comprehensive identity and access management platform designed to streamline user authentication, authorization, and management processes for your application. It offers a range of features, including single sign-on (SSO), multi-factor authentication (MFA), and centralized user management.
With ZITADEL, you can securely authenticate users using industry-standard protocols such as OpenID Connect and SAML. This enables seamless integration with various applications and services, providing a unified authentication experience for your users.
Besides federated authentication with OpenID Connect and SAML, ZITADEL offers an API to authenticate users allowing you to create your own login process and user interface.
In this guide, we will walk through the different protocols, features and concepts that can be used to login users securely into your applications.
![](../../../../public/img/guides/integrate/login/login-start.png)
## Using industry-standard protocols
### Authenticate users with OpenID Connect 1.0
OpenID Connect (OIDC) offers a modern and lightweight authentication protocol built on top of OAuth 2.0, providing flexible authentication flows and easy integration with web and mobile applications.
ZITADEL offers a certified compliant implementation of the OpenID Connect Standard, ensuring compliance with proven security best practices.
Authenticating users through the OpenID Connect protocol typically requires an application to redirect the user with an [Auth Request](https://zitadel.com/playgrounds/oidc) to the identity provider that contains information such as the requesting application, [scopes](/apis/openidoauth/scopes), and redirect url.
The identity provider is not part of the original application, but a standalone service like ZITADEL that may run under the [same domain](/concepts/features/custom-domain)
The user will authenticate using their credentials.
After successful authentication, the user will be redirected back to the original application.
If you want to read more about authenticating with OIDC, head over to our comprehensive [OpenID Connect Guide](/guides/integrate/login/oidc).
### Authenticate users with SAML
SAML (Security Assertion Markup Language) is a widely adopted standard for exchanging authentication and authorization data between identity providers and service providers.
Authentication with SAML (Security Assertion Markup Language) involves a series of exchanges between a service provider (SP), an identity provider (IdP), and the user. Here's an overview of how the process typically works:
1. **User Attempts to Access a Service**: The user tries to access a service or application that requires authentication, such as logging into a web application.
2. **Service Provider Redirects to Identity Provider**: The service provider (SP) detects that the user needs to be authenticated and redirects the user's browser to the designated identity provider (IdP) for authentication. This redirection often occurs via a SAML request, which includes information about the service provider and a request for authentication.
3. **User Authenticates with Identity Provider**: The user is presented with the identity provider's login page, where they enter their credentials (username and password). Alternatively, depending on the IdP's configuration, the user might be authenticated using single sign-on (SSO) mechanisms such as a pre-existing session or multi-factor authentication.
4. **Identity Provider Generates SAML Assertion**: Upon successful authentication, the identity provider creates a SAML assertion containing information about the user, such as their identity and attributes. This assertion is digitally signed by the identity provider to ensure its integrity and authenticity.
5. **SAML Assertion Sent to Service Provider**: The identity provider sends the SAML assertion back to the user's browser as a response to the original SAML request. The browser then forwards the assertion to the service provider.
6. **Service Provider Validates SAML Assertion**: The service provider receives the SAML assertion and validates it to ensure its integrity and authenticity. This typically involves verifying the digital signature of the assertion and checking that it originated from a trusted identity provider.
7. **User Granted Access**: If the SAML assertion is successfully validated, the service provider considers the user authenticated and grants them access to the requested service or application. The user can now interact with the service without needing to reauthenticate until their session expires or they log out.
Overall, authentication with SAML provides a secure and standardized method for enabling single sign-on (SSO) and federated identity management across different applications and systems.
It allows users to access multiple services with a single set of credentials, streamlining the authentication process while maintaining strong security measures.
Note that SAML might not be suitable for mobile applications.
In case you want to integrate a mobile application, use OpenID Connect or our Session API.
There are more [differences between SAML and OIDC](https://zitadel.com/blog/saml-vs-oidc) that you might want to consider.
If you want to read more about authenticating with SAML, head over to our comprehensive [SAML Guide](/guides/integrate/login/saml).
## ZITADEL's Session API
ZITADEL's [Session API](/reference/api/session) provides developers with a straightforward method to manage user sessions within their applications.
The Session API is not an industry-standard and can be used instead of OpenID Connect or SAML to authenticate users by [building your own custom login user interface](/guides/integrate/login-ui).
### Tokens in the Session API
The session API will return a session token that can be used to authenticate users from your application.
This token should not be confused with am access or id tokens in opaque or JWT form that is issued during OpenID connect flows.
<Callout title="Token exchange">
Token exchange between Session API and OIDC / SAML tokens is not possible at this moment.
</Callout>
### Key features of the Session API
These are some key features of the API:
1. **Session Management**: The Session API allows you to create, retrieve, update, and delete sessions for users within your application. Sessions represent a user's active login session, providing temporary access to your application's resources.
2. **Creating Sessions**: With the Session API, you can initiate a new session for a user after they successfully authenticate through ZITADEL. This typically involves generating a session token or ID, which is then associated with the user's identity and used to track their session activity.
3. **Retrieving Sessions**: You can retrieve information about existing sessions using the Session API, such as session ID, user ID, creation time, and expiration time. This allows you to monitor active user sessions and retrieve session details as needed for auditing or troubleshooting purposes.
4. **Updating Sessions**: The Session API enables you to update session attributes or extend session lifetimes based on specific application requirements. Each authentication step, such as username / password check or multi-factor verification, will result in an update session. Also you might want to refresh a session token periodically to prevent it from expiring prematurely or update session metadata to reflect changes in user permissions or settings.
5. **Deleting Sessions**: When a user logs out or their session expires, you can use the Session API to delete the corresponding session from your application's records. This helps maintain data integrity and security by ensuring that inactive sessions are properly removed from the system.
Overall, ZITADEL's Session API simplifies session management within your application, providing a convenient and secure way to track user sessions, enforce session policies, and maintain a seamless user experience. By integrating the Session API into your application, you can effectively manage user authentication and access control while ensuring data privacy and security.
## Use the Hosted Login to sign-in users
ZITADEL provides a hosted single-sign-on page for secure user authentication within your applications.
This centralized authentication interface simplifies application integration by offering a ready-to-use login experience.
For a comprehensive understanding of the hosted login page and its capabilities, please refer to our [dedicated guide](/guides/integrate/login/hosted-login)
The hosted login is particularly well-suited for scenarios where:
- **Minimal branding is required:** If your primary focus is on functionality over a highly customized look and feel.
- **Standard authentication flows suffice:** Your application doesn't necessitate complex or unique authentication processes.
- **OIDC or SAML are suitable:** Your application integrates seamlessly with industry-standard protocols.
- **Time-to-market is critical:** You need a rapid and efficient authentication solution to accelerate your development timeline.
- **Embedding the login UI is unnecessary:** You prefer a separate, hosted login page for user authentication.
## Build a custom Login UI to authenticate users
In certain cases, you want to build your own login UI to optimize your user experience.
We have dedicated guides on [how to build your custom login UI](/guides/integrate/login-ui) with ZITADEL.
When building your own login UI, you will leverage the [Session API](#zitadel-session-api) to authenticate users and manage user sessions.
<Callout title="Session API support for OIDC and SAML">
At the moment developers can only integrate with ZITADEL's Session API and not with standard compliant OpenID Connect or SAML flows.
</Callout>
### When to build your custom Login UI
Main reasons why developers might want to build their own login UI include:
1. **Embedding Login in your application**: From a security standpoint you should follow the best practice recommendation to open a browser and then redirect to your application. For certain applications, such as games or mobile apps, you might want to embed the login for a better user experience
2. **Customize business process**: You might want to change existing flows that are provided by the hosted login to fit your business process. Make sure to validate that your customization can't be handled by [Actions](/guides/manage/customize/behavior) instead.
3. **Customize branding**: We designed the hosted login with security in mind, which limits the [customization capabilities](/guides/manage/customize/branding). Besides that we might not be able to handle all requirements for custom branding. If you believe your customization should be part of the hosted login instead, please open an [improvement idea](https://github.com/zitadel/zitadel/issues/new/choose).
4. **Independence of standards** You don't want or need to rely on industry standards for authentication such as OpenID Connect or SAML for authenticating users.
## Further reference
- Learn how to [register and onboard users](/guides/integrate/onboarding)
- Learn how to [build your own login UI](/guides/integrate/login-ui) based on ZITADEL
- Learn how to [configure ZITADEL](/guides/manage/console/console-overview) through the Management Console
@@ -0,0 +1,58 @@
import PKCE from "./authmethods/_pkce.mdx";
import Basic from "./authmethods/_basic.mdx";
import Implicit from "./authmethods/_implicit.mdx";
import PKCENative from "./authmethods/_pkcenative.mdx";
import JWTPrivateKey from "./authmethods/_jwtpk.mdx";
export default function AuthMethods(props) {
const { Tab, Tabs } = props.components || {};
return props.selected == "web" ? (
<div>
<Tabs
groupId="auth-type"
default="pkce"
values={[
{ label: "PKCE", value: "pkce" },
{ label: "Basic Auth", value: "basic" },
{ label: "JWT with Private Key", value: "jwtpk" },
]}
>
<Tab value="pkce">
<PKCE components={props.components} />
</Tab>
<Tab value="basic">
<Basic components={props.components} />
</Tab>
<Tab value="jwtpk">
<JWTPrivateKey components={props.components} />
</Tab>
</Tabs>
</div>
) : props.selected == "native" ? (
<Tabs
groupId="auth-type"
default="pkce"
values={[{ label: "PKCE", value: "pkce" }]}
>
<Tab value="pkce">
<PKCE components={props.components} Native />
</Tab>
</Tabs>
) : props.selected == "spa" ? (
<Tabs
groupId="auth-type"
default="pkce"
values={[
{ label: "PKCE", value: "pkce" },
{ label: "Implicit", value: "implicit" },
]}
>
<Tab value="pkce">
<PKCE components={props.components} />
</Tab>
<Tab value="implicit">
<Implicit components={props.components} />
</Tab>
</Tabs>
) : null;
}
@@ -0,0 +1,9 @@
---
title: Authentication Methods
---
import AuthMethods from './_authmethods.mdx';
ZITADEL supports various authentication methods for OIDC clients.
<AuthMethods components={props.components} />
@@ -0,0 +1,69 @@
#### redirect_uri
After selecting the authentication method, you can register a redirect_uri and post_logout_redirect_uri.
The redirect_uri will be called after user authentication for code exchange.
You can even register multiple, but typically one will be enough. If you need to distinguish between different scenarios
or environments we recommend using the `state` parameter for the former and multiple projects for the latter.
## Auth Request
To initialize the user authentication, you will have to create an authorization request using HTTP GET in the user agent (browser)
on /authorize with at least the following parameters:
- `client_id`: this tells the authorization server which application it is, copy from Management Console
- `redirect_uri`: where the authorization code is sent to after the user authentication, must be one of the registered in the previous step
- `response_type`: if you want to have a code (authorization code flow) or directly a token (implicit flow), so when ever possible use `code`
- `scope`: what scope you want to grant to the access_token / id_token, minimum is `openid`, if you're unsure what you need you might start with `openid profile email`
We recommend always using two additional parameters `state` and `nonce`. The former enables you to transfer a state through
the authentication process. The latter is used to bind the client session with the id_token and to mitigate replay attacks.
You don't need any additional parameter for this request. We're identifying the app by the `client_id` parameter.
Try out the request in our [OIDC Authentication Request Playground](https://zitadel.com/playgrounds/oidc?auth_method=Client%20Secret%20Basic).
### Additional parameters and customization
There are additional parameters and values you can provide to satisfy your use case and to customize the user's authentication flow.
Please check the [authorization_endpoint reference](/apis/openidoauth/endpoints#authorization-endpoint) in the OAuth / OIDC documentation.
## Callback
Regardless of a successful or error response from the authorization_endpoint, the authorization server will call your
callback endpoint you provided by 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>
Upon successful authentication you'll be given a `code` and if provided the unmodified `state` parameter.
You will need this `code` in the token request.
If a parameter was missing, malformed or any other error occurred, your answer will contain an `error` stating the error type,
possibly an `error_description` providing some information about the error and its reason and the `state` parameter.
Check the [error response section](/apis/openidoauth/endpoints#error-response) in the authorization_endpoint reference.
## Token request
Next you will have to exchange the given `code` for the tokens. For this HTTP POST request (form-urlencoded) you will need to provide the following:
- code: the code that was issued from the authorization request
- grant_type: must be `authorization_code`
- redirect_uri: callback uri where the code was sent to. Must match exactly the redirect_uri of the authorization request
Depending on your authentication method you'll need additional headers and parameters:
Send your `client_id` and `client_secret` as Basic Auth Header. Note that OAuth2 requires client_id and client_secret to be form url encoded.
So check [Client Secret Basic Auth Method](/apis/openidoauth/authn-methods#client-secret-basic) on how to build it correctly.
```curl
curl --request POST \
--url ${CUSTOM_DOMAIN}/oauth/v2/token \
--header 'Authorization: Basic ${basic}' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data grant_type=authorization_code \
--data code=${code} \
--data redirect_uri=${redirect_uri}
```
@@ -0,0 +1,49 @@
<Callout type="warn" title="Security Notice">
In contrast to the Code Flow, where you'll receive a code for token exchange, with the implicit flow you'll receive
the tokens directly from the authorization endpoint. This is unsecure and might lead to token leakage and replay attacks.
It will further be removed in OAuth 2.1 for the exact same reason.
We therefore discourage the use of Implicit Flow and do not cover the flow in this guide.
</Callout>
If you still need to rely on the implicit flow, simply keep in mind that the response on the authorization_endpoint is
the same you would be given on the token_endpoint and check the [OAuth / OIDC endpoint documentation](/apis/openidoauth/endpoints) for more information.
#### redirect_uri
After selecting the authentication method, you can register a redirect_uri and post_logout_redirect_uri.
The redirect_uri will be called after user authentication for code exchange.
You can even register multiple, but typically one will be enough. If you need to distinguish between different scenarios
or environments we recommend using the `state` parameter for the former and multiple projects for the latter.
## Auth Request
To initialize the user authentication, you will have to create an authorization request using HTTP GET in the user agent (browser)
on /authorize with at least the following parameters:
- `client_id`: this tells the authorization server which application it is, copy from Management Console
- `redirect_uri`: where the authorization code is sent to after the user authentication, must be one of the registered in the previous step
- `response_type`: if you want to have a code (authorization code flow) or directly a token (implicit flow), so when ever possible use `code`
- `scope`: what scope you want to grant to the access_token / id_token, minimum is `openid`, if you're unsure what you need you might start with `openid profile email`
When using the Implicit Flow you will also have to provide a `nonce` parameter to bind the client session to the id_token and to mitigate replay attacks. Furthermore, we recommend using a `state` parameter, which enables you to transfer a state through the authentication process.
### Additional parameters and customization
There are additional parameters and values you can provide to satisfy your use case and to customize the user's authentication flow.
Please check the [authorization_endpoint reference](/apis/openidoauth/endpoints#authorization-endpoint) in the OAuth / OIDC documentation.
## Callback
Regardless of a successful or error response from the authorization_endpoint, the authorization server will call your callback endpoint you provided by 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>
Upon successful authentication you'll be given the `access_token`, `id_token`, `expires_in` and if provided the unmodified `state` parameter, as you would be given from the token_endpoint when using Authorization Code Flow.
If a parameter was missing, malformed or any other error occurred, your answer will contain an `error` stating the error type,
possibly an `error_description` providing some information about the error and its reason and the `state` parameter.
Check the [error response section](/apis/openidoauth/endpoints#error-response) in the authorization_endpoint reference.
@@ -0,0 +1,72 @@
#### redirect_uri
After selecting the authentication method, you can register a redirect_uri and post_logout_redirect_uri.
The redirect_uri will be called after user authentication for code exchange.
You can even register multiple, but typically one will be enough. If you need to distinguish between different scenarios
or environments we recommend using the `state` parameter for the former and multiple projects for the latter.
## Auth Request
To initialize the user authentication, you will have to create an authorization request using HTTP GET in the user agent (browser)
on /authorize with at least the following parameters:
- `client_id`: this tells the authorization server which application it is, copy from Management Console
- `redirect_uri`: where the authorization code is sent to after the user authentication, must be one of the registered in the previous step
- `response_type`: if you want to have a code (authorization code flow) or directly a token (implicit flow), so when ever possible use `code`
- `scope`: what scope you want to grant to the access_token / id_token, minimum is `openid`, if you're unsure what you need you might start with `openid profile email`
We recommend always using two additional parameters `state` and `nonce`. The former enables you to transfer a state through
the authentication process. The latter is used to bind the client session with the id_token and to mitigate replay attacks.
You don't need any additional parameter for this request. We're identifying the app by the `client_id` parameter.
So your request might look like this (linebreaks and whitespace for display reasons):
Try out the request in our [OIDC Authentication Request Playground](https://zitadel.com/playgrounds/oidc?auth_method=Client%20Secret%20Basic).
### Additional parameters and customization
There are additional parameters and values you can provide to satisfy your use case and to customize the user's authentication flow.
Please check the [authorization_endpoint reference](/apis/openidoauth/endpoints#authorization-endpoint) in the OAuth / OIDC documentation.
## Callback
Regardless of a successful or error response from the authorization_endpoint, the authorization server will call your
callback endpoint you provided by 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>
Upon successful authentication you'll be given a `code` and if provided the unmodified `state` parameter.
You will need this `code` in the token request.
If a parameter was missing, malformed or any other error occurred, your answer will contain an `error` stating the error type,
possibly an `error_description` providing some information about the error and its reason and the `state` parameter.
Check the [error response section](/apis/openidoauth/endpoints#error-response) in the authorization_endpoint reference.
## Token request
Next you will have to exchange the given `code` for the tokens. For this HTTP POST request (form-urlencoded) you will need to provide the following:
- code: the code that was issued from the authorization request
- grant_type: must be `authorization_code`
- redirect_uri: callback uri where the code was sent to. Must match exactly the redirect_uri of the authorization request
Depending on your authentication method you'll need additional headers and parameters:
Send a JWT in the `client_assertion` and set the `client_assertion_type` to `urn:ietf:params:oauth:client-assertion-type:jwt-bearer`
for us to validate the signature against the registered public key:
```curl
curl --request POST \
--url ${CUSTOM_DOMAIN}/oauth/v2/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data grant_type=authorization_code \
--data code=${code} \
--data redirect_uri=${redirect_uri} \
--data client_assertion=${client_assertion} \
--data client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
```
@@ -0,0 +1,79 @@
#### redirect_uri
After selecting the authentication method, you can register a redirect_uri and post_logout_redirect_uri.
The redirect_uri will be called after user authentication for code exchange.
You can even register multiple, but typically one will be enough. If you need to distinguish between different scenarios
or environments we recommend using the `state` parameter for the former and multiple projects for the latter.
## Auth Request
To initialize the user authentication, you will have to create an authorization request using HTTP GET in the user agent (browser)
on /authorize with at least the following parameters:
- `client_id`: this tells the authorization server which application it is, copy from Management Console
- `redirect_uri`: where the authorization code is sent to after the user authentication, must be one of the registered in the previous step
- `response_type`: if you want to have a code (authorization code flow) or directly a token (implicit flow), so when ever possible use `code`
- `scope`: what scope you want to grant to the access_token / id_token, minimum is `openid`, if you're unsure what you need you might start with `openid profile email`
We recommend always using two additional parameters `state` and `nonce`. The former enables you to transfer a state through
the authentication process. The latter is used to bind the client session with the id_token and to mitigate replay attacks.
### PKCE
PKCE stands for Proof Key for Code Exchange. So other than "normal" code exchange, the PKCE does not authenticate using
client_id and client_secret but an additional code. You will have to generate a random string, hash it and send this hash
on the authorization_endpoint. On the token_endpoint you will then send the plain string for the authorization to compute
the hash as well and to verify it's correct. In order to do so you're required to send the following two parameters as well:
- `code_challenge`: the base64url representation of the (sha256) hash of your random string
- `code_challenge_method`: must always be `S256` standing for sha256, this is the only algorithm we support
For example for `random-string` the code_challenge would be `9az09PjcfuENS7oDK7jUd2xAWRb-B3N7Sr3kDoWECOY`
Try out the request in our [OIDC Authentication Request Playground](https://zitadel.com/playgrounds/oidc).
### Additional parameters and customization
There are additional parameters and values you can provide to satisfy your use case and to customize the user's authentication flow.
Please check the [authorization_endpoint reference](/apis/openidoauth/endpoints#authorization-endpoint) in the OAuth / OIDC documentation.
## Callback
Regardless of a successful or error response from the authorization_endpoint, the authorization server will call your
callback endpoint you provided by 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>
Upon successful authentication you'll be given a `code` and if provided the unmodified `state` parameter.
You will need this `code` in the token request.
If a parameter was missing, malformed or any other error occurred, your answer will contain an `error` stating the error type,
possibly an `error_description` providing some information about the error and its reason and the `state` parameter.
Check the [error response section](/apis/openidoauth/endpoints#error-response) in the authorization_endpoint reference.
## Token request
Next you will have to exchange the given `code` for the tokens. For this HTTP POST request (form-urlencoded) you will need to provide the following:
- code: the code that was issued from the authorization request
- grant_type: must be `authorization_code`
- redirect_uri: callback uri where the code was sent to. Must match exactly the redirect_uri of the authorization request
Depending on your authentication method you'll need additional headers and parameters:
Send your `client_id` and the previously generated string as `code_verifier` for us to recompute the `code_challenge` of the authorization request:
```curl
curl --request POST \
--url ${CUSTOM_DOMAIN}/oauth/v2/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data grant_type=authorization_code \
--data code=${code} \
--data redirect_uri=${redirect_uri} \
--data client_id=${client_id} \
--data code_verifier=${code_verifier}
```
@@ -0,0 +1,63 @@
#### redirect_uri
Native clients might have to register multiple redirect_uris as operating system have different requirements.
Typically, you register a redirect_uri starting with a custom protocol, e.g. `ch.zitadel.app://callback`.
You're also allowed to use http://localhost, http://127.0.0.1 and http:[::1] without specifying a port: `http://locahost/callback`.
#### post creation actions
After the application creation, you might want to set additional options like `refresh_token` and `additional origins`.
If you want to request refresh_tokens and use them to renew the user's access_tokens without their interaction,
enable them in the OIDC Configuration section by ticking the checkbox.
When calling the userinfo_endpoint or any ZITADEL API, we will check if an origin header is sent. This is automatically done
by the user agent. If one is sent we will check if the origin is allowed for your application. By default, all computed
origins of the redirect_uri list are allowed.
So if your native app is built with a JavaScript base framework like ReactNative and you only specified redirect_uris
with a custom protocol, you will need to add the origin where the app is served from, e.g. `http://localhost:8100`.
### Additional parameters and customization
There are additional parameters and values you can provide to satisfy your use case and to customize the user's authentication flow.
Please check the [authorization_endpoint reference](/apis/openidoauth/endpoints#authorization-endpoint) in the OAuth / OIDC documentation.
## Callback
Regardless of a successful or error response from the authorization_endpoint, the authorization server will call your
callback endpoint you provided by 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>
Upon successful authentication you'll be given a `code` and if provided the unmodified `state` parameter.
You will need this `code` in the token request.
If a parameter was missing, malformed or any other error occurred, your answer will contain an `error` stating the error type,
possibly an `error_description` providing some information about the error and its reason and the `state` parameter.
Check the [error response section](/apis/openidoauth/endpoints#error-response) in the authorization_endpoint reference.
## Token request
Next you will have to exchange the given `code` for the tokens. For this HTTP POST request (form-urlencoded) you will need to provide the following:
- code: the code that was issued from the authorization request
- grant_type: must be `authorization_code`
- redirect_uri: callback uri where the code was sent to. Must match exactly the redirect_uri of the authorization request
Depending on your authentication method you'll need additional headers and parameters:
Send your `client_id` and the previously generated string as `code_verifier` for us to recompute the `code_challenge` of the authorization request:
```curl
curl --request POST \
--url ${CUSTOM_DOMAIN}/oauth/v2/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data grant_type=authorization_code \
--data code=${code} \
--data redirect_uri=${redirect_uri} \
--data client_id=${client_id} \
--data code_verifier=${code_verifier}
```
@@ -0,0 +1,125 @@
---
title: Integrating Your Application with ZITADEL using RFC 8628 OAuth 2.0 Device Authorization Flow
sidebar_label: Device Authorization Flow
---
ZITADEL implements device authorization as per [RFC 8628](https://datatracker.ietf.org/doc/html/rfc8628). This document demonstrates its use.
This documentation aims to guide you through the process of seamlessly integrating your application with ZITADEL, leveraging the capabilities of [RFC 8628](https://datatracker.ietf.org/doc/html/rfc8628). ZITADEL is a powerful identity and access management (IAM) solution that provides robust authentication and authorization services for your applications. RFC 8628, also known as "OAuth 2.0 Device Authorization Grant", offers a standardized protocol for devices with limited input capabilities to obtain user authorization. By following the steps outlined here, you'll be able to empower your application with secure authentication and access control mechanisms provided by ZITADEL, ensuring a smooth and secure user experience.
## What is RFC 8628?
[RFC 8628](https://datatracker.ietf.org/doc/html/rfc8628), or the OAuth 2.0 Device Authorization Grant, defines a protocol for OAuth 2.0 clients that have limited input capabilities, such as devices with no browser or low-power sensors. It allows such devices to obtain user consent for authorizing access to protected resources by directing the user to a device-friendly authentication endpoint.
## Why Integrate with Zitadel?
ZITADEL offers a comprehensive identity and access management solution, providing features like single sign-on (SSO), multi-factor authentication (MFA), user management, and more. By integrating your application with ZITADEL, you can leverage these powerful features to enhance the security and usability of your application without reinventing the wheel.
## Getting Started
To begin integrating your application with Zitadel using RFC 8628, follow the steps outlined in this documentation. We'll walk you through the process, from setting up your application in the Zitadel management console to handling user authentication and access tokens in your application code.
1. Go to “Organization”/”Projects”.
2. Under “Applications” click the “New” button.
<img src="/docs/img/device-auth/device-auth-01.png" alt="Device Authorization Flow in ZITADEL"/>
3. Select “Native” and enter a name for the application, and click “Continue”.
<img src="/docs/img/device-auth/device-auth-02.png" alt="Device Authorization Flow in ZITADEL"/>
4. Select “Device Code”. Click “Continue”.
<img src="/docs/img/device-auth/device-auth-03.png" alt="Device Authorization Flow in ZITADEL"/>
5. Check the details and click “Create”.
<img src="/docs/img/device-auth/device-auth-04.png" alt="Device Authorization Flow in ZITADEL"/>
6. Copy the “Client ID” and store it for later use.
<img src="/docs/img/device-auth/device-auth-05.png" alt="Device Authorization Flow in ZITADEL"/>
## Device Client Example
The [ZITADEL OpenID Connect client and server library](https://github.com/zitadel/oidc/) written for Go has a device client example, which can behave and authenticate as a device. In order to run this client, you need a recent version of Go (>=1.20) installed on your device.
The example requires two environment variables to be set:
- `ISSUER`: server address of your instance or domain. You can find the issuer URL in the “URLs” section. In this example, it will be set to [https://test-0o6zvq.zitadel.cloud](https://test-0o6zvq.zitadel.cloud). **Do not use a trailing slash!**
<img src="/docs/img/device-auth/device-auth-06.png" alt="Device Authorization Flow in ZITADEL"/>
- `CLIENT_ID`: the Client ID we obtained earlier.
Replace `ISSUER` and `CLIENT_ID` values with the ones you obtained and run the example as shown below:
``` bash
ISSUER="https://test-0o6zvq.zitadel.cloud" \
CLIENT_ID="232685602728952637@device_auth" \
go run github.com/zitadel/oidc/v2/example/client/device@latest
```
You should see some info-level log output with response data and a line with login instructions as given below:
``` bash
Please browse to https://test-0o6zvq.zitadel.cloud/device and enter code GQWC-FWFK
INFO[0002] start polling
```
At this point, the device app starts polling the token endpoint at 5-second intervals until the request is allowed, denied, or times out (currently 5 minutes).
## Authenticate
When you browse to the given URL and the device code is entered, the authentication flow for a user is started. If you are already logged in, it skips right ahead to the final screen where you can choose to allow or deny the request. If you are not logged in, you will have to enter your credentials for login.
<img src="/docs/img/device-auth/device-auth-07.png" alt="Device Authorization Flow in ZITADEL"/>
<img src="/docs/img/device-auth/device-auth-08.png" alt="Device Authorization Flow in ZITADEL"/>
<img src="/docs/img/device-auth/device-auth-09.png" alt="Device Authorization Flow in ZITADEL"/>
<img src="/docs/img/device-auth/device-auth-10.png" alt="Device Authorization Flow in ZITADEL"/>
<img src="/docs/img/device-auth/device-auth-11.png" alt="Device Authorization Flow in ZITADEL"/>
When “allow” is clicked, the device (the CLI in this case) will receive a token on the next poll. A log line will be shown as given below:
``` bash
INFO[0165] successfully obtained token: &{Um2W5Od0yBU0KHfhP0AD0726rXrpxlepOR7yyftMvocgMWr25pVCuca1oiSSLiQjcXQqCEA Bearer 43199 }
```
At this point, the program terminates as it doesn’t have any other useful purpose. Regular device apps would of course use the token to consume the actual service.
## Code URL
During the start of the authorization flow ,there is a log line printed with the complete response object from ZITADEL that looks like this:
``` bash
resp&{dOcbPeysDhT26ZatRh9n7Q KPVZ-DJGG https://test-0o6zvq.zitadel.cloud/device https://test-0o6zvq.zitadel.cloud/device?user_code=GQWC-FWFK 300 5}
```
From this object, we used the `verification_uri` and `user_code` fields to print the information to the user to go to an address and enter the code. We also return a `verification_uri_complete` field, which already includes the code and allows skipping manual entry of the code.
``` bash
https://test-0o6zvq.zitadel.cloud/device?user_code=GQWC-FWFK
```
In real-life applications, this could be used to create a QR code on the device screen, and users can use their mobile phones to scan the code to go to the required URL for authentication. In the context of this example, you can restart the device example program and copy/paste the complete link instead of manually entering it.
## Security considerations
The user uses a low entropy code to link a device to their account, which is easy to brute-force. Also, the code or QR might be displayed in environments where others could observe and use the code to authenticate the device (hijack the session) to their account.
The device authorization grant specification was written with the philosophy that hijacking such sessions is not beneficial for the attacker. For example, with an on-demand streaming service, the attacker would be paying for the services the user is benefiting from. Hence, economically, the attack would not make sense.
See [here](https://datatracker.ietf.org/doc/html/rfc8628#section-5) for more information.
## Client library
For Go clients, the ZITADEL OIDC repository already includes device authorization in the `rp` package.
Use [rp.DeviceAuthorization](https://pkg.go.dev/github.com/zitadel/oidc/v2/pkg/client/rp#DeviceAuthorization) to start the flow and receive the URL and user code.
[rp.DeviceAccessToken](https://pkg.go.dev/github.com/zitadel/oidc/v2/pkg/client/rp#DeviceAccessToken) polls the token endpoint and returns the access token once it succeeds.
The client implementation is rather simple and can easily be recreated in other languages. Go can be used as an example, or simply follow the [RFC](https://datatracker.ietf.org/doc/html/rfc8628) as a guide.
@@ -0,0 +1,25 @@
---
title: "Authenticate users with OpenID Connect (OIDC)"
description: "This guide explains how to utilize ZITADEL for user authentication within your applications using OpenID Connect (OIDC). Here, we offer comprehensive guidance on seamlessly integrating ZITADEL's authentication features, ensuring both security and user experience excellence. Throughout this documentation, we'll cover the setup process for ZITADEL authentication, including the recommended OIDC flows tailored to different application types. Additionally, we'll provide clear instructions on securely signing out or logging out users from your application, ensuring data security and user privacy. With our guidance, you'll be equipped to leverage ZITADEL's authentication capabilities effectively, enhancing your application's security posture while delivering a seamless login experience for your users."
---
{/* THIS FILE IS AUTO-GENERATED FROM SIDEBAR-DATA.
ANY MANUAL CHANGES WILL BE OVERWRITTEN.
*/}
import { Card, Cards } from 'fumadocs-ui/components/card';
import { FileText, Folder, Link as LinkIcon } from 'lucide-react';
<Cards>
<Card title="Oauth Recommended Flows" href="/guides/integrate/login/oidc/oauth-recommended-flows" icon={<FileText />} />
<Card title="Login Users" href="/guides/integrate/login/oidc/login-users" icon={<FileText />} />
<Card title="Device Authorization" href="/guides/integrate/login/oidc/device-authorization" icon={<FileText />} />
<Card title="Endpoints" href="/apis/openidoauth/endpoints" icon={<FileText />} />
<Card title="Authn Methods" href="/apis/openidoauth/authn-methods" icon={<FileText />} />
<Card title="Scopes" href="/apis/openidoauth/scopes" icon={<FileText />} />
<Card title="Claims" href="/apis/openidoauth/claims" icon={<FileText />} />
<Card title="Grant Types" href="/apis/openidoauth/grant-types" icon={<FileText />} />
<Card title="Authrequest" href="/apis/openidoauth/authrequest" icon={<FileText />} />
<Card title="Webkeys" href="/guides/integrate/login/oidc/webkeys" icon={<FileText />} />
<Card title="Token Exchange" href="/guides/integrate/token-exchange" icon={<FileText />} />
</Cards>
@@ -0,0 +1,93 @@
---
title: Authenticate users with OpenID Connect
sidebar_label: OIDC Code Flow + PKCE
---
import AuthMethods from "./_authmethods.mdx";
## Overview
This guide will show you how to use ZITADEL to login users into your application (authentication).
It will guide you step-by-step through the basics and point out on how to customize process.
## OIDC / OAuth Flow
OAuth and therefore OIDC know three different application types:
- Web (Server-side web applications such as java, .net, ...)
- Native (native, mobile or desktop applications)
- User Agent (single page applications / SPA, generally JavaScript executed in the browser)
Depending on the app type you're trying to register, there are small differences.
But regardless of the app type we recommend using Proof Key for Code Exchange (PKCE).
Please read the following guide about the [different-client-profiles](./oauth-recommended-flows#different-client-profiles) and why to use PKCE.
### Code Flow
For a basic understanding of OAuth and its flows, we'll briefly describe the most important flow: **Authorization Code**.
The following diagram demonstrates a basic authorization_code flow:
![](../../../../../public/img/guides/auth_flow.png)
1. When an unauthenticated user visits your application,
2. you will create an authorization request to the authorization endpoint.
3. The Authorization Server (ZITADEL) will send an HTTP 302 to the user's browser, which will redirect them to the login UI.
4. The user will have to authenticate using the demanded auth mechanics.
5. Your application will be called on the registered callback path (redirect_uri) and be provided an authorization_code.
6. This authorization_code must then be sent together with you applications authentication (client_id + client_secret) to the token_endpoint
7. In exchange the Authorization Server (ZITADEL) will return an access_token and if requested a refresh_token and in the case of OIDC an id_token as well
8. The access_token can then be used to call a Resource Server (API). The token will be sent as Authorization Header.
This flow is the same when using PKCE or JWT with Private Key for authentication.
## Create Application
To create an application, open your project in Management Console and start by clicking on the "New" button in the Application section.
#### Application type
This will start a wizard asking you for an application name and a type.
<Tabs
groupId="app-type"
default="web"
values={[
{'label': 'Web Application', 'value': 'web'},
{'label': 'Native Application', 'value': 'native'},
{'label': 'Single Page Application', 'value': 'spa'},
]}
>
<Tab value="web">
#### Authentication method
After selecting WEB, you'll next have to choose an authentication method. As mentioned before we recommend using PKCE.
For even better security you could switch to JWT or just rely on the standard Code Flow. For security reasons we don't
recommend using POST and will not cover it in this guide.
Please change the authentication method here as well, if you did so in the wizard, so we can better guide you through the process:
<AuthMethods components={props.components} selected="web" />
</Tab>
<Tab value="native">
#### Authentication method
When selecting Native the authentication method always needs to be PKCE.
<AuthMethods components={props.components} selected="native" />
</Tab>
<Tab value="spa">
#### Authentication method
When selecting SPA the recommended authentication method is again PKCE. All common Frameworks like Angular, React, Vue.js and so on
are able to successfully authenticate with PKCE. Our management console UI for instance uses PKCE as well.
<AuthMethods components={props.components} selected="spa" />
</Tab>
</Tabs>
@@ -0,0 +1,75 @@
---
title: Log Out Users from an Application with ZITADEL
sidebar_label: Logout
---
This guide shows you the different concepts and use cases of the logout process and how to use it in ZITADEL.
## OpenID Connect Single Logout
### Single Sign On (SSO) vs Single Logout (SLO)
Single Sign On (SSO) allows a user to login once without the need for authentication across multiple applications.
Single Logout (SLO) is the counterpart to SSO. With SLO a user can logout and terminate sessions across many applications, without actively logging out from them.
The purpose of a logout is to terminate a user session.
Depending on how the session handling is implemented, there are different mechanisms that can be used.
There are two possibilities where sessions are stored:
- The User Agent (e.g the Browser or Mobile App) stores the session information (e.g. in a cookie)
- A Server stores the session information (e.g. in a database or api)
### OpenID Connect Logout
OpenID Connect defines three logout mechanisms to address the different architectures:
- [OpenID Connect Session Management 1.0](https://openid.net/specs/openid-connect-session-1_0.html)
- [OpenID Connect Front-Channel Logout 1.0](https://openid.net/specs/openid-connect-frontchannel-1_0.html)
- [OpenID Connect Back-Channel Logout 1.0](https://openid.net/specs/openid-connect-backchannel-1_0.html)
#### Session Management
Session Management in OpenID Connect defines a mechanism for a client (Relying Party, RP) to monitor the state of the user session from a identity provider (OP, e.g ZITADEL).
When a user logs out of the provider, the user's session is terminated and the client can in turn reflect that in its behavior.
#### RP initiated Logout
With the RP initiated flow all logout processes are triggered by a request from the client (e.g your application) through a well defined standard API by redirecting the user-agent to the [end_session_endpoint](/apis/openidoauth/endpoints#end-session-endpoint).
If you have specified some post_logout_redirect_uris on your client you have to send either the id_token_hint or the client_id as param in your request.
So ZITADEL is able to read the configured redirect uris.
```
GET ${CUSTOM_DOMAIN}/oidc/v1/end_session
?id_token_hint={id_token}
&post_logout_redirect_uri=https://rp.example.com/logged_out
&state=random_string
```
#### Front-Channel Logout
The user agent handles the front-channel logout.
Each client with an OpenID Session of the user that supports front-channel renders an iframe so the logout request is performed on all clients parallel.
<Callout>
This is not yet implemented in ZITADEL
</Callout>
#### Back-Channel Logout
The back-channel logout is a mechanism on the server-side and the user agent does not have to do anything.
The user will logout from all clients even in the case the user agent was closed.
<Callout>
This is not yet implemented in ZITADEL
</Callout>
## Scenarios
1. Logout all users from the current user-agent/browser (current implementation of ZITADEL end_session_endpoint)
2. Logout my user from the current user-agent/browser
3. Logout my user from the all devices
## Session Handling in ZITADEL
The session management in ZITADEL is done on the server side.
As soon as a user authenticates the first time, ZITADEL generates a user-agent cookie.
All open sessions on that user-agent (browser) will be stored to the same cookie.
If you delete the cookie in your browser, we will not be able to find out which sessions belong to your user-agent.
@@ -0,0 +1,103 @@
---
title: Recommended authorization flows with OpenID Connect (OIDC) and OAuth 2.x
sidebar_label: Recommended authorization flows
---
## Introduction
In this guide we will go over some basics on how to obtain an authorization with OpenID Connect 1.x and OAuth 2.x.
ZITADEL does not make assumptions about the application type you are about to integrate. Therefore you must qualify and define an appropriate method for your users to gain authorization to access your application (“authentication flow”). Your choice depends on the application’s ability to maintain the confidentiality of client credentials and the technological capabilities of your application. If you choose a deprecated or unfeasible flow to obtain authorization for your application, your users’ credentials may be compromised.
We invite you to further explore the different authorization flows in the OAuth 2.x standard. For the start we assume that you have a brand-new application (ie. without legacy requirements) and you found an reliable SDK/Library that does the heavy lifting for you.
So this module will only go over the basics and explain why we recommend the flow “Authorization Flow with PKCE” as default for most applications. We will also cover the case of machine-to-machine communication, ie. where there is no interactive login. Further we will guide you to further reading viable alternatives, if the default flow is not feasible.
## Basics of Federated Identity
Although Federated Identities are not a new concept ([RFC 6749](https://tools.ietf.org/html/rfc6749), “The OAuth 2.0 Authorization Framework” was released in 2012) it is important to highlight the difference between the traditional client-server authentication model and the concept of delegated authorization and authentication.
The aforementioned RFC provides us with some [problems and limitations](https://tools.ietf.org/html/rfc6749#section-1) of the client-server authentication, where a client requests a protected resource on the server by authenticating with the user’s credentials:
- Applications need to store users credentials (eg, password) for future use; compromise of any application results in compromise of the end-users credentials
- Servers are required to support password authentication
- Without means of limiting scope when providing the user’s credentials, the application gains overly broad access to protected resources
- Users cannot revoke access for a single application, but only for all by changing credentials
So what do we want to achieve with delegated authentication?
- Instead of implementing authentication on each server and trusting each server
- Users only **authenticate** with a trusted server (ie. ZITADEL), that validates the user’s identity through a challenge (eg, multiple authentication factors) and issues an **ID token** (OpenID Connect 1.x)
- Applications have means of **validating the integrity** of presented access and ID tokens
- Instead of sending around the user’s credentials
- Clients may access protected resources with an **access token** that is only valid for specific scope and limited lifetime (OAuth 2.x)
- Users have to **authorize** applications to access certain [**scopes**](/apis/openidoauth/scopes) (eg, email address or custom roles). Applications can request [**claims**](/apis/openidoauth/claims) (key:value pairs, eg email address) for the authorized scopes with the access token or ID token from ZITADEL
- Access tokens are bearer tokens, meaning that possession of the token provides access to a resource. But the tokens expire frequently and the application must request a new access token via **refresh token** or the user must reauthenticate
![](../../../../../public/img/guides/consulting_federated_identities_basics.png)
This is where the so-called “flows” come into play: There are a number of different flows on how to handle the process from authentication, over authorization, getting tokens and requesting additional information about the user.
Maybe interesting to mention is that we are mostly concerned with choosing the right OAuth 2.x flows (alas “authorization”). OpenID Connect extends the OAuth 2.x flow with useful features like endpoint discovery (where to ask), ID Token (who is the user, when and how did she authenticate), and UserInfo Endpoint (getting additional information about the user).
## Different client profiles
As mentioned in the beginning of this module, there are two main determinants for choosing the optimal authorization flow:
1. Client’s ability to maintain the confidentiality of client credentials
2. Technological limitations
OAuth 2.x defines two [client types](https://tools.ietf.org/html/rfc6749#section-2.1) based on their ability to maintain the confidentiality of their client credentials:
- Confidential: Clients capable of maintaining the confidentiality of their credentials (e.g., client implemented on a secure server with restricted access to the client credentials), or capable of secure client authentication using other means.
- Public: Clients incapable of maintaining the confidentiality of their credentials (e.g., clients executing on the device used by the resource owner, such as an installed native application or a web browser-based application), and incapable of secure client authentication via any other means.
The following table gives you a brief overview of different client profiles.
<table className="table-wrapper">
<tbody>
<tr>
<th>Confidentiality of client credentials</th>
<th>Client profile</th>
<th>Examples of clients</th>
</tr>
<tr>
<td rowSpan="2">Public</td>
<td>User-Agent</td>
<td>Single-page web applications (SPA), generally JavaScript executed in Browser</td>
</tr>
<tr>
<td>Native</td>
<td>Native, Mobile, or Desktop applications</td>
</tr>
<tr>
<td rowSpan="2">Confidential</td>
<td>Web</td>
<td>Server-side web applications such as java, .net, …</td>
</tr>
<tr>
<td>Machine-to-Machine</td>
<td>APIs, generally services without direct user-interaction at the authorization server</td>
</tr>
</tbody>
</table>
## Our recommended authorization flows
We recommend using the flow **“Authorization Code with Proof Key of Code Exchange (PKCE)”** ([RFC7636](https://tools.ietf.org/html/rfc7636)) for **User-Agent**, **Native**, and **Web** clients.
If you don’t have any technical limitations, you should favor the flow Authorization Code with PKCE over other methods. The PKCE part makes the flow resistant against authorization code interception attack as described well in RFC7636.
_So what about APIs?_
We recommend using **“JWT bearer token with private key”** ([RFC7523](https://tools.ietf.org/html/rfc7523)) for Machine-to-Machine clients.
What this means is that you have to send an JWT token, containing the [standard claims for access tokens](/apis/openidoauth/claims) and that is signed with your private key, to the token endpoint to request the access token. We will see how this works in another module about Service Accounts.
If you don’t have any technical limitations, you should prefer this method over other methods.
A JWT with a private key can also be used with client profile web to further enhance security.
In case you need alternative flows and their advantages and drawbacks, there will be a module to outline more methods and our recommended fallback strategy per client profile that are available in ZITADEL.
@@ -0,0 +1,357 @@
---
title: OpenID Connect and OAuth2 web keys
sidebar_label: Web Keys
---
Web Keys in ZITADEL are used to sign and verify JSON Web Tokens (JWT).
ID tokens are created, signed and returned by ZITADEL when a OpenID connect (OIDC) or OAuth2
authorization flow completes and a user is authenticated.
Optionally, ZITADEL can return JWTs for access tokens if the OIDC Application is configured for it.
## Introduction
ZITADEL uses asymmetric cryptography to sign and validate JWTs.
Keys are generated in pairs resulting in a private and public key.
Private keys are used to sign tokens.
Public keys are used to verify tokens.
OIDC clients need the public key to verify ID tokens.
OAuth2 API apps might need the public key if they want to client-side verification of a
JWT access tokens, instead of [introspection](/apis/openidoauth/endpoints#introspection-endpoint).
ZITADEL uses public key verification when API calls are made or when the userInfo or introspection
endpoints are called with a JWT access token.
### JSON Web Key
ZITADEL implements the [RFC7517 - JSON Web Key (JWK)](https://www.rfc-editor.org/rfc/rfc7517) format for storage and distribution of public keys.
Web keys in ZITADEL support a number of [JSON Web Algorithms (JWA)](https://www.rfc-editor.org/rfc/rfc7518) for digital signatures:
| Identifier | Description |
| ---------- | ------------------------------- |
| RS256 | RSASSA-PKCS1-v1_5 using SHA-256 |
| RS384 | RSASSA-PKCS1-v1_5 using SHA-384 |
| RS512 | RSASSA-PKCS1-v1_5 using SHA-512 |
| ES256 | ECDSA using P-256 and SHA-256 |
| ES384 | ECDSA using P-384 and SHA-384 |
| ES512 | ECDSA using P-512 and SHA-512 |
| EdDSA | EdDSA signature algorithms[^1] |
[^1]: EdDSA refers to both Ed25519 and Ed448 curves. ZITADEL only supports Ed25519 with a SHA-512 hashing algorithm. EdDSA is for JSON Object Signing is defined in [RFC8037](https://www.rfc-editor.org/rfc/rfc8037).
### Client Algorithm Support
Before customizing the algorithm the instance admin **MUST** make sure the complete app and API ecosystem
supports the chosen algorithm.
When all OIDC applications of an instance use opaque access tokens, and they call APIs which only use
introspection for token validation, only the OIDC applications will need to support the chosen algorithm.
If JWT access tokens are used and APIs do public key verification, those APIs need to support the chosen algorithm as well.
RS256 is widely considered the default algorithm and must be supported by all OIDC/Oauth2 providers, relying parties and resource servers.
This is also the default ZITADEL uses when [creating web keys](#creation).
It might be reasonable to assume RS384 and RS512 are just as supported, because those are just variations on RSA based keys.
The ES256, ES384 and ES512 might have reasonable support as well,
ECDSA is part of the same [JSON Web Algorithms (JWA)](https://www.rfc-editor.org/rfc/rfc7518) as RSA.
EdDSA usage is defined in the supplemental [RFC8037](https://www.rfc-editor.org/rfc/rfc8037),
and therefore may be less supported than the others.
Also, the `at_hash` claim in the ID token is a hashed string of the access token.
The hasher is usually defined by the keys `alg` header. For example:
- RS256 defines an RSA key and a SHA256 hasher.
- ES512 defines an elliptic curve key with the P-512 and SHA512 hasher.
Unfortunately, there is no published standard for the `at_hash` hasher used for EdDSA.
In fact, EdDSA may use different curves and internally uses different hashers:
- ed25519 uses SHA512;
- ed448 uses SHAKE256;
This resulted in a [proposal](https://bitbucket.org/openid/connect/issues/1125/_hash-algorithm-for-eddsa-id-tokens) at
the Open ID workgroup to follow suit and use the same hashing algorithms for the `at_hash` claim.
This means both signers and verifiers can't know the hasher by the `alg` value alone and need to inspect `crv` value as well.
Since the decision in the proposal isn't published yet,
there is a big change some OIDC client libraries don't have proper support for EdDSA / ed25519.
The ZITADEL back-end is written in Go. The Go developers have denied ed448 curve implementations to be included.
Therefore ZITADEL only uses ed25519 with a SHA512.
The same counts for [zitadel/oidc](https://github.com/zitadel/oidc) Go library.
## Web Key management
ZITADEL provides a resource based [web keys API](/reference/api/webkey).
The API allows the creation, activation, deletion and listing of web keys.
All public keys that are stored for an instance are served on the [JWKS endpoint](#json-web-key-set).
Applications need public keys for token verification and not all applications are capable of on-demand
key fetching when receiving a token with an unknown key ID (`kid` header claim).
Instead, those application may do a time-based refresh or only load keys at startup.
Using the web keys API, keys can be created first and activated for signing later.
This allows the keys to be distributed to the instance's apps and caches.
Once a key is deactivated, its public key will remain available for token verification until the web key is deleted.
Delayed deletion makes sure tokens that were signed before the key got deactivated remain valid.
When the `web_key` [feature](/reference/api/feature/zitadel.feature.v2.FeatureService.SetInstanceFeatures) is enabled the first time,
two web key pairs are created with one activated.
### Creation
The web key [create](/apis/resources/webkey_service_v2/zitadel-webkey-v-2-web-key-service-create-web-key) endpoint generates a new web key pair,
using the passed generator configuration from the request. This config is a one-of field of:
- RSA
- ECDSA
- ED25519
When the request does not contain any specific configuration,
[RSA](#rsa) is used as default with the default options as described below:
```bash
curl -L 'https://${CUSTOM_DOMAIN}/v2/web_keys' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
-d '{}'
```
#### RSA
The RSA generator config takes two enum values.
- The `bits` fields determines the size of the RSA key:
- `RSA_BITS_2048` (**default**)
- `RSA_BITS_3072`
- `RSA_BITS_4096`
- The `hasher` field sets the hash mode and
determines the `alg` header value of the web key:
- `RSA_HASHER_SHA256` results in the RS256 algorithm header. (**default**)
- `RSA_HASHER_SHA384` results in the RS384 algorithm header.
- `RSA_HASHER_SHA512` results in the RS512 algorithm header.
For example, to create a RSA web key with the size of 3072 bits and the SHA512 algorithm (RS512):
```bash
curl -L 'https://${CUSTOM_DOMAIN}/v2/web_keys' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
-d '{
"rsa": {
"bits": "RSA_BITS_3072",
"hasher": "RSA_HASHER_SHA512"
}
}'
```
#### ECDSA
The ECDSA generator config takes a single `curve` enum value which determines both the key's curve parameters and hashing algorithm:
- `ECDSA_CURVE_P256` uses the NIST P-256 curve and sets the ES256 algorithm header.
- `ECDSA_CURVE_P384` uses the NIST P-384 curve and sets the ES384 algorithm header.
- `ECDSA_CURVE_P512` uses the NIST P-512 curve and sets the ES512 algorithm header.
For example, to create a ECDSA web key with a P-256 curve and the SHA256 algorithm:
```bash
curl -L 'https://${CUSTOM_DOMAIN}/v2/web_keys' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
-d '{
"ecdsa": {
"curve": "ECDSA_CURVE_P256"
}
}'
```
#### ED25519
ED25519 is an EdDSA curve and currently the only EdDSA curve supported by ZITADEL.[^2]
No config is needed for ed25519 as its specification already includes the curve parameters.
ed25519 always uses the SHA512 hasher.
Note that the `alg` header for ed25519 is `EdDSA` and refers to both ed25519 and ed448 curves.
Both curves specify different hashers.
Clients which support both curves must inspect `crv` header value to assert the difference.
For example, to create a ed25519 web key:
```bash
curl -L 'https://${CUSTOM_DOMAIN}/v2/web_keys' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
-d '{
"ed25519": {}
}'
```
[^2]: The ZITADEL back-end is written in Go. The Go developers have denied ed448 curve implementations to be included.
Therefore ZITADEL won't support this either.
### Activation
When a generated web key is [activated](/apis/resources/webkey_service_v2/zitadel-webkey-v-2-web-key-service-activate-web-key),
its private key will be used to sign new tokens.
There can be only one active key on an instance.
Activating a key implies deactivation of the previously active key.
Public keys on the [JWKS](#json-web-key-set) endpoint may be [cached](#caching).
Therefore it is advised to delay activation after generating a key,
at least for the duration of the max-age setting plus any time it might take for client applications to refresh.
### Deletion
Non-active keys may be [deleted](/apis/resources/webkey_service_v2/zitadel-webkey-v-2-web-key-service-delete-web-key).
Deletion also means tokens signed with this key become invalid.
Active keys can't be deleted.
As each public key is available on the [JWKS](#json-web-key-set) endpoint,
it is important to cleanup old web keys that are no longer needed.
Otherwise the endpoint's response size will only grow over time, which might lead to performance issues.
Once a key was activated and deactivated (by activation of the next key) deletion should wait:
- Until access and ID tokens are expired. See [OIDC token lifetimes](/guides/manage/console/default-settings#oidc-token-lifetimes-and-expiration).
- ID tokens may be used as `id_token_hint` in authentication and end-session requests. The hint typically doesn't expire, but becomes invalid once the key is deleted.
It might be desirable to keep keys around long enough to minimize user impact.
### Rotation example
This section gives an example on a key rotation strategy.
This strategy aims to fulfill the following requirements:
1. Web keys are rotated monthly.
2. Applications have enough time to see the next activated web key on the [JWKS](#json-web-key-set) endpoint.
3. Web keys are kept long enough to cover the access and ID token validity of 24 hours.
4. Web keys are kept long enough to to allow usage of the `id_token_hint` for at least 3 months.
Users that haven't logged in / refreshed tokens with the client app for that period,
will need to re-enter their username.
When the instance was created, resp. the feature was rolled out, the instance got two keys with the first one activated. When this feature becomes generally available, instance creation will setup the first two keys in the same way. So the initial state always looks like this:
| id | created | changed | state |
| --- | ---------- | ---------- | --------------- |
| 1 | 2025-01-01 | 2025-01-01 | `STATE_ACTIVE` |
| 2 | 2025-01-01 | 2025-01-01 | `STATE_INITIAL` |
For the sake of this example we will use simplified IDs and restrict timestamps to dates.
After one month, on 2025-02-01, we wish to activate the next available key and create a new key to be available for activation next month. This fulfills requirements 1 and 2.
```bash
curl -L -X POST 'https://${CUSTOM_DOMAIN}/v2/web_keys/2/_activate' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>'
curl -L 'https://${CUSTOM_DOMAIN}/v2/web_keys' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>' \
-d '{}'
```
Key ID 2 became active, Key ID 1 became inactive and a new key with ID 3 was created:
| id | created | changed | state |
| --- | ---------- | ---------- | ---------------- |
| 1 | 2025-01-01 | 2025-02-01 | `STATE_INACTIVE` |
| 2 | 2025-01-01 | 2025-02-01 | `STATE_ACTIVE` |
| 3 | 2025-02-01 | 2025-02-01 | `STATE_INITIAL` |
No keys are deleted yet.
We continue like this monthly.
At one point (on 2025-05-01) we will have a web key with `STATE_INACTIVE` with a changed date of 3 months ago:
| id | created | changed | state |
| --- | ---------- | ---------- | ---------------- |
| 1 | 2025-01-01 | 2025-02-01 | `STATE_INACTIVE` |
| 2 | 2025-01-01 | 2025-03-01 | `STATE_INACTIVE` |
| 3 | 2025-02-01 | 2025-04-01 | `STATE_INACTIVE` |
| 4 | 2025-03-01 | 2025-05-01 | `STATE_INACTIVE` |
| 5 | 2025-04-01 | 2025-05-01 | `STATE_ACTIVE` |
| 6 | 2025-05-01 | 2025-05-01 | `STATE_INITIAL` |
In addition to the activate and create calls we made on this iteration,
we can now safely delete the oldest key, as both requirement 3 and 4 are now fulfilled:
```bash
curl -L -X DELETE 'https://${CUSTOM_DOMAIN}/v2/web_keys/1' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <TOKEN>'
```
The final state:
| id | created | changed | state |
| --- | ---------- | ---------- | ---------------- |
| 2 | 2025-01-01 | 2025-03-01 | `STATE_INACTIVE` |
| 3 | 2025-02-01 | 2025-04-01 | `STATE_INACTIVE` |
| 4 | 2025-03-01 | 2025-05-01 | `STATE_INACTIVE` |
| 5 | 2025-04-01 | 2025-05-01 | `STATE_ACTIVE` |
| 6 | 2025-05-01 | 2025-05-01 | `STATE_INITIAL` |
Next month, Key ID 6 will be activated, an new key added and Key ID 2 can be deleted.
## JSON web key set
The JSON web key set (JWKS) endpoint serves all available public keys for the instance on
`${CUSTOM_DOMAIN}/oauth/v2/keys`. This includes activated, newly non-activated and deactivated web keys. The response format is defined in [RFC7517, section 5: JWK Set Format](https://www.rfc-editor.org/rfc/rfc7517#section-5).
And looks like:
```json
{
"keys": [
{
"use": "sig",
"kty": "RSA",
"kid": "280543383892525058",
"alg": "RS384",
"n": "0pVcbjTEr-awBmvztGLbBJB_-_YwjCKKXURJRpoXrChlaqtAvbkxby7mu9wSKAibxnvaobfuxnQydlB4CoKObUr00ARVBNeP5HLzeQUEx3CZh3s1LsjiuYov_yyvK9D12WH1LikP4ZPS68j-DVoEOEcFAE6cNikXTeDyCKa-ixROALieRXUQXTlvVyA_s0FhevmH0-M6rEN4YcfQuIZACEv2nQ4AJo0sNnugwrrqNn595ONKMSh2XTVngxxAD3TGHXg9bELB-WmgnZamVbO-ObpDBp5Ov73HL60_UoBTzBDECM6ovl52fHusLFw6Vkdt9_W3QhuRFljNqTPnna6rB-bLptQltBpnSBV3TxmklBcQ1EO3qeGvgOJsmDwSRlr28Du_1pyFMFANnG174eX5XrYASqTgJ1Wq7AfMBmv7YwGU7PbMce1V_CAV9u_hNkMJf0xQ4AIqrQ98f9hC5VCdCoKSOH1-1d8icEu7UmDyJohWqvY7xGOM_0Abx8ekMRT2O9PulmQ22me_GI5zXh7iv9yaoNq8EUNP5bdtr-ZG4PG8mqpLDSLpCpobYRK5AynyJkf-7_6neSy-ihu604ADKsNzB-uO58V8MPFdSPncyuUeTPX4dAVajbFyMtoAjtI1k_HYMU8nojRUrLSCJae9b0KtcPm9s7dCIL1Zpa4B-YM",
"e": "AQAB"
},
{
"use": "sig",
"kty": "OKP",
"kid": "280998627474669570",
"crv": "Ed25519",
"alg": "EdDSA",
"x": "B51hFhRUHMHpqO1f-OThtnk3PfnRFaPFJWCLXSM_kuI"
},
{
"use": "sig",
"kty": "EC",
"kid": "282465789963927554",
"crv": "P-256",
"alg": "ES256",
"x": "X5s3tNoIXd5odp_-IwQq5oaAgMSoAxj0hwQ1DgHihmI",
"y": "JqmTlRjoOv5bY5E9tAZXHaUHUamAAAFshO8zLhEZ9ZM"
}
]
}
```
After the `web_key` feature is enabled, the response may still contain legacy keys, in order not to invalidate older sessions.
The legacy keys will disappear once they expire.
### Caching
As web keys can be created and distributed ahead of time, it is safe for JWKS responses to be cached at intermediate proxies.
Once the `web_key` feature is enabled, ZITADEL will send a `Cache-Control` header which allows caching.
By default and in ZITADEL Cloud we allow 5 minutes of caching:
```
Cache-Control: max-age=300, must-revalidate
```
Self-hosters can modify this setting through the `ZITADEL_OIDC_JWKSCACHECONTROLMAXAGE`
environment variable or in the configuration yaml:
```yaml
OIDC:
JWKSCacheControlMaxAge: 5m
```
Setting the value to `0` will result in a `no-store` value in the `Cache-Control` header.
@@ -0,0 +1,279 @@
---
title: Authenticate users with SAML
sidebar_label: SAML
---
SAML stands for Security Assertion Markup Language. It is a standard commonly used for identity federation and single sign-on (SSO). It is one of the original and most popular standards for SSO. Although it is prone to certain security flaws and exploits if not implemented correctly, it remains relevant and widely used.
## Why use SAML?
Here are some reasons why organizations might choose SAML:
**Legacy systems compatibility**
SAML has been in use since 2002 and is deeply integrated into many legacy systems and enterprise environments. Organizations with existing SAML infrastructure may prefer to continue using it to avoid costly and complex migrations.
**Enterprise use cases**
SAML is often favored in enterprise settings where detailed user attributes and complex authorization requirements are necessary. Its support for rich metadata and customizable assertions makes it suitable for intricate access control scenarios.
**Mature ecosystem**
The SAML ecosystem is mature, with extensive support from a wide range of enterprise applications and identity providers. This broad compatibility ensures that SAML can be used seamlessly across various platforms and services.
## Common SAML terms
- **Service Provider (SP)**: The application the user is trying to sign into.
- **Identity Provider (IdP)**: The centralized point of authentication.
- **SAML Request**: A communication from the SP to the IdP.
- **SAML Response**: A communication from the IdP to the SP, containing assertions about the user.
- **Assertions**: Statements within the SAML response about the user, signed using XML signatures.
- **Assertion Consumer Service (ACS)**: The endpoint at the SP responsible for processing the SAML response.
- **Attributes**: User information within the SAML response.
- **Relay State**: A way for the IdP to remember where a user was before authentication.
- **SAML Trust**: The configuration between the identity provider and the service provider
- **Metadata**: Trust information exchanged between the identity provider and service provider
## SAML explained
The **Service Provider (SP)** is the application that the user is trying to sign into. When the service provider sends a communication to the identity provider, it is called a **SAML request**. When the **Identity Provider (IdP)** responds or sends a communication to the service provider, it is called a SAML response.
Within the SAML response, there are multiple statements about the user, known as **assertions**. These assertions are all signed using XML signatures (also known as DSig) and are sent to be processed at the service provider's specific endpoint called an **Assertion Consumer Service (ACS)**. The ACS is responsible for receiving the SAML response from the identity provider, checking the assertions' signatures, and validating the entire document. This is a crucial part of implementing SAML from the service provider's perspective.
Additionally, the SAML response contains other pieces of information about the user, such as their first name, last name, and other profile information, referred to as **attributes**.
Another important concept in SAML is the **relay state**. The relay state allows the identity provider to remember where a user was before authentication. If a user is browsing anonymously through the service provider and triggers authentication, they will be redirected to the identity provider. After validating the user's identity, the identity provider will redirect them back to the service provider's ACS. The relay state makes sure that the user returns to their original location instead of being dropped on a generic home page.
**SAML trust** is the configuration between the identity provider and the service provider, involving shared information such as a signing certificate and an entity ID (also known as an issuer) from the identity provider. This shared information establishes a trust that allows both parties to validate communications, requests, and responses.
**Metadata** is another crucial term in SAML. Metadata allows for self-configuration between the identity provider and the service provider. Instead of manually exchanging certificates, endpoint URLs, and issuer information, metadata enables the sharing of an XML configuration file or URLs to these files. This allows the service provider and identity provider to self-configure based on the information within these configuration files, making the process less manual and more convenient.
## SAML workflow
One important aspect of SAML is that the user can initiate the authentication process in two primary workflows:
**1. IdP-Initiated: The user starts at the IdP, which sends a SAML response to the SP.**
Commonly, with workforce identity providers, there is a centralized user portal where a user can see a list of applications. Clicking on one of these applications initiates the authentication process without the user first going to the service provider. This method is called IdP-initiated (Identity Provider-initiated) authentication. In this flow, the user goes to the identity provider, which automatically kicks off the SAML response, directing them to the desired application.
**2. SP-Initiated: The user starts at the SP, which sends a SAML request to the IdP, followed by a SAML response from the IdP.**
The other method is SP-initiated (Service Provider-initiated) authentication. Here, the user starts at the service provider, which then sends a SAML request to the identity provider. The identity provider processes this request and sends back a SAML response.
In IdP-initiated flows, there is no SAML request, while in SP-initiated flows, the SAML request and response are both involved. The identity provider must understand how to receive a SAML request and create a SAML response. The service provider, particularly its Assertion Consumer Service (ACS), needs to validate the SAML response and potentially generate a SAML request if SP-initiated flow is supported.
It's important to note that not all service providers support both methods. Some only support IdP-initiated, while others only support SP-initiated. The choice between supporting IdP-initiated or SP-initiated authentication depends on the specific requirements of the product and the preferences of the developer implementing SAML. ZITADEL, for instance, supports only the SP-initiated flow.
## SAML requests and responses
SAML uses XML for both requests and responses. A typical SAML request from an SP to an IdP includes an ID, timestamp, destination URL, and issuer information. The IdP processes this request and returns a SAML response containing user assertions, which the SP validates.
Let's delve into what a SAML request and response look like with the following shortened examples.
**Sample SAML request**
```xml
<?xml version="1.0" encoding="utf-8"?>
<ns0:AuthnRequest
xmlns:ns0="urn:oasis:names:tc:SAML:2.0:protocol" ID="id-8LjuzBEUQFYFWjL55" Version="2.0" IssueInstant="2024-06-11T04:13:52Z" Destination="https://my-instance-xtzfbc.zitadel.cloud/saml/v2/SSO" AssertionConsumerServiceURL="http://127.0.0.1:5000/acs">
<ns1:Issuer
xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://zitadel-test.sp/metadata
</ns1:Issuer>
<ns2:Signature
xmlns:ns2="http://www.w3.org/2000/09/xmldsig#">
<ns2:SignedInfo>
<ns2:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ns2:CanonicalizationMethod>
<ns2:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ns2:SignatureMethod>
<ns2:Reference URI="#id-8LjuzBEUQFYFWjL55">
<ns2:Transforms>
<ns2:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ns2:Transform>
<ns2:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ns2:Transform>
</ns2:Transforms>
<ns2:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ns2:DigestMethod>
<ns2:DigestValue>WSz/EQ72RZ0DTh3DBSRCElpITqM=</ns2:DigestValue>
</ns2:Reference>
</ns2:SignedInfo>
<ns2:SignatureValue>HHjsNh0OLj7...</ns2:SignatureValue>
<ns2:KeyInfo>
<ns2:X509Data>
<ns2:X509Certificate>MIIDtzCCAp+gAwIBAgIUfITRQGue...</ns2:X509Certificate>
</ns2:X509Data>
</ns2:KeyInfo>
</ns2:Signature>
</ns0:AuthnRequest>
```
In this SAML request:
- The **ID** (id-8LjuzBEUQFYFWjL55) is generated by the service provider, and the identity provider will respond to this ID in the SAML response.
- **IssueInstant** (2024-05-11T04:13:52Z) is the timestamp for this request, used by the identity provider for verification to ensure the request is within an acceptable time frame.
- **Destination** (https://my-instance-xtzfbc.zitadel.cloud/saml/v2/SSO) points to the identity provider's URL, ensuring the request is sent to the correct recipient.
- **AssertionConsumerServiceURL** (http://127.0.0.1:5000/acs) indicates where the response should be sent after user authentication.
- **Issuer** (https://zitadel-test.sp/metadata) is a predefined string formatted as a URL, matching what the identity provider expects.
- **Signature** ensures the integrity and authenticity of the SAML request. It includes:
- **SignedInfo** contains details about the canonicalization and signature methods.
- **Reference** points to the signed data and includes a transform and digest method.
- **SignatureValue** provides the actual digital signature.
- **KeyInfo** containing the X.509 certificate, which holds the public key used to verify the signature.
These XMLs are stringified, encoded, and transmitted according to the SAML binding used. For instance, in HTTP Redirect Binding, the request is sent as URL parameters, while in HTTP POST Binding, it is included in the request body. The signature's transmission method also depends on the binding used, ensuring the integrity and authenticity of the SAML message across different communication channels.
**Sample SAML response**
```xml
<?xml version="1.0" encoding="utf-8"?>
<Response
xmlns="urn:oasis:names:tc:SAML:2.0:protocol" ID="_164ba12b-6711-40e0-8ddb-55aa810f1c92" InResponseTo="id-8LjuzBEUQFYFWjL55" Version="2.0" IssueInstant="2024-06-11T04:17:41Z" Destination="http://127.0.0.1:5000/acs">
<Issuer
xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://my-instance-xtzfbc.zitadel.cloud/saml/v2/metadata
</Issuer>
<Status>
<StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"></StatusCode>
</Status>
<Assertion
xmlns="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_6fbdb616-b77f-46af-9554-989c8b89eeda" IssueInstant="2024-06-11T04:17:41Z">
<Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://my-instance-xtzfbc.zitadel.cloud/saml/v2/metadata</Issuer>
<Signature
xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></SignatureMethod>
<Reference URI="#_6fbdb616-b77f-46af-9554-989c8b89eeda">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></Transform>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></DigestMethod>
<DigestValue>/b1R9LJJSeNX...</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>n5GbV4xhkXV...</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIFITCCAwmgAwIBAgIBUTANBgkqh...</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
<Subject>
<NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">[email protected]</NameID>
<SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<SubjectConfirmationData NotOnOrAfter="2024-06-11T04:22:41Z" Recipient="http://127.0.0.1:5000/acs" InResponseTo="id-8LjuzBEUQFYFWjL55"></SubjectConfirmationData>
</SubjectConfirmation>
</Subject>
<Conditions NotBefore="2024-06-11T04:17:41Z" NotOnOrAfter="2024-06-11T04:22:41Z">
<AudienceRestriction>
<Audience>https://zitadel-test.sp/metadata</Audience>
</AudienceRestriction>
</Conditions>
<AuthnStatement AuthnInstant="2024-06-11T04:17:41Z" SessionIndex="_6fbdb616-b77f-46af-9554-989c8b89eeda">
<AuthnContext>
<AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</AuthnContextClassRef>
</AuthnContext>
</AuthnStatement>
<AttributeStatement>
<Attribute Name="Email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<AttributeValue>[email protected]</AttributeValue>
</Attribute>
<Attribute Name="SurName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<AttributeValue>Stark</AttributeValue>
</Attribute>
<Attribute Name="FirstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<AttributeValue>Tony</AttributeValue>
</Attribute>
<Attribute Name="FullName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<AttributeValue>Tony Stark</AttributeValue>
</Attribute>
<Attribute Name="UserName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<AttributeValue>[email protected]</AttributeValue>
</Attribute>
<Attribute Name="UserID" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">
<AttributeValue>260242264868201995</AttributeValue>
</Attribute>
</AttributeStatement>
</Assertion>
</Response>
```
In this SAML response:
- The **ID** (_164ba12b-6711-40e0-8ddb-55aa810f1c92) matches the ID from the SAML request (id-8LjuzBEUQFYFWjL55), allowing the service provider to verify it.
- **InResponseTo** references the ID of the SAML request.
- **IssueInstant** (2024-06-11T04:17:41Z) is the timestamp of the response.
- **Destination** (http://127.0.0.1:5000/acs) confirms that the response is intended for the service provider's Assertion Consumer Service URL.
- **Issuer** (https://my-instance-xtzfbc.zitadel.cloud/saml/v2/metadata) is the identity provider's unique string, used to verify the response's origin.
- **StatusCode** indicates the status of the authentication process, with a value of "Success." If the status is not "Success," it means there was a problem with the login, such as incorrect credentials or other authentication issues.
- **Signature** ensures the integrity and authenticity of the SAML response. It includes:
- **SignedInfo** contains details about the canonicalization and signature methods.
- **Reference** points to the signed data and including transforms and a digest method.
- **SignatureValue** provides the actual digital signature.
- **KeyInfo** contains the X.509 certificate, which holds the public key used to verify the signature.
- **Assertion** includes user information, such as email, surname, first name, full name, username, user ID etc.
- **Subject** contains the user's unique identifier.
- **Conditions** specify the response's validity window.
- **AuthnStatement** includes authentication details.
- **AttributeStatement** contains additional user attributes.
## SAML identity brokering
### How SAML identity brokering works
- **Initial Authentication Request**: A user attempts to access a service (SP1) that is protected by an IdP (IdP1).
- **Redirection to IdP1**: The user is redirected to IdP1 for authentication. If IdP1 trusts another IdP (IdP2) for authentication, it will redirect the user to IdP2.
- **Authentication at IdP2**: The user authenticates with IdP2, which generates a SAML assertion containing the user's identity and attributes.
- **Assertion Processing**: ZITADEL (IdP1) processes the SAML assertion from IdP2. ZITADEL then creates an independent SAML assertion based on the information received and its own policies.
- **Response to SP1**: ZITADEL (IdP1) sends this newly created SAML assertion to SP1, completing the authentication process.
- **Access Granted to SP1**:IdP1 then sends a final SAML assertion to SP1, which grants the user access to the requested service.
See [Let Users Login with Preferred Identity Provider](https://zitadel.com/docs/guides/integrate/identity-providers/introduction) for more information.
## Best practices for SAML implementation
Implementing SAML securely involves several best practices:
- Limit XML Parser Features: Disable unnecessary features to prevent XML external entity (XXE) attacks.
- Use Canonicalized XML: Normalize XML to prevent manipulation.
- Validate XML Schema: Ensure only expected XML formats are accepted.
- Validate Signatures: Check all signatures in the SAML response.
- Use SSL Encryption: Protect against interception.
- Validate Parties: Ensure the destination, audience, recipient, and issuer information is correct.
- Enforce Validation Window: Accept responses only within a valid time frame.
- Use Historical Cache: Track and reject duplicate IDs to prevent replay attacks.
- Minimize Buffer Size: Protect against DDoS attacks.
## Alternatives to SAML
SAML has its flaws; it can be complex and cumbersome to implement. The primary alternatives to consider is OpenID Connect (OIDC). Some illustrate this with the following analogy: “SAML is to OpenID Connect as SOAP is to REST." Just as REST was created to address some inherent flaws in SOAP, OpenID Connect was created to address some of the limitations in specifications like SAML. OpenID Connect is flexible, easy to use, widely adopted, and reliably secure.
For example, SAML is not well-suited for desktop and mobile applications due to its reliance on HTTP redirects, cookie-based session management, and complex certificate handling. OIDC, on the other hand, offers a modern, flexible, and simpler solution for authentication and authorization needs across desktop and mobile applications. It uses authorization codes and tokens that are easier to manage and supports custom URL schemes and deep linking, which simplifies the handling of redirects and improves the overall user experience​. Furthermore, OIDC offers more flexibility in managing sessions without relying solely on cookies.
If a project requires SAML due to specific requirements or existing infrastructure, it should be used. However, for new projects, it is advisable to consider OpenID Connect because it is a more modern standard and is the more popular choice in the industry.
## Testing SAML scenarios using ZITADEL
To test SAML scenarios with ZITADEL, follow these steps:
1. Integrate a SAML SP with ZITADEL as the IdP:
- Sign up for a ZITADEL account if you don't already have one. If you are self-hosting ZITADEL, you can skip this step.
- Create an Organization and a Project in ZITADEL.
- Within your project, create a SAML application.
- Follow this example on how to create a SAML SP and integrate ZITADEL as the SAML IdP: [ZITADEL Python SAML SP Integration](https://github.com/zitadel/python-saml-sp).
2. Integrate ZITADEL with another SAML IdP for identity brokering:
- Configure an identity provider that supports SAML.
- Set up the necessary metadata and endpoints. Here are some guides to help with this setup:
- [Configure Entra ID as a SAML IdP](https://zitadel.com/docs/guides/integrate/identity-providers/azure-ad-saml)
- [Configure Okta as a SAML IdP](https://zitadel.com/docs/guides/integrate/identity-providers/okta-saml)
- [Configure MockSAML as a SAML IdP](https://zitadel.com/docs/guides/integrate/identity-providers/mocksaml)
3. Create test users and simulate authentication requests:
- Create test users in ZITADEL.
- Simulate authentication requests to verify that the SAML assertions are correctly generated and transmitted.
- Ensure that the SAML assertions contain the expected attributes and that these attributes are correctly processed by the service provider.
4. Simulate various scenarios:
- Successful Login: Verify that a valid user can successfully authenticate and access the service.
- Failed Login: Test scenarios where authentication fails, such as incorrect credentials or disabled accounts.
- Attribute Mapping: Check that user attributes (e.g., roles, permissions) are correctly mapped and utilized by the service provider.
- Logout Requests: Test single logout (SLO) to ensure that logging out from one service logs the user out of all connected services.
For more information, refer to [SAML Endpoints in ZITADEL](https://zitadel.com/docs/apis/saml/endpoints).