Configuring OIDC

Wiring an OIDC identity provider into Flux — issuer, audience, JWKS, signing algorithms, claim mapping, and clock skew.

Flux validates OIDC bearer tokens on every request: there is no session and no token cache. The provider fetches your IdP’s discovery document, pulls JWKS keys from it, verifies the JWT signature, and resolves the sub claim to a principal in Flux’s registry. This page walks through the configuration surface, one provider example, and the failure modes you will hit first.

Configuration fields

All fields live under [flux.security.auth.oidc]. Defaults come from flux/security/config.py::OIDCConfig.

FieldTypeDefaultPurpose
enabledboolfalseTurn the provider on. When true, issuer and audience are required at startup.
issuerstr""Your IdP’s issuer URL. Flux appends /.well-known/openid-configuration to it for discovery.
audiencestr""The aud claim your IdP issues for this Flux server. Tokens with any other aud are rejected.
jwks_cache_ttlint3600Seconds to cache the discovery document and JWKS keys.
clock_skewint30Leeway in seconds for exp / nbf validation.
default_user_roleslist[str][]Roles assigned to a principal on auto-provisioning. Empty means auto-provisioning is off — unknown subjects get a Principal not provisioned 401.
roles_claimstr"roles"Deprecated. Flux no longer reads roles from token claims; roles live in the principals registry.

There is no jwks_url field. Flux always discovers the JWKS endpoint from the issuer’s .well-known/openid-configuration document and reads jwks_uri from it. Without discovery, OIDC cannot be configured.

Supported signing algorithms are RS256 and ES256. HS256 and other symmetric algorithms are rejected.

Enabling OIDC

Pick one of the two surfaces; they are equivalent and follow the same precedence as the rest of Flux config (env vars override flux.toml).

In flux.toml:

[flux.security.auth.oidc]
enabled        = true
issuer         = "https://login.example.com/realms/flux"
audience       = "flux-server"
jwks_cache_ttl = 3600
clock_skew     = 30
default_user_roles = ["viewer"]

Or via environment variables:

export FLUX_SECURITY__AUTH__OIDC__ENABLED=true
export FLUX_SECURITY__AUTH__OIDC__ISSUER=https://login.example.com/realms/flux
export FLUX_SECURITY__AUTH__OIDC__AUDIENCE=flux-server
export FLUX_SECURITY__AUTH__OIDC__JWKS_CACHE_TTL=3600
export FLUX_SECURITY__AUTH__OIDC__CLOCK_SKEW=30
export FLUX_SECURITY__AUTH__OIDC__DEFAULT_USER_ROLES='["viewer"]'

Enabling OIDC flips the derived auth.enabled flag to true automatically (it is computed as oidc.enabled or api_keys.enabled). You do not set it directly — anything written to [flux.security.auth] enabled is ignored.

Claim mapping

Flux reads four token fields directly: sub, iss, aud, and exp (all four are required). After signature validation, the provider resolves the principal by (sub, iss) against the principals registry.

Token claimUsed for
subPrincipal lookup key and the FluxIdentity.subject value.
issCo-lookup key — supports two IdPs issuing the same sub.
audMust equal the configured audience.
exp, iatLifetime validation (with clock_skew leeway).
nameDisplay name on the principal record.
everything elseStored on the principal as opaque metadata, minus a small exclusion list (email, email_verified, iss, aud, exp, iat, jti, azp, nonce, at_hash, auth_time, session_state, acr, amr, realm_access, resource_access, typ).

Roles are not pulled from the token. They are assigned in Flux via flux roles and flux principals assign-role. If default_user_roles is set, an unknown (sub, iss) is auto-provisioned with those roles on first sign-in; otherwise, the request fails with Principal not provisioned.

Example: Keycloak

On the Keycloak side, create a client (flux-server), set its access type appropriately for your front-end, and add an audience mapper so that issued tokens carry aud=flux-server. Confirm the realm signs with RS256 (the default).

On the Flux side:

[flux.security.auth.oidc]
enabled  = true
issuer   = "https://keycloak.example.com/realms/flux"
audience = "flux-server"
default_user_roles = ["viewer"]

Sanity-check the issuer by hitting https://keycloak.example.com/realms/flux/.well-known/openid-configuration — the issuer field in that response must match your config exactly (trailing slash and all). Mismatched issuers are the most common first-deployment 401.

Token lifecycle

OIDC tokens are short-lived. Flux validates every request from scratch — there is no server-side session, no refresh-token handling, and no token introspection callback. When a token expires, the next request returns 401; obtaining a fresh access token is the client’s responsibility (typically via the OAuth refresh-token flow against your IdP). JWKS keys are cached for jwks_cache_ttl seconds, so a key rotation at your IdP propagates within that window without restarting Flux.

What goes wrong first