aletyx-oidc-spring-boot-starter. Authorization is driven by app roles carried in the JWT issued by the provider. Two providers are supported out of the box:
- Microsoft Entra ID (formerly Azure AD)
- Keycloak
ALETYX_OIDC_PROVIDER; defaults to entra. Setting it to none disables authentication entirely and is intended for local diagnostics only — never run a production deployment with none.
Required claims, scopes, and roles
Regardless of provider, Decision Control expects the following from the issued ID/access token:Scopes
Configurable via
ALETYX_OIDC_SCOPES as a comma-separated list. The default openid,profile,email meets the minimum required set.
Role claim
Decision Control reads roles from a single configurable claim path. The default path isroles (top-level claim). Override with ALETYX_OIDC_ROLE_CLAIM_PATH if your provider emits roles under a different path (e.g. realm_access.roles for Keycloak realm roles).
You define your own role values at the provider and map them to Decision Control capabilities through the URL-level RBAC allow-lists (see URL-level RBAC below). Decision Control does not hard-code role names — the values you assign are entirely up to you.
Tenant claim
Microsoft Entra ID
Prerequisites
- An Entra tenant with permissions to create app registrations
- A user account allowed to grant tenant-wide admin consent
App registration steps
-
Register the application
- Azure Portal → Microsoft Entra ID → App registrations → New registration
- Name: e.g.
Decision Control — <env> - Supported account types: Accounts in this organizational directory only (single tenant)
- Redirect URI: select Web and enter
https://<dc-host>/auth/callback - Click Register
-
Record the identifiers
- From the Overview page, note the Application (client) ID and Directory (tenant) ID. These map to
ALETYX_OIDC_CLIENT_IDandALETYX_OIDC_ENTRA_TENANT_IDrespectively.
- From the Overview page, note the Application (client) ID and Directory (tenant) ID. These map to
-
Configure additional redirect URIs (optional)
- Authentication → Web → Add URI: include any additional environments (
http://localhost:8080/auth/callbackfor local dev, additional cluster hostnames, etc.)
- Authentication → Web → Add URI: include any additional environments (
-
Generate a client secret
- Certificates & secrets → Client secrets → New client secret
- Provide a description and expiry (24 months is a common default)
- Copy the
Valueimmediately — Azure displays it only once - It maps to
ALETYX_OIDC_ENTRA_CLIENT_SECRET
-
Grant API permissions
- API permissions → Add a permission → Microsoft Graph → Delegated permissions
- Add:
openid,profile,email,User.Read - Click Grant admin consent for
<tenant>
-
Define app roles
- App roles → Create app role for each role your organization needs (e.g. a viewer role, an author role, an admin role). The role Value is what appears in the token and what you list in the RBAC allow-lists.
-
Assign users to roles
- Enterprise applications → select the application → Users and groups → Add user/group
- Group-based assignment requires Microsoft Entra ID P1 or higher; Free tenants must assign individual users.
Required configuration variables — Entra
Example — environment file
Keycloak
Realm and client setup
- Create or select a realm in the Keycloak admin console.
-
Create the client
- Clients → Create client → Client type: OpenID Connect → Client ID: e.g.
decision-control - Client authentication: ON (confidential client) · Standard flow: ON · Direct access grants: OFF
- Valid redirect URIs:
https://<dc-host>/auth/callback(addhttp://localhost:8080/auth/callbackfor local dev) - Web origins:
https://<dc-host>
- Clients → Create client → Client type: OpenID Connect → Client ID: e.g.
-
Capture the client secret
- Credentials tab → copy the Client Secret. The client ID maps to
ALETYX_OIDC_KEYCLOAK_CLIENT_IDand the secret to its own dedicated variableALETYX_OIDC_KEYCLOAK_CLIENT_SECRET.
- Credentials tab → copy the Client Secret. The client ID maps to
-
Define and map roles
- Create client or realm roles, then add a role mapper so the roles land in the token under the claim path you set in
ALETYX_OIDC_ROLE_CLAIM_PATH(rolestop-level, orrealm_access.rolesfor realm roles).
- Create client or realm roles, then add a role mapper so the roles land in the token under the claim path you set in
Required configuration variables — Keycloak
Example — environment file
URL-level RBAC
Decision Control can gate access to specific UI areas and runtime endpoints by role. RBAC is disabled by default; when enabled, each request must carry a role from the relevant per-action allow-list. An empty list locks everyone out of that action, so populate all the lists you intend to use when you turn RBAC on.
Each allow-list is a comma-separated set of role values (the same values you assigned at the provider). A user is permitted an action if any of their token roles appears in that action’s list.
Example (RBAC on, three roles
dc-user, dc-author, dc-admin):
Server-to-server secret
Dev identity (provider=none)
Only used when ALETYX_OIDC_PROVIDER=none. The shared starter installs a synthetic authenticated principal from these values so local/dev runs behave like a real OIDC session (audit trail, RBAC checks, tenant resolution all see a real user). Ignored in entra / keycloak mode, where the identity comes from the JWT. Intended for local diagnostics only; never use in production.
Verifying the configuration
After deploying, the Tower/DC auth bootstrap endpoint returns the public client metadata expected by the SPA:/auth/callback.
Common pitfalls
-
Redirect URI mismatch (
AADSTS50011for Entra,Invalid redirect URIfor Keycloak): the redirect URI must exactly match a URI registered at the provider, including the trailing/auth/callback. Common cause: changing the application hostname without updating the registration. -
401on every request after enabling Entra: the UI readsALETYX_OIDC_PROVIDERfrom/env.json, which the backend caches at startup. After changing the provider, restart the pod so the cached/env.jsonis regenerated. If a CDN sits in front, ensure/env.jsonis not cached as a static asset (it contains.json, which can match*.jscache rules). -
RBAC locks everyone out: enabling
ALETYX_OIDC_RBAC_ENABLED=truewith empty allow-lists denies all access. Populate every list you intend to use. -
Roles missing from the token: confirm the user has the role assignment, that the role is mapped into the token, and that
ALETYX_OIDC_ROLE_CLAIM_PATHmatches the path in the token. -
Disabling authentication for diagnostics:
ALETYX_OIDC_PROVIDER=nonebypasses authentication. Intended for local diagnostics only; never use in production.