mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
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:
co-authored by
Federico Coppede
parent
7b6395c348
commit
d8ea041c56
@@ -0,0 +1,161 @@
|
||||
---
|
||||
title: JWT IdP
|
||||
sidebar_label: JWT IdP
|
||||
---
|
||||
|
||||
## In simple terms
|
||||
|
||||
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.
|
||||
|
||||
## When should I use JWT IdP?
|
||||
|
||||
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 flow with JWT IdP looks like this:
|
||||
|
||||

|
||||
|
||||
**Step-by-Step Process**
|
||||
|
||||
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.
|
||||
|
||||
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](/apis/openidoauth/scopes#reserved-scopes) to pre-select the JWT IdP in the OIDC Authorization Request.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
- **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.
|
||||
|
||||
```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
|
||||
Reference in New Issue
Block a user