Skip to content

OIDC authentication

Operator login is OIDC-only. The configured Authority is the URL of any provider that supports the Authorization Code flow with PKCE and OIDC discovery — Keycloak, Okta, Auth0, Azure Entra ID, Google Workspace, AWS Cognito, ADFS, GitLab, GitHub Enterprise.

Settings

Key Required Notes
Authentication:Oidc:Authority yes Issuer URL (must equal the iss claim in returned tokens)
Authentication:Oidc:ClientId yes Application ID
Authentication:Oidc:ClientSecret yes (confidential client) Secret value; treat as a password
Authentication:Oidc:RoleClaim no Default ampora:role. Where Ampora reads the role from
Authentication:Oidc:TenantClaim no Default tenant. Where Ampora reads the tenant discriminator from
Authentication:Oidc:CallbackPath no Default /signin-oidc. Override if you mount Ampora under a path

Provider checklist

Any compliant provider needs to do four things:

  1. Issue ID tokens signed with RS256 (or another JWS alg supported by the .NET OIDC handler).
  2. Honour the openid profile email scopes.
  3. Carry an Ampora role claim — see below for the mapping.
  4. Carry a tenant discriminator claim if you run multi-tenant.

Role mapping

Ampora has three roles:

  • Admin — full read/write
  • Operator — rollouts, no settings/governance
  • Viewer — read-only

The role is read from a single string-or-array claim. Default claim name is ampora:role. Configure your provider to emit one of:

  • a single string value: "Admin"
  • a list: ["Operator", "Viewer"] (highest wins)

The first user to log in is bootstrapped as Admin if no users exist yet. After that, role assignment follows the claim.

Keycloak

Use the local development stack as a working blueprint — deploy/dev/keycloak/realm-ampora.json ships:

  • a confidential client ampora-web,
  • a protocol mapper that emits ampora:role from a user attribute,
  • three users (alice/Admin, bob/Operator, carol/Viewer).

For production-grade Keycloak:

  1. Create a confidential client ampora-web. Set:
  2. Valid Redirect URIs to https://AMPORA_HOST/signin-oidc.
  3. Valid Post Logout Redirect URIs to https://AMPORA_HOST/signout-callback-oidc.
  4. Web Origins to https://AMPORA_HOST.
  5. Add a User Attribute mapper:
  6. User Attribute: ampora_role
  7. Token Claim Name: ampora:role
  8. Claim JSON Type: String
  9. Add to ID token, access token, userinfo: yes.
  10. Or, alternatively, a Group Membership mapper that emits group membership as ampora:role. Combined with three groups (ampora-admin, ampora-operator, ampora-viewer) this gives group-based role assignment without per-user attributes.

Azure Entra ID (Azure AD)

  1. Register an application:
  2. Redirect URI: https://AMPORA_HOST/signin-oidc
  3. Front-channel logout URL: https://AMPORA_HOST/signout-callback-oidc
  4. Add a client secret. Copy it into Authentication__Oidc__ClientSecret.
  5. Token configuration → Add optional claimgroups. Use a transitive groups assignment if your AAD is large.
  6. Add an App role for each Ampora role (Admin, Operator, Viewer). Assign users / groups to roles.
  7. Set Authentication__Oidc__RoleClaim to roles (the standard claim name AAD uses for app roles).

Authority: https://login.microsoftonline.com/{tenant}/v2.0 (the /v2.0 suffix is mandatory).

Okta / Auth0

  1. Create an OIDC application of type "Web".
  2. Redirect URI: https://AMPORA_HOST/signin-oidc.
  3. Map a user property to the ampora:role claim via the Authorization Servers → Claims UI. Use a Groups claim if you prefer group-based assignment.
  4. Authority: https://{tenant}.okta.com/oauth2/default (Okta) or https://{tenant}.auth0.com (Auth0).

Common pitfalls

  • Issuer mismatch: the URL in Authority must equal the iss claim exactly, including trailing slash and protocol. The dev stack works around this with host.docker.internal so the browser and the Ampora container resolve the IdP through the same hostname.
  • Confidential client with client_secret_post: some providers default to client_secret_basic, others to client_secret_post. .NET's OIDC handler defaults to client_secret_post. If your IdP rejects, switch the client to "post" auth or set Authentication__Oidc__ClientAuthentication=ClientSecretBasic.
  • Path-based mount: if you serve Ampora under https://AMPORA_HOST/ampora, override CallbackPath to /ampora/signin-oidc and update redirect URIs accordingly.

Verifying the login flow

Once configured, hitting https://AMPORA_HOST should:

  1. Redirect to your IdP's login page.
  2. After login, redirect back to /signin-oidc.
  3. Show the Ampora dashboard with the user's name in the top-right.
  4. The user menu shows the active Tenant and Role.

If any step fails, see Troubleshooting → OIDC for the diagnostic walkthrough.