Authentication Setup🔗
This guide provides the steps required to configure your OAuth 2.0 / OpenID Connect (OIDC) identity provider so the Apheris Hub can validate JWTs and support multi-user deployments. After completing the provider configuration here, continue with the Docker Deployment Guide or the Kubernetes Deployment Guide depending on how you deploy the Hub.
Identity Provider Requirements🔗
Your identity provider must meet the following requirements:
- OIDC Discovery: Exposes
{domain}/.well-known/openid-configurationso the Hub can discover the JWKS endpoint - JWKS Endpoint: Publishes a JWKS (JSON Web Key Set) for token signature verification
- JWT Token Format: Issues JWT access tokens with enough standard claims to identify users
- Required Token Claims:
iss(issuer): Must match what the Hub expects as issuer (hub.auth.issuerwhen set, otherwisehub.auth.domain)aud(audience): Must include the value configured as the Hub audience (hub.auth.audience) so tokens are intended for the Hub APIemail: Used for user identification and data segregation, so it must be present in the access tokenexp: Token expiration timestamp
- Optional but recommended claims (used for a better user experience in the UI):
namegiven_namefamily_name
The Hub has been tested with Auth0, Microsoft Entra (Azure AD), ForgeRock and Dex. Other OIDC-compliant providers should be supported but have not been validated.
General Configuration🔗
The Hub learns about your identity provider through the hub.auth configuration block. The following fields are required for most providers, with issuer, extraScopes, and browserUrl only necessary when your provider requires them:
hub:
auth:
enabled: true
providerType: "oidc" # optional (required for Auth0 and ForgeRock)
domain: "https://your-auth-domain/" # required
audience: "https://your-api-audience" # required
clientId: "your-client-id" # required
issuer: "https://issuer-if-different/" # optional (required for Microsoft Entra)
extraScopes: "optional:scope" # optional (required for Microsoft Entra)
browserUrl: "https://hub.example.com" # optional (internal IdP behind proxy/ingress)
Enabled🔗
Controls whether the Hub enforces authentication and multi-user isolation.
- When
enabled: false, the Hub runs in single-user mode. Tokens are not required, and the otherhub.auth.*fields are ignored by the backend. - When
enabled: true, the Hub requires at leastdomain,audienceandclientIdto be set and will reject configuration that is incomplete or malformed.
Domain🔗
hub.auth.domain is the base URL of your identity provider and is used to discover JWKS and metadata, and by the frontend as the OIDC authority.
- Always provide a full URL with scheme (for example,
https://...), for example:- Auth0:
https://<tenant>.auth0.com/ - Microsoft Entra:
https://login.microsoftonline.com/<tenant-id>/v2.0/ - Dex (cluster DNS):
http://dex.dex.svc.cluster.local:5556
- Auth0:
- If
enabled: trueanddomainis empty, the backend treats the configuration as invalid and will not accept requests using this auth setup. - For providers where the discovery base URL and issuer are identical (e.g., Auth0, Dex),
domainis sufficient; for providers where they differ (e.g., Entra), also configureissuer(see below).
Audience🔗
hub.auth.audience tells the Hub which API the access token must target.
- It must be an identifier, usually a URI or URL, that your identity provider issues tokens for. For example:
- Auth0:
https://hub.yourdomain.com/api - Microsoft Entra:
api://<client-id>
- Auth0:
- The backend validates that this value is present and has a URL scheme when auth is enabled. If
audienceis empty whileenabled: true, the Hub rejects the configuration. - Tokens that do not include this audience are rejected by the validator.
Client ID🔗
hub.auth.clientId is the public client identifier of your Single Page Application (SPA), used by the browser when initiating the authorization code + PKCE (Proof Key for Code Exchange) flow. This value must be set correctly for the frontend to obtain tokens, see examples below for IdP-specific configurations.
Issuer (optional)🔗
hub.auth.issuer is the issuer URL the backend expects in the iss claim of access tokens.
- If
issueris empty, the backend usesdomainas the expected issuer. - For providers where the discovery URL and token issuer are the same (Auth0, Dex), you can leave this field empty.
- For providers where they differ (Microsoft Entra), set
hub.auth.issuerto the exact issuer in the token (for example,https://sts.windows.net/<tenant-id>/) while keepingdomainas the discovery URL. - If you set
issuer, it must be a valid URL with a scheme; otherwise the backend treats the configuration as invalid. issuermust exactly match the tokenissclaim, including any trailing slash.
Extra Scopes (optional)🔗
hub.auth.extraScopes lets you request provider-specific scopes in addition to the default openid profile email offline_access.
- If empty, the frontend requests only the default scopes. Many providers (e.g., Auth0) will still issue an access token for your API when
audienceis set. - Some providers (notably Microsoft Entra) require you to request a specific API scope (for example,
api://<client-id>/<scope-name>) so the identity provider issues an access token with the expected audience. In those cases, use the scope name you configured on the provider side.
Browser URL (optional)🔗
hub.auth.browserUrl is only needed when the backend reaches the identity provider through an internal address that browsers cannot resolve (for example, Kubernetes cluster DNS) but users access the provider through a different, browser-friendly URL.
- When set, the Hub fetches the discovery document from
issuerand rewrites its URLs so the browser talks to thebrowserUrlhost that it can reach.- Typical use case: an internal IdP with issuer
https://idp.internal.svc.cluster.local/and an external proxy or ingress athttps://sso.example.com; setissuerto the internal URL andbrowserUrlto the public one.
- Typical use case: an internal IdP with issuer
- For Auth0 and Entra, where discovery uses public HTTPS endpoints reachable from the browser, leave
browserUrlempty and the frontend will use direct discovery.
Provider Type (optional)🔗
hub.auth.providerType enables small provider-specific compatibility behaviors on the Hub.
- Supported values are
oidc(default),auth0, andforgerock. auth0: enables Auth0-specific logout behavior.forgerock: enables ForgeRock-specific behavior. The UI exchanges authorization codes and refresh tokens via the Hub proxy endpoint (/api/v1/auth/token) to avoid browser CORS issues at the ForgeRock token endpoint. The UI also forcesprompt=loginon the initial login redirect to avoid "stuck" SSO sessions in common ForgeRock setups. Logout is local (clears browser state and returns to the Hub), even if the provider advertises anend_session_endpoint.
Docker Deployment Demo Auth0🔗
The Docker deployment script deploy_apherisfold includes a proof-of-concept configuration that uses a shared Apheris-managed Auth0 tenant so you can try multi-user mode without configuring your own identity provider.
-
Set the following in your
config.yaml:config.yamlhub: auth: enabled: true domain: "" audience: "" clientId: ""
- When
enabled: trueand these fields are left empty, the script injects demo Auth0domain,audience, andclientIdvalues when starting the Hub container. - This mode is:
- Intended for localhost development only (ports 8080/8081).
- Shared across multiple users and environments.
When you are ready to use your own identity provider, replace the empty values with your real hub.auth.domain, hub.auth.audience, and hub.auth.clientId and follow the Auth0, Microsoft Entra, ForgeRock, or Dex sections below.
Auth0 Guide🔗
- Create the SPA application
- Visit Applications → Applications → Create Application.
- Name it (e.g.,
ApherisFold) and choose Single Page Application. - Note the Domain and Client ID from the Settings tab (you will need them for
hub.auth.domainandhub.auth.clientId).
-
Configure allowed URLs
-
In the application settings, add your frontend URLs to Allowed Callback URLs, Allowed Logout URLs, and Allowed Web Origins. Example entries for localhost development:
http://localhost:8000 http://localhost:8000/login
-
-
Create the API
- Navigate to Applications → APIs → Create API.
- Set the name (e.g.,
ApherisFold API) and use a URL-like identifier such ashttps://fold.apheris.com/api. - Keep the signing algorithm at RS256.
- Authorize the SPA
- In the API’s Machine to Machine Applications tab, authorize your SPA so it can request an access token for the API.
- Enable login methods
- Under Authentication → Connections, enable the identity providers you want (Database, Google, GitHub, Microsoft, etc.).
- Back in Applications → [Your SPA], toggle the desired connections so they appear on the login screen.
- Claims
- Auth0 already includes
emailclaim in both ID and access tokens, so no extra configuration is required.
- Auth0 already includes
Example configuration:
hub:
auth:
enabled: true
providerType: "auth0"
domain: "https://<tenant>.auth0.com/"
audience: "https://fold.apheris.com/api"
clientId: "<client-id>"
Auth0 deployments do not require issuer or extraScopes to be set since the discovery URL and token issuer are identical, and the audience is sufficient to obtain access tokens for your API.
Microsoft Entra (Azure AD) Guide🔗
Use this section to configure Microsoft Entra (formerly Azure AD) as the OIDC provider for the Hub.
Microsoft Entra setup🔗
- Register a SPA application
- In the Microsoft Entra admin center, go to Identity → Applications → App registrations → New registration.
- Enter a name (for example,
ApherisFold) and choose the supported account types. - Leave redirect URIs empty for now.
- Click Register.
- Configure the application as a SPA
- In Authentication (Preview), on the Redirect URI configuration tab, click Add Redirect URI.
- Select Single-page application.
- Add a redirect URI, for example
http://localhost:8000, matching the URL where your frontend is served. - Click Configure.
- Still in Authentication (Preview), on the Settings tab, enable Allow public client flows so the SPA can use the authorization code + PKCE flow to obtain access tokens.
- Click Save.
- Expose the API (audience and scope)
- In Expose an API, click Add a scope.
- Keep the default Application ID URI (
api://<client-id>). This becomes yourhub.auth.audience. Click Save and continue. - Set a Scope name (for example,
access) so the resulting full scope isapi://<client-id>/<scope-name>(for example,api://<client-id>/access). - Select Admins and users for who can consent, add a short Admin consent display name and Admin consent description, then click Add scope.
- Configure token claims
- Under Token configuration, click Add optional claim, choose token type Access, and select the email claim so the access token includes
email. - Optionally also add the
given_nameandfamily_nameclaims for a better user experience in the UI. - Confirm by granting consent and clicking Add.
- Under Token configuration, click Add optional claim, choose token type Access, and select the email claim so the access token includes
Hub configuration🔗
On the Overview tab of your registered application, note the following values:
Application (client) ID: use this forhub.auth.clientId.Directory (tenant) ID: use this to build thehub.auth.domainandhub.auth.issuerURLs.Application ID URI: use this forhub.auth.audience.- When you click on
Application ID URI, you can see the full scope name you created earlier; use this forhub.auth.extraScopes(for example,api://<client-id>/access).
Example configuration:
hub:
auth:
enabled: true
domain: "https://login.microsoftonline.com/<tenant-id>/v2.0/"
issuer: "https://sts.windows.net/<tenant-id>/"
audience: "api://<client-id>"
clientId: "<client-id>"
extraScopes: "api://<client-id>/<scope-name>"
hub.auth.domain(https://login.microsoftonline.com/<tenant-id>/v2.0/) is the discovery URL used by both the frontend and backend to fetch metadata and JWKS.hub.auth.issuer(https://sts.windows.net/<tenant-id>/) is the expectedissclaim used by the backend for token validation. This differs from the discovery URL, so you must set it explicitly.hub.auth.audience(api://<client-id>) must match theaudclaim in access tokens issued for your API.hub.auth.clientIdis the public client identifier of your SPA application.hub.auth.extraScopesmust include the API scope you created so the frontend requests an access token for your API. Bothhub.auth.issuerandhub.auth.extraScopesare required when you integrate with Microsoft Entra.
ForgeRock Guide🔗
Use this section to configure ForgeRock Access Management as the OIDC provider for the Hub.
ForgeRock setup (high level)🔗
- Create a public OAuth2/OIDC client for the Hub UI (SPA) Authorization Code flow with PKCE enabled. Redirect URI: the Hub base URL you serve the UI on. Enable refresh tokens if you want long-lived sessions (the UI uses silent renew by default).
- Ensure discovery works
Your issuer base must expose
/.well-known/openid-configuration. - Ensure the access token is a JWT and contains required claims
The Hub requires
iss,aud,exp, andemailin the access token. Verify by logging in once, decoding the access token, and confirming the claims match yourhub.auth.*values.
Example configuration:
hub:
auth:
enabled: true
providerType: "forgerock"
domain: "https://am.example.com/auth/oauth2"
audience: "<your-audience>"
clientId: "<spa-client-id>"
providerTypemust be set toforgerockto enable the Hub token exchange proxy and ForgeRock-specific login/logout behavior.issuermust match theissclaim in ForgeRock tokens, which is typically the base URL of your ForgeRock Access Management instance (for example,https://am.example.com/auth/oauth2), anddomainshould point to the same URL so the Hub can fetch discovery metadata.browserUrlneeds to be set if your ForgeRock instance is behind a proxy or ingress and the URL it serves to browsers differs from the internal URL the Hub backend uses for discovery and token validation.
Dex (Cluster OIDC Provider) Guide🔗
Dex can act as a central OIDC provider inside your Kubernetes cluster. This guide covers two common deployment scenarios.
Prerequisites🔗
Before configuring the Hub, gather the following information from your Dex deployment:
# 1. Find Dex's issuer URL (this MUST match what's in Dex config)
kubectl get cm dex -n <dex-namespace> -o yaml | grep issuer
# 2. Find Dex's client ID for the Hub
kubectl get cm dex -n <dex-namespace> -o yaml | grep -A 5 staticClients
# 3. Find Dex's internal service name
kubectl get svc -n <dex-namespace> | grep dex
Save these values - you'll need them for configuration.
Scenario 1: Dex with Same External URL (Simple)🔗
When to use: Dex is accessible at the same external URL from both the Hub backend and user browsers.
Example: Dex exposed at https://dex.company.com/dex via ingress, accessible from everywhere.
Configuration🔗
hub:
auth:
enabled: true
# External URL accessible from both Hub backend and browsers
domain: "https://dex.company.com/dex"
# Must EXACTLY match Dex's configured issuer
issuer: "https://dex.company.com/dex"
# Dex client ID - get from Dex staticClients.id
clientId: "https://hub.company.com/api"
# Must match clientId for Dex
audience: "https://hub.company.com/api"
Key points:
domainandissuerpoint to the same URLclientIdandaudiencemust be identical (Dex requirement)browserUrlnot needed - same URL for all
Scenario 2: Dex with Internal/External URLs (Common in Kubernetes)🔗
When to use: Hub backend reaches Dex via internal cluster DNS, but browsers use external ingress URL.
Example:
- Internal (Hub -> Dex):
http://dex.auth.svc.cluster.local:5556/dex - External (Browser -> Dex):
https://dex.company.com/dex
Why this is common: Kubernetes services are accessible internally via cluster DNS (faster, no TLS needed) but exposed externally via ingress for browsers.
Step 1: Verify Dex Configuration🔗
# Get Dex's issuer (external URL that appears in tokens)
kubectl get cm dex -n auth -o yaml | grep issuer
# Example output: issuer: https://dex.company.com/dex
# Get Dex's client ID
kubectl get cm dex -n auth -o yaml | grep -A 5 staticClients
# Example output: - id: https://hub.company.com/api
# Get Dex's internal service
kubectl get svc -n auth | grep dex
# Example output: dex ClusterIP 10.96.0.1 <none> 5556/TCP
Step 2: Configure Hub🔗
hub:
auth:
enabled: true
# Internal cluster DNS - Hub uses this to fetch discovery & validate tokens
domain: "http://dex.auth.svc.cluster.local:5556/dex"
# External URL - must EXACTLY match Dex's configured issuer
issuer: "https://dex.company.com/dex"
# External URL - Hub's discovery proxy rewrites URLs for the browser
browserUrl: "https://dex.company.com/dex"
# Dex client ID - get from Dex staticClients.id
clientId: "https://hub.company.com/api"
# Must match clientId for Dex
audience: "https://hub.company.com/api"
Critical: browserUrl is required!
- Without it, the browser tries to fetch discovery from the internal URL (fails with CORS)
- With it, Hub's proxy rewrites URLs so browser uses the external URL
Dex Setup Checklist🔗
When setting up Dex for the Hub:
- Deploy Dex behind ingress/gateway with stable external URL
- Set issuer in Dex config to EXACTLY match external URL (e.g.,
https://dex.company.com/dex) - Create static client with:
id: Use a URL likehttps://hub.company.com/apiredirectURIs: Add Hub's URLs:https://hub.company.comhttps://hub.company.com/login
public: truefor browser-based PKCE flow
- Configure connectors for user authentication
- Ensure claims include
email(required by Hub for user isolation)
Next Steps🔗
After configuring your identity provider, return to the Docker Deployment Guide or the Kubernetes Deployment Guide to finish the deployment that matches your environment.