Archived ApherisFold documentation
This page is part of the archived ApherisFold and Hub documentation. For Apheris Foundry, see the Foundry overview.
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 Kubernetes Deployment Guide.
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.
Organization admission (opt-in)🔗
hub.auth.organizationCheckEnabled defaults to false, independently of
providerType. When enabled, set hub.auth.allowedOrganizations to a nonempty
list of exact organization IDs allowed for this deployment. A one-item list admits
a single organization. Auth must be enabled and each ID must be nonempty without
surrounding whitespace. Invalid settings fail chart and API validation.
The API and every socket connect/reconnect require a signed access token whose
https://sso.apheris.com/org_id is a string exactly equal to an allowed ID, before
user matching, profile writes or owner-room joins. Missing, empty, malformed and
different values fail authentication. Standard org_id, roles and azp do not
provide a fallback or exception, including for password-grant test users.
Existing RS256, exact issuer, Hub audience, subject and temporal checks remain.
Expiry is optional; temporal checks retain the existing 30-second tolerance.
This does not add continuous expiry or revocation checks to connected sockets.
The corresponding server environment variables are
APH_HUB_AUTH_ORGANIZATION_CHECK_ENABLED and APH_HUB_AUTH_ALLOWED_ORGANIZATIONS
(a JSON string array).
These settings add no CLI discovery fields.
Leave admission disabled for existing OIDC/ForgeRock deployments unless their
verified tokens deliberately follow this custom-claim contract. No-auth and lite
mode retain their existing access rules.
Migration identity preflight🔗
Before a migrated user's first live API or socket authentication, keep that user on hold and run the read-only preflight using the target Hub auth configuration and their recorded internal user ID. Obtain a controlled access token directly from the configured issuer without sending it to Hub's protected API or sockets. Use a read-only database account and the existing Hub database; the command does not start Hub, apply migrations, synchronize profiles, link identities or write any data.
This tool is for an authorized Hub migration operator. Building it requires a
checkout of the Hub source for the version being migrated, a Go toolchain
compatible with api/go.mod, and authenticated access to its private Go module
dependencies, including the foundry-toolkit module. Installing the Foundry CLI
application is not required. End users do not need to build or run this tool.
Run the tool from an environment that can reach the configured issuer's discovery and signing-key endpoints and the existing Hub PostgreSQL database. Arrange read-only database credentials and connectivity first, using an approved port forward if needed.
From api/ in that checkout, build the standalone operator tool:
go build -o /tmp/hub-auth-preflight ./cmd/auth-preflight
Supply APH_HUB_DATABASE_DSN through your normal secret mechanism and the target
APH_HUB_AUTH_ENABLED, APH_HUB_AUTH_DOMAIN, APH_HUB_AUTH_ISSUER (if different),
APH_HUB_AUTH_AUDIENCE, APH_HUB_AUTH_ORGANIZATION_CHECK_ENABLED and
APH_HUB_AUTH_ALLOWED_ORGANIZATIONS. Check the intended enabled organization
policy in the preflight environment before enabling it on the live deployment.
Pipe the access token to stdin from your approved token source, or redirect a
temporary, permission-restricted token file:
/tmp/hub-auth-preflight -user-id usr_RECORDED_ID < /secure/temporary-access-token
Never put tokens in command arguments, shell history, logs or evidence. Remove
temporary token files after use. The tool prints only a safe PASS or HOLD
outcome and exits nonzero on failure. It first verifies the signature and all
configured token/admission checks, then selects nonblank standard email before
https://sso.apheris.com/email, trims and lowercases it, and compares it with the
recorded user and the existing email lookup. It requires no new email_verified
claim. A new subject with the same selected email retains the existing user ID.
This is an operator rollout gate, not a persisted runtime allowlist: keep live access paused until every selected existing user passes. A changed email, missing user, different matched ID or read failure means hold that user's migration and resolve the mapping with the owner before allowing live login. Do not create a replacement account, rewrite ownership or disable admission to recover access. Record the prior user IDs, jobs and files and redacted preflight outcomes; after release, verify the same IDs and owner-only jobs/downloads/socket updates. Preflight does not itself grant access or modify existing data.
For the Auth0 migration, EN-6880 connection/Action wiring and verification of signed normal and test-user claims are rollout dependencies. Keep the chart default disabled until those checks and identity preflight pass. Local signed fixtures validate implementation, not live enterprise login acceptance. Preserve the existing issuer/client/audience and database/files; an issuer change requires the separate session transition described in the migration plan.
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 http://127.0.0.1:8765/callback
- If you use
apheris-foundry login, includehttp://127.0.0.1:8765/callbackin Allowed Callback URLs for the same Auth0 application, or register a separate public client for the CLI that targets the same API.
-
-
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. - If you want the ApherisFold CLI to log in against the same Entra app, also add the loopback callback
http://localhost:8765/callbackas an allowed redirect URI for that public client flow. - 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.
If you use
apheris-foundry login, also registerhttp://127.0.0.1:8765/callbackas an allowed redirect URI for the CLI loopback flow. 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/loginhttp://127.0.0.1:8765/callback
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 Kubernetes Deployment Guide to finish the deployment.