Skip to content

Single sign-on with OIDC

Lumastra can act as an OpenID Connect relying party: users click a “Sign in with…” button, authenticate at your provider, and land back in Lumastra with a normal session. Unlike forward auth, this needs no reverse-proxy auth layer. Lumastra runs the whole Authorization Code + PKCE flow itself.

Use OIDC when you want SSO and central user management but would rather not put a forward-auth proxy in front of every app. (If you already run forward auth, that works just as well: see Authentication for how to choose.)

1. Register an application at your provider

Section titled “1. Register an application at your provider”

Create an OAuth2 / OpenID Connect application (Authorization Code flow, PKCE enabled; Lumastra always sends S256). Configure:

  • Redirect / callback URI: https://your-domain/auth/oidc/callback (exact match).
  • Scopes: openid profile email. Add groups if your provider needs an explicit scope to emit group membership.
  • Client type: confidential (with a secret) or public (PKCE only: leave the secret empty in Lumastra).

Note the issuer URL the provider shows. Lumastra appends /.well-known/openid-configuration to it for discovery.

AUTH_OIDC_ENABLED=true
AUTH_OIDC_ISSUER=https://idp.example.com/application/o/lumastra/
AUTH_OIDC_CLIENT_ID=your-client-id
AUTH_OIDC_CLIENT_SECRET=your-client-secret # omit for a public client
AUTH_OIDC_SCOPES=openid profile email groups # default: openid profile email
AUTH_OIDC_GROUPS_CLAIM=groups # claim carrying group membership
AUTH_OIDC_PROVIDER_NAME=Authentik # shown on the "Sign in with…" button
# A user in this group becomes an admin (shared with forward auth; default: admin)
ADMIN_GROUPS=lumastra-admins
# Your public origin, so the session cookie gets its Secure flag
ORIGIN=https://your-domain

.env.example already lists ADMIN_GROUPS (commented out). Edit that line rather than adding a second one: with two, the last one silently wins.

Lumastra’s server calls the issuer directly (discovery, token exchange), so it must trust the provider’s TLS certificate. If the provider uses a private CA, mount the CA file and point Node at it:

services:
lumastra:
environment:
NODE_EXTRA_CA_CERTS: /certs/idp-ca.crt
volumes:
- ./certs/idp-ca.crt:/certs/idp-ca.crt:ro

The file must be readable by the app user (uid 1000), so chmod 644 it. The issuer’s hostname must also resolve inside the container to the provider, not just in your browser. A *.localhost name never does, because it always means the container itself. Use a real name, and add extra_hosts: if it isn’t in DNS. If the setup wizard’s Test connection fails, the error names the cause (untrusted certificate, unknown host, connection refused).

VariableDefaultPurpose
AUTH_OIDC_ENABLEDfalseMaster switch. Also requires issuer + client ID.
AUTH_OIDC_ISSUER(none)Discovery base URL (provider’s issuer).
AUTH_OIDC_CLIENT_ID / AUTH_OIDC_CLIENT_SECRET(none)Client credentials. Empty secret means a public/PKCE-only client.
AUTH_OIDC_SCOPESopenid profile emailSpace-separated; must include openid.
AUTH_OIDC_GROUPS_CLAIMgroupsClaim (dot-path for nested) carrying groups, matched against ADMIN_GROUPS.
AUTH_OIDC_PROVIDER_NAMESingle Sign-OnButton label (“Sign in with …”).
AUTH_OIDC_REDIRECT_URI(auto)Override only if the auto-derived …/auth/oidc/callback is wrong.
AUTH_OIDC_LINK_BY_VERIFIED_EMAILtrueLink a first OIDC login to an existing account on a verified email match.
AUTH_OIDC_LINK_BY_USERNAMEfalseLink by username. Less safe; use it for native→OIDC migration.
AUTH_OIDC_SYNC_AVATARtrueCopy the IdP picture at sign-in for users without an uploaded avatar. The server stores it; browsers do not fetch it from the IdP.
AUTH_OIDC_USERINFOfalseAlso call the UserInfo endpoint when groups/email are missing from the ID token.
AUTH_OIDC_ALLOW_INSECUREfalseAllow a non-HTTPS issuer. Local test IdPs only: it disables TLS verification.
AUTH_OIDC_BACKCHANNEL_LOGOUT_CASCADEfalseOn a user-wide (sub) back-channel logout, also revoke the user’s device tokens (TV/mobile), not just the browser session.

If a reverse-proxy auth layer also fronts Lumastra, allowlist the OIDC paths so the login round-trip and the back-channel logout callback aren’t intercepted:

  • /auth/oidc/login
  • /auth/oidc/callback
  • /auth/oidc/backchannel-logout (only if you enable back-channel logout)

In Authentik, add them to the proxy provider’s unauthenticated paths. Other proxies have an equivalent bypass/allow rule.

  • Authentik: add groups as a property mapping on the provider so it lands in the ID token. Set AUTH_OIDC_ISSUER to the provider’s issuer (e.g. https://authentik.example.com/application/o/<slug>/).
  • Keycloak: groups usually arrive as realm_access.roles. Set AUTH_OIDC_GROUPS_CLAIM=realm_access.roles and AUTH_OIDC_SCOPES=openid profile email roles, and enable the roles mapper on the client.
  • Google: standard tokens carry no groups claim, so Google-SSO users come in as non-admin. Promote them manually in Admin → Users (the manual toggle pins them).
  • Local testing: the repo ships a self-contained example at docs/examples/oidc-sso-dex/, a Docker Compose stack with Dex as a throwaway IdP, a ready .env.example, and the exact client/redirect registration.
  1. The user clicks “Sign in with <provider>” on /login (or /setup), which hits /auth/oidc/login.
  2. Lumastra generates PKCE + state + nonce, stores a short-lived transaction, and redirects to your provider.
  3. After the user authenticates, the provider redirects to /auth/oidc/callback; Lumastra verifies the code, signature, issuer, audience, nonce, and PKCE.
  4. It resolves the identity ((issuer, sub)), provisions or links the account, applies admin from the groups claim, and sets the session cookie.

On a fresh install, the first OIDC sign-in becomes the admin only if that account is in an ADMIN_GROUPS group; otherwise the wizard rejects it and creates nothing.

By default, signing out at your provider does not end the user’s Lumastra session. The local session cookie lives until it expires. With OpenID Connect Back-Channel Logout 1.0, your provider notifies Lumastra the moment a user logs out (or an admin terminates their session), and Lumastra revokes the matching local session.

Lumastra exposes a receiver at:

https://your-domain/auth/oidc/backchannel-logout

Point your provider’s back-channel logout URL at that endpoint (no trailing slash). In Authentik, edit the OAuth2 provider and set the Back-channel Logout URI field. No Lumastra config is required to enable the receiver. It’s active whenever OIDC is on.

It’s a POST with Content-Type: application/x-www-form-urlencoded and a single logout_token field. It returns 200 on success, 400 on an invalid token, 404 if OIDC is disabled, or 503 (with a Retry-After header) on a transient outage reaching the provider’s JWKS/discovery endpoint or the database. Retry on a 503.

When a logout arrives, Lumastra verifies the provider’s signed logout_token: signature against the provider JWKS, issuer, audience, expiry, the back-channel-logout events member, that nonce is absent (the spec forbids it on logout tokens), that sub or sid is present, and replay protection. Then:

  • if the token identifies a specific session (sid), it ends just that session;
  • otherwise it ends all of that user’s OIDC sessions (sub).
# Optional: on a user-wide (sub) logout, also revoke the user's device tokens
# (TV / mobile apps signed in via the device flow), for a full sign-out everywhere.
# Default false — device clients are authorized separately and keep their own
# lifecycle. Never applies to a single-session (sid) logout.
AUTH_OIDC_BACKCHANNEL_LOGOUT_CASCADE=false