diff --git a/docs/docs/guides/integrate/identity-providers/jwt_idp.md b/docs/docs/guides/integrate/identity-providers/jwt_idp.md
index 52324ad5c..372b0e464 100644
--- a/docs/docs/guides/integrate/identity-providers/jwt_idp.md
+++ b/docs/docs/guides/integrate/identity-providers/jwt_idp.md
@@ -3,54 +3,160 @@ title: JWT IdP
sidebar_label: JWT IdP
---
+## In simple terms
-## Configure a JWT as an Identity Provider in ZITADEL
+A **JWT Identity Provider (JWT IdP)** allows ZITADEL to accept a JSON Web Token (JWT) issued and signed by an external system as proof that a user has already been authenticated elsewhere. In this flow, ZITADEL does not perform authentication itself, but relies on the trustworthiness and validity of the provided JWT.
-JSON Web Token Identity Provider (JWT IdP) gives you the possibility to use an (existing) JWT as federated identity.
-Imagine you have a Web Application Firewall (WAF) which handles your session for an existing application.
-You're now creating a new application which uses ZITADEL as its IDP / Authentication Server.
-The new app might even be opened from within the existing application and you want to reuse the session information for the new application.
-This is where JWT IdP comes into place.
+## When should I use JWT IdP?
-All you need to provide is an endpoint where ZITADEL can receive a JWT and some information for its signature verification.
+Use JWT IdP if:
+- You have an existing application or Web Application Firewall (WAF) that authenticates users and is able to generate a JWT for them.
+- You want users to access new applications via ZITADEL without re-authentication, effectively reusing an existing session.
+- You want to enable silent single sign-on (SSO) between legacy and new apps.
+- You wish to federate authentication with a system that can't act as a full OpenID Connect provider, but can issue JWTs.
+
+Do **not** use JWT IdP if:
+- You need ZITADEL to interactively authenticate users (e.g., password-based logins).
+- There is no secure way to transfer or validate the JWT.
+
+---
+
+## Configuring a JWT as an Identity Provider in ZITADEL
+
+Configuring JWT IdP enables ZITADEL to accept a JWT generated by an external authentication system, such as your WAF or a legacy app. The typical setup involves obtaining the JWT from the user's previously established session.
+
+To configure JWT IdP in ZITADEL, you need to provide:
+- The endpoint to obtain the JWT.
+- Information ZITADEL requires to validate the JWT, including issuer, signature keys, and which HTTP header carries the JWT.
+
+---
## Authentication using JWT IdP
-The authentication process then might look like the following:
+The authentication flow with JWT IdP looks like this:

-1. The user is logged into the existing application and the WAF holds the session information. It might even send a JWT to the application.
- The new application is opened by clicking on a link in the existing application.
-2. The application bootstraps and since it cannot find a session, it will create an OIDC Authorization Request to ZITADEL.
- In this request it provides a scope to directly request the JWT IdP.
-3. ZITADEL will do so and redirect to the preconfigured JWT Endpoint. While the endpoint is behind the WAF, ZITADEL is able to receive a JWT from a defined HTTP header.
- It will then validate its signature, which might require to call the configured Keys Endpoint.
- If the signature is valid and token is not expired, ZITADEL will then use the token and the enclosed `sub` claim as if it was an id_token returned from an OIDC IDP:
- It will try to match a user's external identity and if not possible, create a new user with the information of the provided token.
- Prerequisite for this is that the IDP setting `autoregister` is set to `true`.
-4. ZITADEL will then redirect to its main instance and the login flow will proceed.
-5. The user will be redirected to the Callback Endpoint of the new Application, where the application will exchange the code for tokens.
- The user is finally logged in the new application, without any direct interaction.
+**Step-by-Step Process**
-### Terms and example values
+1. **User authenticates in the existing app (Server-side & Browser):**
+ - The user logs in to the current application, with authentication managed (often) by a WAF or legacy system.
+ - The WAF or app creates a session and can issue a JWT representing that session.
-To further explain and illustrate how a JWT IdP works, we will assume the following:
+2. **User accesses new app (Browser):**
+ - The user visits the new application, possibly via a link from the existing app.
+ - If no session is found, the new app initiates an OIDC login via ZITADEL.
+ - The user either selects the JWT IdP in the ZITADEL UI,
+ - **or** the application uses a [custom scope](/docs/apis/openidoauth/scopes#reserved-scopes) to pre-select the JWT IdP in the OIDC Authorization Request.
-- the **Existing Application** is deployed under `apps.test.com/existing/`
-- the **New Application** is deployed under `new.test.com`
-- the **Login UI of ZITADEL** is deployed under `accounts.test.com`
+3. **ZITADEL redirects to the JWT Endpoint (Browser):**
+ - ZITADEL redirects the user's browser to the configured JWT Endpoint (usually behind the WAF).
+
+ - **Purpose:**
+ Redirecting the browser ensures that any existing session cookies for the previous app/WAF are available, letting the WAF determine the authenticated user and issue the appropriate JWT.
-The **JWT IdP Configuration** might then be:
- - **JWT Endpoint** (Endpoint where ZITADEL will redirect to):
`https://apps.test.com/existing/auth-new`
- - **Issuer** (of the JWT):
`https://issuer.test.internal`
- - **Keys Endpoint** (where keys of the JWT Signature can be gathered):
`https://issuer.test.internal/keys`
- - **Header Name** (of the JWT, Authorization if omitted):
`x-custom-tkn`
+4. **WAF/session gateway attaches JWT and proxies request to ZITADEL (Server-side):**
+ - The JWT Endpoint (behind the WAF) uses the session (from browser cookies) to ascertain the authenticated user.
+ - The WAF/server injects the JWT into a specific HTTP header (never as a URL parameter) and forwards (proxies) the user’s request with **all original query parameters intact** to ZITADEL’s JWT receiving endpoint.
+ - This preserves critical OIDC flow state.
-Therefore, if the user is redirected from ZITADEL to the JWT Endpoint on the WAF (`https://apps.test.com/existing/auth-new`),
-the session cookies previously issued by the WAF, will be sent along by the browser due to the path being on the same domain as the exiting application.
-The WAF will reuse the session and send the JWT in the HTTP header `x-custom-tkn` to its upstream, the ZITADEL JWT Endpoint (`https://accounts.test.com/ipds/jwt`).
+ - **How to proxy the request (example):**
+ The example below shows a simplified "serverless/WAF proxy" approach to:
+ - obtain/generate the JWT,
+ - forward all the user's query params,
+ - and place the JWT in the appropriate HTTP header when proxying to ZITADEL.
-For the signature validation, ZITADEL must be able to connect to Keys Endpoint (`https://issuer.test.internal/keys`)
-and it will check if the token was signed (claim `iss`) by the defined Issuer (`https://issuer.test.internal`).
+ ```js
+ export default {
+ async fetch(request, env, ctx) {
+ // 1. Obtain the JWT for the current session (implementation will vary)
+ const jwt = await getJwtForCurrentSession(request, env);
+
+ // 2. Prepare the ZITADEL endpoint URL and copy all query params
+ const userUrl = new URL(request.url);
+ // Important: The ZITADEL_JWT_IDP_ENDPOINT should be your ZITADEL custom domain plus "/idps/jwt"
+ // For example: https://accounts.test.com/idps/jwt
+ const zitadelUrl = new URL(env.ZITADEL_JWT_IDP_ENDPOINT);
+ userUrl.searchParams.forEach((v, k) => zitadelUrl.searchParams.set(k, v));
+
+ // 3. Proxy request, attaching JWT in the configured HTTP header
+ const zitadelReq = new Request(zitadelUrl.toString(), {
+ method: "GET",
+ headers: {
+ "x-custom-tkn": jwt, // Use header name as set in ZITADEL JWT IdP settings
+ "Accept": request.headers.get("Accept") || "*/*",
+ },
+ redirect: "manual",
+ });
+
+ // 4. Send to ZITADEL and relay its response to the browser
+ const zitadelResp = await fetch(zitadelReq);
+ return new Response(zitadelResp.body, {
+ status: zitadelResp.status,
+ headers: zitadelResp.headers,
+ });
+ }
+ }
+ ```
+ *(Replace `getJwtForCurrentSession` with your logic for retrieving/creating a JWT from the user's WAF session.
+ Note: `env.ZITADEL_JWT_IDP_ENDPOINT` should be set to the custom domain of your ZITADEL instance
+ with the `/idps/jwt` path, e.g. `https://accounts.test.com/idps/jwt`.)*
+
+5. **ZITADEL receives and validates the JWT (Server-side):**
+ - ZITADEL extracts the JWT from the configured HTTP header.
+ - ZITADEL verifies that:
+ - The **signature** matches using keys from the Keys Endpoint.
+ - The `issuer` (`iss` claim) matches the configured issuer.
+ - The JWT is **not expired**.
+ - **Note:**
+ - ZITADEL **does not** re-authenticate the user.
+ - ZITADEL **does not** issue this JWT.
+ - ZITADEL only verifies authenticity and validity.
+ - The JWT is treated as an external ID token, similar to third-party IdPs.
+
+6. **Login completes (Browser):**
+ - ZITADEL finishes the OIDC flow.
+ - The browser is redirected to the application’s callback endpoint.
+ - The app exchanges the code for tokens, and the user is now logged in—without extra authentication steps.
+
+---
+
+## Terms and Example Values
+
+Here’s a reference integration scenario:
+
+- **Existing Application:** `apps.test.com/existing/`
+- **New Application:** `new.test.com`
+- **ZITADEL Login UI:** `accounts.test.com`
+
+Sample **JWT IdP Configuration**:
+
+- **JWT Endpoint:**
+ Where ZITADEL redirects users to retrieve the JWT:
+ `https://apps.test.com/existing/auth-new`
+
+- **Issuer:**
+ The `iss` claim ZITADEL expects in the JWT:
+ `https://issuer.test.internal`
+
+- **Keys Endpoint:**
+ Where ZITADEL fetches public keys for signature verification:
+ `https://issuer.test.internal/keys`
+
+- **Header Name:**
+ The HTTP header delivering the JWT (defaults to `Authorization` if not set):
+ `x-custom-tkn`
+
+**Clarifications and Best Practices:**
+- **Why must the JWT Endpoint be on the same domain as the existing app?**
+ This ensures browser sessions (cookies) are correctly sent and recognized by the WAF or app, so the right JWT can be generated.
+- **Why are cookies sent automatically?**
+ Browsers automatically include relevant cookies when redirecting within the same domain, enabling server-side authentication seamlessly.
+- **Why send the JWT in a header, not as a parameter?**
+ HTTP headers are more secure for transmitting sensitive tokens, prevent them from being exposed in URLs or logs, and avoid user tampering.
+
+When ZITADEL performs signature validation, it uses the Keys Endpoint to:
+- Verify the JWT’s signature
+- Check the issuer matches the JWT IdP configuration
+- Ensure the JWT is not expired