Skip to Content
Deploy & Operate🔐 SSO with OIDC

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  as the reference provider and includes setup notes for Keycloak and Microsoft Entra ID.

Beyond basic sign-in, this page covers the optional access controls: group allowlists, silent session renewal, back-channel logout, SCIM user provisioning, and login auditing.

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.

Sessions last OIDC_SESSION_LIFETIME_SECONDS (8 hours by default) and renew without interrupting the user — see Silent session renewal.

Redis must be reachable by the API — it stores the short-lived login state, handoff codes, server-side refresh tokens, and the session revocation denylist. Redis is already a required DocsGPT dependency, so no extra infrastructure is needed.

IdP compatibility notes

  • Token-endpoint authentication follows the IdP’s discovery document (token_endpoint_auth_methods_supported): client_secret_post when the IdP advertises it, otherwise HTTP Basic (the RFC default). Okta’s default web-app configuration works without extra toggles.
  • Userinfo fallback: when the ID token lacks the user-id claim (OIDC_USER_ID_CLAIM) — or the groups claim while a group allowlist is configured — the backend fetches the IdP’s userinfo endpoint and merges the missing claims. ID-token values win on conflict, and the userinfo sub must match the ID token’s.

Settings reference

SettingRequiredDefaultDescription
AUTH_TYPEyes—Set to oidc.
OIDC_ISSUERyes—Issuer URL of your IdP. Discovery is read from <issuer>/.well-known/openid-configuration.
OIDC_CLIENT_IDyes—Client ID registered at the IdP.
OIDC_FRONTEND_URLyes—Browser-facing URL of the DocsGPT frontend (where users land after login/logout), e.g. https://docsgpt.example.com.
OIDC_CLIENT_SECRETno—Set when the IdP client is confidential. PKCE is always used, so public clients work without a secret.
OIDC_SCOPESnoopenid profile emailScopes requested at the IdP. Add offline_access when your IdP requires it for refresh tokens (Authentik does).
OIDC_USER_ID_CLAIMnosubID-token claim used as the DocsGPT user id. Set to email or preferred_username for human-readable ids; use email when provisioning over SCIM. With Microsoft Entra ID, use oid — see Choose the Entra user id claim.
OIDC_REDIRECT_URInoderivedFull 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_SECONDSno28800 (8h)Lifetime of the DocsGPT session JWT. Sessions renew before expiry — see Silent session renewal.
OIDC_PROVIDER_NAMEno—Display name on the sign-in button: Acme SSO renders “Sign in with Acme SSO”. Unset, the button shows a generic “SSO”.
OIDC_ALLOWED_GROUPSno—Comma-separated group allowlist. Unset, any authenticated IdP user may sign in — see Restricting sign-in by group.
OIDC_ADMIN_GROUPSno—Comma-separated groups whose members are granted the global admin role. Re-checked at every login and renewal — see Granting admin via groups.
OIDC_GROUPS_CLAIMnogroupsID-token/userinfo claim carrying the user’s group membership.
JWT_SECRET_KEYrequired in productionauto-generated for local developmentSigns DocsGPT session tokens and agent-avatar capabilities. Every API and worker replica must use the same value.

SCIM_ENABLED and SCIM_TOKEN are listed in the SCIM section.

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:
    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>

Planning to use silent session renewal? Authentik only issues refresh tokens when the offline_access scope is requested — set OIDC_SCOPES=openid profile email offline_access.

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. If you plan to provision users over SCIM, use OIDC_USER_ID_CLAIM=email — SCIM matches users by userName, which IdPs typically send as the email.

Keycloak (and other IdPs)

Any OIDC provider with discovery works the same way. For Keycloak:

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.

The feature sections below carry their own per-IdP notes — group claims, refresh tokens, back-channel logout, and SCIM each need one IdP-side setting.

Microsoft Entra ID

Microsoft Entra ID (formerly Azure Active Directory) signs users in through the same flow. Entra differs from the other IdPs on this page in a few ways: the issuer must be tenant-specific, a client secret is required, and group membership arrives as object IDs rather than names. This section covers the Entra-side setup end to end.

Register the app in Entra

  1. In the Microsoft Entra admin center , go to Entra ID → App registrations → New registration:

    • Supported account types: Accounts in this organizational directory only (single tenant).
    • Redirect URI: platform Web, value https://<your-docsgpt-api>/api/auth/oidc/callback.

    Don’t pick the Single-page application platform. DocsGPT redeems the authorization code on the server, and Entra refuses server-side redemption for single-page application redirect URIs.

  2. On the app’s Overview page, copy the Application (client) ID and the Directory (tenant) ID.

  3. Under Certificates & secrets → Client secrets → New client secret, create a secret and copy its Value (not the Secret ID). Entra requires a secret for Web redirect URIs, so OIDC_CLIENT_SECRET is mandatory with Entra. Secrets expire, at most 24 months after creation — rotate the secret before its expiry date or sign-in stops working.

  4. Under Authentication, add a second Web redirect URI equal to OIDC_FRONTEND_URL, without a trailing slash (for example https://docsgpt.example.com). Entra only honors a sign-out redirect that matches a registered redirect URI; without it, signing out leaves users on Microsoft’s “You signed out” page instead of returning them to DocsGPT. Leave Front-channel logout URL empty, since DocsGPT doesn’t implement front-channel logout.

  5. Under API permissions → Add a permission → Microsoft Graph → Delegated permissions, add the OpenID permissions openid, profile, email and offline_access, then select Grant admin consent so users don’t see a consent prompt on first sign-in. The default User.Read permission can stay.

Configure DocsGPT for Entra

AUTH_TYPE=oidc OIDC_ISSUER=https://login.microsoftonline.com/<tenant-id>/v2.0 OIDC_CLIENT_ID=<application (client) id> OIDC_CLIENT_SECRET=<client secret value> OIDC_FRONTEND_URL=https://docsgpt.example.com OIDC_SCOPES=openid profile email offline_access OIDC_USER_ID_CLAIM=oid OIDC_PROVIDER_NAME=Microsoft JWT_SECRET_KEY=<long random string>
  • OIDC_ISSUER must be the tenant-specific v2.0 authority. The multi-tenant common and organizations authorities publish an issuer containing a literal {tenantid} placeholder, so every ID token fails issuer validation and sign-in ends in auth_failed. Signing in users from several tenants with one DocsGPT deployment isn’t supported. National clouds use their own host, for example login.microsoftonline.us for Azure Government.
  • offline_access in OIDC_SCOPES makes Entra issue a refresh token, which enables silent session renewal.
  • OIDC_PROVIDER_NAME only sets the sign-in button label (“Sign in with Microsoft”).

Choose the Entra user id claim

Use OIDC_USER_ID_CLAIM=oid. The oid claim is the user’s object ID in your tenant: immutable, never reused, and the same value Entra provisioning can send as the SCIM userName (see Provision Entra users over SCIM). Entra only includes it when the profile scope is requested.

The default sub claim also works, but Entra issues a different sub for each app registration. If you ever replace the app registration, every user gets a new sub and therefore a fresh, empty DocsGPT account. Avoid email and preferred_username (the user principal name): both change when a user is renamed and can be reassigned to someone else later, and Entra doesn’t guarantee email is present. Pick the claim before going live; changing it later gives existing users new, empty accounts.

Restrict access with app roles or groups

By default every user in the tenant can sign in. Entra offers three ways to narrow that down, and they combine.

Require assignment in Entra. Under Entra ID → Enterprise apps → your app → Properties, set Assignment required? to Yes, then add the allowed users or groups under Users and groups. Unassigned users are stopped on the Microsoft sign-in page and never reach DocsGPT, so no DocsGPT setting is involved.

App roles (recommended for the allowlist and admin mapping). App roles arrive in the ID token’s roles claim as the readable values you define, and they aren’t subject to the group-count limit described below.

  1. In the app registration, open App roles → Create app role and create one role per access level, with Allowed member types set to Users/Groups — for example the values DocsGPT.User and DocsGPT.Admin.
  2. Under Enterprise apps → your app → Users and groups → Add user/group, assign users or groups to those roles. Assigning a group to a role requires Microsoft Entra ID P1 or P2; assigning individual users works on every tier. Group assignment reaches direct members only: someone who belongs to the assigned group only through a nested group gets no role. Assign such users directly, or assign a group they’re a direct member of.
  3. Point DocsGPT at the roles claim:
    OIDC_GROUPS_CLAIM=roles OIDC_ALLOWED_GROUPS=DocsGPT.User,DocsGPT.Admin OIDC_ADMIN_GROUPS=DocsGPT.Admin

Security groups. In the app registration, open Token configuration → Add groups claim and choose Security groups or Groups assigned to the application. Entra emits group object IDs, not display names, so list the IDs shown on each group’s Overview page:

OIDC_ALLOWED_GROUPS=<object id of the users group> OIDC_ADMIN_GROUPS=<object id of the admins group>

When a user belongs to more than 200 groups, Entra leaves the groups claim out of the ID token and points to Microsoft Graph instead. DocsGPT doesn’t query Graph, and Entra’s userinfo endpoint doesn’t return groups, so with OIDC_ALLOWED_GROUPS set that user is denied with not_authorized; without an allowlist they sign in, but OIDC_ADMIN_GROUPS leaves their admin role unchanged. Groups assigned to the application only emits the groups assigned to the enterprise app, which keeps the claim short (assigning groups needs Entra ID P1 or P2). It only counts direct membership, though: a user who is in an assigned group only through a nested group gets no ID for it and fails the allowlist. App roles avoid the limit but not the nesting restriction (see step 2 of the app-role setup above).

Session renewal and sign-out with Entra

With offline_access requested, Entra issues refresh tokens and sessions renew silently. Each renewal re-checks the allowlist and admin mapping against the fresh ID token. Disabling or deleting the user in Entra, or selecting Revoke sessions on their user page, makes the next renewal fail, so they lose access at their next renewal — up to OIDC_SESSION_LIFETIME_SECONDS later.

Entra doesn’t support back-channel logout; it only offers front-channel logout, which DocsGPT doesn’t implement. To cut access off sooner than the next renewal, deactivate users over SCIM or lower OIDC_SESSION_LIFETIME_SECONDS.

Provision Entra users over SCIM

Entra’s provisioning service can create DocsGPT accounts ahead of first sign-in and deactivate them when users are disabled, deleted, or unassigned. Enable SCIM in DocsGPT first (SCIM_ENABLED=true and a SCIM_TOKEN), then:

  1. Create a separate enterprise app for provisioning: Entra ID → Enterprise apps → New application → Create your own application, choose Integrate any other application you don’t find in the gallery (Non-gallery), and name it, for example, DocsGPT provisioning. The enterprise app that Entra creates for your app registration can’t run automatic provisioning, so SCIM needs this second app. Set Visible to users? to No in its Properties so it doesn’t appear as an extra tile in users’ My Apps.

  2. Open the new app’s Provisioning page and create a configuration with automatic provisioning: Tenant URL https://<your-docsgpt-api>/scim/v2 and Secret Token set to your SCIM_TOKEN. Select Test Connection, then save.

  3. Under Attribute mapping:

    • Disable Provision Microsoft Entra ID Groups, since DocsGPT answers group provisioning with 501.
    • In Provision Microsoft Entra ID Users, change the source attribute of the userName mapping to objectId, so it matches the oid claim used as OIDC_USER_ID_CLAIM. Keep the active mapping and delete all other mappings.

    DocsGPT honors only userName and active. Entra sends changes to any other mapped attribute (display name, email, name parts) as SCIM PATCH operations that DocsGPT rejects with 400, and a PATCH that bundles such a change with a deactivation is rejected as a whole.

  4. Under the provisioning app’s Users and groups, assign the users or groups to provision (the default scope is Sync only assigned users and groups), then start provisioning. If the sign-in app has Assignment required? turned on, keep the two apps’ assignments in step.

Entra runs a provisioning cycle roughly every 40 minutes, so a user disabled in Entra is deactivated in DocsGPT — and their sessions revoked — at the next cycle rather than instantly. Use Provision on demand on the provisioning page to push one user immediately.

Entra troubleshooting

Entra reports its own errors as AADSTS codes, either on the Microsoft sign-in page or in the DocsGPT API log when the token request fails.

SymptomCause and fix
AADSTS50011 on the Microsoft sign-in pageThe redirect URI doesn’t match a registered one. Register https://<your-docsgpt-api>/api/auth/oidc/callback exactly; behind a reverse proxy, also set OIDC_REDIRECT_URI to that URL.
AADSTS50105 on the Microsoft sign-in pageAssignment required? is on and the user isn’t assigned to the enterprise app.
auth_failed, and the API log shows AADSTS9002327The callback is registered under the Single-page application platform. Remove it there and add it under Web.
auth_failed, and the API log shows AADSTS7000215 or AADSTS7000222The client secret is wrong (215) or expired (222). Create a new secret and update OIDC_CLIENT_SECRET — use the secret’s Value, not its ID.
auth_failed, and the API log reports an invalid issuerOIDC_ISSUER uses the common or organizations authority. Use https://login.microsoftonline.com/<tenant-id>/v2.0.
missing_claim with OIDC_USER_ID_CLAIM=oidprofile is missing from OIDC_SCOPES; Entra only emits oid with that scope.
not_authorized for some users onlyThey have no role assignment (with OIDC_GROUPS_CLAIM=roles), they’re in more than 200 groups and the groups claim was left out, or they’re only nested members of the assigned group (Entra’s group assignment, for both app roles and Groups assigned to the application, covers direct members only) — see Restrict access with app roles or groups.
Signing out ends on Microsoft’s “You signed out” pageOIDC_FRONTEND_URL isn’t registered as a Web redirect URI.

Restricting sign-in by group

By default any user who can authenticate at the IdP may use DocsGPT. To restrict access to specific IdP groups:

OIDC_ALLOWED_GROUPS=docsgpt-users,platform-admins # OIDC_GROUPS_CLAIM=groups # only if your IdP uses a different claim name

At login the backend reads the OIDC_GROUPS_CLAIM claim (default groups) from the ID token, falling back to the userinfo endpoint when the claim is absent. A user whose groups share no entry with the allowlist is rejected with a clean “not authorized” screen (oidc_error=not_authorized), and the denial lands in the audit log.

Group changes take effect at the next sign-in or the next silent renewal: whenever the IdP returns a fresh ID token during renewal, the allowlist is re-checked — so removing a user from the allowed group cuts off their session at the next renewal instead of whenever they happen to sign in again.

Getting groups into the token:

  • Authentik includes group names in the groups claim through its default profile scope — no extra configuration needed.
  • Keycloak does not emit groups by default. On the client, open Client scopes → the client’s dedicated scope → Add mapper → By configuration → Group Membership, set the claim name to groups, and turn Full group path off so the claim carries plain names (devs) rather than paths (/devs).
  • Microsoft Entra ID emits group object IDs rather than names, and omits the claim for users in more than 200 groups. App roles in the roles claim are usually the better fit — see Restrict access with app roles or groups.

Granting admin via groups

Separately from who may sign in, you can map an IdP group to the global admin role with OIDC_ADMIN_GROUPS:

OIDC_ADMIN_GROUPS=platform-admins

Members of the listed groups are granted admin; the mapping is re-evaluated at every login and every silent renewal, so removing a user from the admin group revokes their admin at the next renewal (exactly like the sign-in allowlist). It is independent of OIDC_ALLOWED_GROUPS, and leaving it unset never mass-revokes admin. On a fresh deployment, bootstrap the first admin with this mapping or with the docsgpt grant-admin command (see Bootstrapping the first admin). For the full roles, teams, and admin-dashboard model see Access Control, Roles & Teams.

Silent session renewal

The DocsGPT session JWT lives for OIDC_SESSION_LIFETIME_SECONDS (default 8 hours). Sessions renew without user-visible interruptions, in one of two ways:

  • With a refresh token. When the IdP issues one, the backend stores it server-side (in Redis — never in the browser) and the frontend calls POST /api/auth/oidc/refresh about 15 minutes before the session expires. The backend redeems the refresh token at the IdP, re-validates the fresh ID token (including the group allowlist), mints a new session JWT, and rotates the stored refresh token. The user notices nothing.
  • Without a refresh token. The frontend lets the session run to expiry and then redirects through the IdP again. While the IdP session is still alive, this round-trip is also silent; the user only sees a sign-in page once the IdP session is gone too.

Getting a refresh token:

  • Keycloak issues refresh tokens for the authorization-code flow by default — nothing to change.
  • Microsoft Entra ID issues refresh tokens when offline_access is in OIDC_SCOPES, as in the Entra configuration.
  • Authentik only issues refresh tokens when the offline_access scope is requested:
    OIDC_SCOPES=openid profile email offline_access

Revoking the user’s consent or sessions at the IdP makes the next renewal fail, and the user must sign in again. For revocation that doesn’t wait for the next renewal, configure back-channel logout.

Back-channel logout

DocsGPT implements OIDC Back-Channel Logout 1.0 . The IdP POSTs a signed logout_token to:

POST https://<your-docsgpt-api>/api/auth/oidc/backchannel-logout

DocsGPT validates the token (signature via JWKS, issuer, audience, replay protection) and immediately revokes the user’s live sessions through a Redis denylist — revoked requests get 401 with error: token_revoked. Signing the user out at the IdP, or an admin revoking their sessions there, takes effect on their next DocsGPT request instead of at session expiry.

The endpoint is called server-to-server, so it must be reachable from the IdP (it is not a browser redirect).

  • Keycloak: open the client → Settings and set Backchannel logout URL to https://<your-docsgpt-api>/api/auth/oidc/backchannel-logout.
  • Authentik (2025.8.0 and later; marked Preview): on the OAuth2/OpenID provider set Logout Method to Back-channel and Logout URI to the same URL — see the Authentik logout docs . Authentik sends the logout token when a user logs out, an admin deletes their session, the account is deactivated, or the session is revoked. On older Authentik versions back-channel logout is unavailable — revocation latency then falls back to the session lifetime, or use SCIM deactivation, which also revokes sessions instantly.
  • Microsoft Entra ID doesn’t support back-channel logout — see Session renewal and sign-out with Entra for the alternatives.

SCIM user provisioning

DocsGPT exposes a SCIM 2.0  endpoint so your IdP can drive the user lifecycle: create accounts ahead of first login and — more importantly — deactivate them on offboarding. Deactivating a user revokes their live sessions immediately and blocks future sign-ins (they see an “account disabled” screen); reactivating restores access.

SettingRequiredDefaultDescription
SCIM_ENABLEDyesfalseSet to true to serve the /scim/v2 endpoints.
SCIM_TOKENyes—Bearer token the IdP’s SCIM client must present. Use a long random string.

The base URL is https://<your-docsgpt-api>/scim/v2; every request must carry Authorization: Bearer <SCIM_TOKEN>.

Match the SCIM userName to the OIDC user id

SCIM identifies users by userName, which DocsGPT matches against its user id — the value of OIDC_USER_ID_CLAIM. With the default sub claim, the userName your IdP sends (typically the email) would never line up with the opaque sub of the same user signing in, and DocsGPT would treat them as two unrelated accounts. When using SCIM, set OIDC_USER_ID_CLAIM=email and have the IdP send the email as the SCIM userName. Microsoft Entra ID is the exception: use oid and send the object ID as userName, as described in Provision Entra users over SCIM.

What the endpoint supports

OperationSupport
GET /scim/v2/ServiceProviderConfig, /ResourceTypes, /SchemasDiscovery documents.
GET /scim/v2/UsersList, with the exact filter userName eq "..." and startIndex/count pagination (1-based, max 200 per page).
POST /scim/v2/UsersCreate; returns 409 when the userName already exists.
GET /scim/v2/Users/<id>Read.
PUT / PATCH /scim/v2/Users/<id>Activate/deactivate via the active attribute; string values such as Okta’s "true" or Entra’s "False" are accepted. userName is immutable. PUT ignores every other attribute. PATCH accepts only replace operations, either with "path": "active" or without a path (keys other than active in the value are ignored); any other operation or path returns 400.
DELETE /scim/v2/Users/<id>Soft delete — deactivates the account instead of removing data.
/scim/v2/GroupsGroup provisioning is not supported: listing returns an empty result so IdP probes don’t fail, and mutations return 501. Use the group allowlist for group-based access control instead.

IdP setup pointers

  • Okta: add SCIM provisioning to the app integration with SCIM connector base URL = https://<your-docsgpt-api>/scim/v2 and authentication mode HTTP Header carrying the bearer token. Enable creating and deactivating users; skip group push.
  • Authentik: create a SCIM provider with the same base URL and the token, and attach it to the application as a backchannel provider. Sync users only — leave group mappings out, since DocsGPT answers group provisioning with 501.
  • Microsoft Entra ID: see Provision Entra users over SCIM — it needs the attribute mappings trimmed to userName and active.

Login auditing

Authentication activity is recorded in auth_events, an append-only Postgres table carrying the event name, IP address, user agent, a JSONB metadata column, a timestamp, and two attribution columns:

  • actor_id — who performed the action. Never empty.
  • target_id — the user the action was performed on, or NULL when the event is not about a user (a team change, an instance-wide policy).

They differ whenever someone acts on someone else: an admin deactivating an account is actor_id = <the admin>, target_id = <the account>. For self-service events such as a login they are the same user. Automated actors are named system:oidc (a group-driven role change at login) and system:scim (provisioning).

A BEFORE INSERT trigger fills both columns for any row that arrives without an actor_id, applying the same rules as the migration’s backfill. That keeps attribution intact for a writer from a release that predates the columns — during a rolling deployment, for instance — instead of rejecting the insert or flattening the actor to a sentinel.

The events this page covers are oidc_login, oidc_login_denied (metadata.reason is not_authorized or account_disabled), oidc_refresh, backchannel_logout, scim_created / scim_deactivated / scim_reactivated, and role_granted / role_revoked with metadata.source = oidc_group for a group-driven change. The same table also records admin, team, quota, token and data-plane events; the full list is the audit event table.

See Access Control, Roles & Teams for the admin Activity view that reads these events, and for the two other journals it merges them with. To query the table directly:

SELECT created_at, event, actor_id, target_id, ip, metadata FROM auth_events ORDER BY created_at DESC LIMIT 50;

Troubleshooting

When sign-in fails, the browser lands back on the frontend with an #oidc_error=<code> fragment and the sign-in screen shows a matching message:

CodeCause
invalid_stateThe login attempt expired (the state is held for 10 minutes) or was replayed. Retrying the sign-in usually fixes it.
auth_failedToken exchange or ID-token validation failed — check the API logs. Most common: OIDC_ISSUER doesn’t match the issuer the discovery document reports (for Authentik this includes the application slug and trailing slash), or clock skew beyond the allowed 60 seconds.
missing_claimNeither the ID token nor userinfo contains 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.
not_authorizedThe user’s groups don’t intersect OIDC_ALLOWED_GROUPS — see Restricting sign-in by group.
account_disabledThe account was deactivated via SCIM or by an admin. Reactivate it over SCIM, or from Admin → Users (PATCH /api/admin/users/<id> with {"active": true}).

Other issues:

  • 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 — without back-channel logout, sessions outlive IdP-side revocation until the next renewal or expiry. Configure back-channel logout for instant revocation, deactivate the user over SCIM, or lower OIDC_SESSION_LIFETIME_SECONDS.
  • The API refuses to start — settings validation fails before the app loads. AUTH_TYPE=oidc requires settings: OIDC_ISSUER, ... names the missing OIDC_ISSUER, OIDC_CLIENT_ID or OIDC_FRONTEND_URL. SCIM_ENABLED requires settings: SCIM_TOKEN means SCIM is on without a token.
  • SCIM requests fail — 404: SCIM_ENABLED is not true. 401: the presented bearer token doesn’t match SCIM_TOKEN.
  • Login endpoints return 503 — Redis is unreachable or the IdP discovery document can’t be fetched from the API host.
Last updated on