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:

  1. Custom API (Applications → APIs → Create API). The Identifier you set here is what Auth0 stamps into the aud claim of access tokens. Use something stable like https://flux.example.com/api. The exact string is what you will put in Flux’s audience field. Signing algorithm: RS256.
  2. 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

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.