feat: oidc login

This commit is contained in:
Alex committed 2026-06-09 23:24:44 +01:00
1 parent 3ef973409e
commit 8bc777a428
25 files changed
+1891 -10

No files matched your search

@@ -230,6 +230,7 @@ The `AUTH_TYPE` setting in your `.env` file or `settings.py` determines the auth
| `None` | No authentication is used. Anyone can access the app. |
| `simple_jwt` | A single, long-lived JWT token is generated at startup. All requests use this shared token. |
| `session_jwt` | Unique JWT tokens are generated for each session/user. |
| `oidc` | Users sign in through an external OpenID Connect provider (Authentik, Keycloak, Okta, ...). See [SSO with OIDC](/Deploying/OIDC-SSO). |
#### How to Configure
@@ -246,6 +247,13 @@ JWT_SECRET_KEY=your_secret_key_here
# OR: Session JWT (per-user/session tokens)
AUTH_TYPE=session_jwt
JWT_SECRET_KEY=your_secret_key_here
# OR: SSO via an OpenID Connect provider (Authentik, Keycloak, Okta, ...)
AUTH_TYPE=oidc
OIDC_ISSUER=https://auth.example.com/application/o/docsgpt/
OIDC_CLIENT_ID=your_client_id
OIDC_FRONTEND_URL=https://docsgpt.example.com
JWT_SECRET_KEY=your_secret_key_here
```
- If `AUTH_TYPE` is set to `simple_jwt` or `session_jwt`, a `JWT_SECRET_KEY` is required.
@@ -265,6 +273,10 @@ JWT_SECRET_KEY=your_secret_key_here
- Clients can request a new token from `/api/generate_token`.
- Use the received token in the `Authorization` header for subsequent requests.
- Each user/session gets a unique token.
- **oidc**:
- The frontend redirects users to your identity provider to sign in (OAuth2 Authorization Code + PKCE).
- After a successful sign-in, DocsGPT issues its own session JWT; API requests carry it in the `Authorization` header like the other modes.
- Stable per-user identities come from the provider — see the full setup guide: [SSO with OIDC](/Deploying/OIDC-SSO).
#### Security Notes
+83
View File
@@ -0,0 +1,83 @@
---
title: SSO with OIDC
description: Sign users into DocsGPT through any OpenID Connect identity provider — Authentik, Keycloak, Okta, and others.
---
# SSO with OIDC
Setting `AUTH_TYPE=oidc` makes DocsGPT delegate sign-in to an external OpenID Connect identity provider (IdP). Any spec-compliant IdP with a discovery document works; this guide uses [Authentik](https://goauthentik.io/) as the reference provider and includes a short note for Keycloak.
## How the flow works
1. A user opens DocsGPT without a session. The frontend redirects the browser to `GET /api/auth/oidc/login` on the DocsGPT API.
2. The backend starts an **OAuth2 Authorization Code + PKCE** flow and redirects to your IdP's sign-in page.
3. After sign-in, the IdP redirects back to `GET /api/auth/oidc/callback`. The backend exchanges the code server-side, validates the ID token (signature via JWKS, issuer, audience, expiry, nonce), and mints a **DocsGPT session JWT** signed with `JWT_SECRET_KEY`.
4. The browser returns to your frontend with a short-lived single-use code in the URL fragment; the frontend exchanges it for the session JWT and stores it. From here on, requests are authenticated exactly like the other `AUTH_TYPE` modes (`Authorization: Bearer <token>`).
5. Signing out clears the local session and redirects through the IdP's end-session endpoint.
The user's identity (`sub` claim by default) becomes the DocsGPT `user_id`, so every user gets their own conversations, sources, agents, and settings.
> Redis must be reachable by the API (it stores the short-lived login state and handoff codes). Redis is already a required DocsGPT dependency, so no extra infrastructure is needed.
## Settings reference
| Setting | Required | Default | Description |
| --- | --- | --- | --- |
| `AUTH_TYPE` | yes | — | Set to `oidc`. |
| `OIDC_ISSUER` | yes | — | Issuer URL of your IdP. Discovery is read from `<issuer>/.well-known/openid-configuration`. |
| `OIDC_CLIENT_ID` | yes | — | Client ID registered at the IdP. |
| `OIDC_FRONTEND_URL` | yes | — | Browser-facing URL of the DocsGPT frontend (where users land after login/logout), e.g. `https://docsgpt.example.com`. |
| `OIDC_CLIENT_SECRET` | no | — | Set when the IdP client is *confidential*. PKCE is always used, so *public* clients work without a secret. |
| `OIDC_SCOPES` | no | `openid profile email` | Scopes requested at the IdP. |
| `OIDC_USER_ID_CLAIM` | no | `sub` | ID-token claim used as the DocsGPT user id. Set to `email` or `preferred_username` for human-readable ids. |
| `OIDC_REDIRECT_URI` | no | derived | Full callback URL registered at the IdP. Defaults to `<request host>/api/auth/oidc/callback`; set it explicitly when the API runs behind a reverse proxy. |
| `OIDC_SESSION_LIFETIME_SECONDS` | no | `28800` (8h) | Lifetime of the DocsGPT session JWT. After expiry the user is silently redirected through the IdP again. |
| `JWT_SECRET_KEY` | recommended | auto-generated | Signs DocsGPT session tokens. Set it explicitly in production — required when running multiple API replicas. |
## Setting up with Authentik
1. **Create a provider.** In the Authentik admin UI go to **Applications → Providers → Create** and pick **OAuth2/OpenID Provider**:
- **Authorization flow**: your preferred flow (e.g. *explicit consent*).
- **Client type**: `Public` (no secret, PKCE only) or `Confidential` (also set `OIDC_CLIENT_SECRET` in DocsGPT).
- **Redirect URIs**: `https://<your-docsgpt-api>/api/auth/oidc/callback`
- **Signing key**: select a certificate so ID tokens are RS256-signed.
2. **Create an application** (Applications → Applications → Create), link it to the provider, and note its **slug**.
3. **Find the issuer.** With Authentik's default per-provider issuer mode it is:
```
https://<your-authentik-host>/application/o/<application-slug>/
```
(the trailing slash is part of the issuer — copy it exactly; the discovery document lives at `.../<application-slug>/.well-known/openid-configuration`).
4. **Configure DocsGPT** in `.env` and restart:
```env
AUTH_TYPE=oidc
OIDC_ISSUER=https://auth.example.com/application/o/docsgpt/
OIDC_CLIENT_ID=<client id from step 1>
# OIDC_CLIENT_SECRET=<only for Confidential client type>
OIDC_FRONTEND_URL=https://docsgpt.example.com
JWT_SECRET_KEY=<long random string>
```
### Which claim becomes the user id?
Authentik's provider setting **Subject mode** controls what lands in the `sub` claim (the default is a hashed user ID — stable but opaque). If you'd rather key DocsGPT users on something readable, either change Subject mode (e.g. *based on username*) or leave Authentik alone and set `OIDC_USER_ID_CLAIM=email` in DocsGPT. Pick one strategy before going live: changing it later gives existing users fresh, empty accounts.
## Keycloak (and other IdPs)
Any OIDC provider with discovery works the same way. For Keycloak:
```env
OIDC_ISSUER=https://keycloak.example.com/realms/<realm>
OIDC_CLIENT_ID=<client id>
```
Create the client with *Standard flow* enabled and PKCE method `S256`; register the same `/api/auth/oidc/callback` redirect URI.
## Troubleshooting
- **Redirected back with `oidc_error=auth_failed`** — check the API logs. The most common causes:
- *Issuer mismatch*: `OIDC_ISSUER` must be the URL the discovery document itself reports as `issuer` (for Authentik this includes the application slug and trailing slash).
- *Clock skew*: ID-token validation allows 60 seconds of skew; sync clocks if the API host drifts more than that.
- **`oidc_error=missing_claim`** — the ID token doesn't contain `OIDC_USER_ID_CLAIM`. Make sure the matching scope is requested (`OIDC_SCOPES`) and the IdP actually emits the claim, or switch the setting back to `sub`.
- **IdP shows a redirect URI error** — the callback URL registered at the IdP must match exactly. Behind a reverse proxy, set `OIDC_REDIRECT_URI` to the public callback URL instead of relying on the derived default.
- **Revoked users can still access DocsGPT** — DocsGPT sessions outlive IdP revocation for up to `OIDC_SESSION_LIFETIME_SECONDS`. Lower it if you need tighter revocation latency.
- **Login endpoints return 503** — Redis is unreachable or the IdP discovery document can't be fetched from the API host.
+4
View File
@@ -3,6 +3,10 @@ export default {
"title": "⚙️ App Configuration",
"href": "/Deploying/DocsGPT-Settings"
},
"OIDC-SSO": {
"title": "🔐 SSO with OIDC",
"href": "/Deploying/OIDC-SSO"
},
"Docker-Deploying": {
"title": "🛳️ Docker Setup",
"href": "/Deploying/Docker-Deploying"