Skip to content

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-configuration so 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.issuer when set, otherwise hub.auth.domain)
    • aud (audience): Must include the value configured as the Hub audience (hub.auth.audience) so tokens are intended for the Hub API
    • email: Used for user identification and data segregation, so it must be present in the access token
    • exp: Token expiration timestamp
  • Optional but recommended claims (used for a better user experience in the UI):
    • name
    • given_name
    • family_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 other hub.auth.* fields are ignored by the backend.
  • When enabled: true, the Hub requires at least domain, audience and clientId to 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
  • If enabled: true and domain is 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), domain is sufficient; for providers where they differ (e.g., Entra), also configure issuer (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>
  • The backend validates that this value is present and has a URL scheme when auth is enabled. If audience is empty while enabled: 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 issuer is empty, the backend uses domain as 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.issuer to the exact issuer in the token (for example, https://sts.windows.net/<tenant-id>/) while keeping domain as the discovery URL.
  • If you set issuer, it must be a valid URL with a scheme; otherwise the backend treats the configuration as invalid.
  • issuer must exactly match the token iss claim, 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 audience is 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 issuer and rewrites its URLs so the browser talks to the browserUrl host that it can reach.
    • Typical use case: an internal IdP with issuer https://idp.internal.svc.cluster.local/ and an external proxy or ingress at https://sso.example.com; set issuer to the internal URL and browserUrl to the public one.
  • For Auth0 and Entra, where discovery uses public HTTPS endpoints reachable from the browser, leave browserUrl empty 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, and forgerock.
  • 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 forces prompt=login on 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 an end_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.yaml
    hub:
      auth:
        enabled: true
        domain: ""
        audience: ""
        clientId: ""
    
  • When enabled: true and these fields are left empty, the script injects demo Auth0 domain, audience, and clientId values 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🔗

  1. 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.domain and hub.auth.clientId).
  2. 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
      
  3. Create the API

    • Navigate to Applications → APIs → Create API.
    • Set the name (e.g., ApherisFold API) and use a URL-like identifier such as https://fold.apheris.com/api.
    • Keep the signing algorithm at RS256.
  4. 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.
  5. 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.
  6. Claims
    • Auth0 already includes email claim in both ID and access tokens, so no extra configuration is required.

Example configuration:

config.yaml
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🔗

  1. 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.
  2. 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.
  3. 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 your hub.auth.audience. Click Save and continue.
    • Set a Scope name (for example, access) so the resulting full scope is api://<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.
  4. 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_name and family_name claims for a better user experience in the UI.
    • Confirm by granting consent and clicking Add.

Hub configuration🔗

On the Overview tab of your registered application, note the following values:

  • Application (client) ID: use this for hub.auth.clientId.
  • Directory (tenant) ID: use this to build the hub.auth.domain and hub.auth.issuer URLs.
  • Application ID URI: use this for hub.auth.audience.
  • When you click on Application ID URI, you can see the full scope name you created earlier; use this for hub.auth.extraScopes (for example, api://<client-id>/access).

Example configuration:

config.yaml
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 expected iss claim 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 the aud claim in access tokens issued for your API.
  • hub.auth.clientId is the public client identifier of your SPA application.
  • hub.auth.extraScopes must include the API scope you created so the frontend requests an access token for your API. Both hub.auth.issuer and hub.auth.extraScopes are 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)🔗

  1. 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).
  2. Ensure discovery works Your issuer base must expose /.well-known/openid-configuration.
  3. Ensure the access token is a JWT and contains required claims The Hub requires iss, aud, exp, and email in the access token. Verify by logging in once, decoding the access token, and confirming the claims match your hub.auth.* values.

Example configuration:

config.yaml
hub:
  auth:
    enabled: true
    providerType: "forgerock"
    domain: "https://am.example.com/auth/oauth2"
    audience: "<your-audience>"
    clientId: "<spa-client-id>"
  • providerType must be set to forgerock to enable the Hub token exchange proxy and ForgeRock-specific login/logout behavior.
  • issuer must match the iss claim in ForgeRock tokens, which is typically the base URL of your ForgeRock Access Management instance (for example, https://am.example.com/auth/oauth2), and domain should point to the same URL so the Hub can fetch discovery metadata.
  • browserUrl needs 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:

  • domain and issuer point to the same URL
  • clientId and audience must be identical (Dex requirement)
  • browserUrl not 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:

  1. Deploy Dex behind ingress/gateway with stable external URL
  2. Set issuer in Dex config to EXACTLY match external URL (e.g., https://dex.company.com/dex)
  3. Create static client with:
    • id: Use a URL like https://hub.company.com/api
    • redirectURIs: Add Hub's URLs:
      • https://hub.company.com
      • https://hub.company.com/login
    • public: true for browser-based PKCE flow
  4. Configure connectors for user authentication
  5. 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.