Auth0
Wiring Auth0 as Flux's OIDC identity provider — tenant URL, custom API audience, and the minimal Flux config.
Auth0 is a hosted OIDC provider that works with Flux out of the box once you configure a custom API (for the audience claim) and an application that can request JWT access tokens for it. The abstract Flux side of the contract lives in Configuring OIDC; this page is the Auth0-specific recipe.
What to register at Auth0
Two pieces, in this order:
- Custom API (Applications → APIs → Create API). The Identifier you set here is what Auth0 stamps into the
audclaim of access tokens. Use something stable likehttps://flux.example.com/api. The exact string is what you will put in Flux’saudiencefield. Signing algorithm:RS256. - Application. The application type depends on how callers acquire tokens:
- Machine-to-Machine for server-to-server callers (CI jobs, scripts, workers calling the Flux API). Authorize the M2M app for the custom API you just created.
- Regular Web Application or SPA if a browser flow is involved. Configure allowed callback URLs for your client; Flux itself does not host a redirect endpoint, so this is only relevant to whatever frontend obtains the token.
When you request a token, you must pass audience=https://flux.example.com/api (the custom API identifier). Without it, Auth0 returns an opaque (non-JWT) access token, and Flux will reject it on the first parse with OIDC token validation failed. This is the single most common Auth0 misconfiguration.
Issuer URL is your tenant URL with a trailing slash: https://<tenant>.<region>.auth0.com/. The trailing slash is part of the value Auth0 puts in the iss claim — drop it and validation fails on issuer mismatch.
Vendor-specific gotchas
- No audience → opaque token. As above. If
curl /oauth/tokenreturns something that does not parse as three base64url segments, theaudienceparameter was missing or wrong. - Trailing slash on the issuer. Auth0 issues
issclaims with the trailing slash. Flux compares theissclaim to the configuredissuerstring verbatim; mismatch is a 401. - Custom domains. If you use an Auth0 custom domain (
https://auth.example.com), tokens issued through that domain carry the custom domain iniss. You then must use the custom domain in Flux’sissuerconfig. Pick one and stick with it; tokens issued under the default tenant URL will not validate against a custom-domain issuer. - Auth0 puts
azpin tokens (authorized party). Flux ignoresazpfor authentication but stores it on the principal as metadata if the principal is auto-provisioned. - Groups, roles, permissions. Auth0 has its own RBAC layer that can stamp
permissionsor custom claims into tokens. Flux does not read any of these —roles_claimis deprecated, and roles come from Flux’s principals registry. Auth0’s RBAC is independent of Flux’s RBAC. See the abstract reference for the role-resolution path. - Token lifetime. Auth0 access tokens default to 24 hours. Shorten them in the API settings if you want faster effective revocation; Flux re-validates every request, so lifetime is the only thing standing between a leaked token and unrestricted access.
The minimal Flux config
[flux.security.auth.oidc]
enabled = true
issuer = "https://your-tenant.us.auth0.com/"
audience = "https://flux.example.com/api"
default_user_roles = ["viewer"]
Replace your-tenant.us.auth0.com with your tenant + region and https://flux.example.com/api with the Identifier of the custom API you created. The default_user_roles = ["viewer"] line opts into auto-provisioning — without it, the first request from any new subject is rejected with Principal not provisioned. Drop the line if you want to manage every principal manually via flux principals create.
jwks_cache_ttl and clock_skew are left at their defaults (3600 and 30 seconds respectively); change them only if you have specific reason.
Verification
Fetch the discovery document:
curl -s https://your-tenant.us.auth0.com/.well-known/openid-configuration | jq .
Confirm issuer matches your config exactly (including trailing slash) and id_token_signing_alg_values_supported contains RS256. Auth0 also advertises HS256 in some contexts; Flux will reject HS256 tokens regardless.
Smoke-test an access token via the client-credentials grant (M2M):
curl -s --request POST https://your-tenant.us.auth0.com/oauth/token \
--header 'content-type: application/json' \
--data '{
"client_id": "<m2m-client-id>",
"client_secret": "<m2m-client-secret>",
"audience": "https://flux.example.com/api",
"grant_type": "client_credentials"
}' | jq -r .access_token
The result should be a three-segment JWT. Decode the payload (echo "$TOKEN" | cut -d. -f2 | base64 -d | jq .) and verify iss, aud, exp, and sub are present. Hit a Flux endpoint with it:
curl -H "Authorization: Bearer $TOKEN" https://flux.example.com/workflows
On startup, Flux logs the OIDC provider initialization once. After the first authenticated request, look for Auto-provisioned principal <sub> (if default_user_roles is set) or OIDC token validation failed: … (if something is wrong with the audience or issuer).
See Configuring OIDC for the Flux-side reference, including the four required claims (sub, iss, aud, exp), the deprecation of roles_claim, and the principal-resolution flow.
Derived against Auth0 docs 2026-05.