Skip to main content

OIDC Configuration

Symphony uses OpenID Connect (OIDC) for all user authentication. If you are using the bundled Keycloak (available in the Docker Compose and Kubernetes installations), OIDC is pre-configured and you can skip this guide.

This page explains what Symphony requires from an external OIDC provider and how to configure specific providers. You will enter your OIDC issuer URL and client IDs in the setup wizard when Symphony starts for the first time. The wizard includes a Validate Configuration button that checks your settings against the provider before you submit—use it to catch errors early.

tip

You do not need to set OIDC environment variables or Helm values in advance. The setup wizard provides all the fields you need, and its built-in validation gives immediate feedback on configuration errors. Environment variables and Helm values exist primarily for automated or scripted deployments.

Requirements

Symphony requires the following from any OIDC provider:

  • OIDC Discovery—the provider must serve a /.well-known/openid-configuration document at its issuer URL
  • Authorization Code flow with PKCE—both the UI and the CLI use the authorization code grant with Proof Key for Code Exchange (S256 challenge method)
  • RS256 token signing—JSON Web Tokens must be signed with RS256
  • Scopes—the provider must support: openid, profile, email, and offline_access
  • Localhost redirect URIs—the API client must allow http://localhost redirect URIs for CLI login (see the API client row in Client Registrations)

Client Registrations

Symphony requires two client registrations in your OIDC provider:

ClientEnvironment VariableTypeGrant TypePurpose
UI clientOIDC_UICLIENTIDPublic (SPA)Authorization Code + PKCEBrowser-based single-page application
API clientOIDC_CLIENTIDPublicAuthorization Code + PKCE / Token validationCLI login (cirata login) and backend API

UI Client Settings

  • Client type: Public (no client secret)
  • Grant type: Authorization Code
  • PKCE: Required, S256 challenge method
  • Redirect URIs:
    • https://<hostname>—main login callback (no trailing slash)
    • https://<hostname>/silent-renew.html—silent token renewal
  • Post-logout redirect URI: https://<hostname> (no trailing slash)
  • Trusted origin / Web origin: https://<hostname>
  • Scopes: openid profile email offline_access
warning

Register these URIs exactly as shown. Some providers (notably Okta) match redirect URIs byte-for-byte—a trailing slash or missing path causes sign-in to fail with a redirect_uri error even though validation passed. The UI sends the browser's origin (https://<hostname>, no trailing slash), not https://<hostname>/.

API Client Settings (CLI Login)

The cirata login command authenticates by opening the user's browser to the OIDC provider, then receiving the authorization code on a temporary local HTTP server. This requires the API client to be configured as follows:

  • Client type: Public (the CLI cannot securely store a client secret)
  • Grant type: Authorization Code with PKCE (S256)
  • Redirect URIs: http://localhost:* or http://localhost with wildcard port support. The CLI starts a temporary server on a random port (e.g. http://localhost:50437/callback) to receive the callback. Your OIDC provider must allow localhost redirect URIs for this client.
  • No client secret needed (public PKCE client)
warning

If http://localhost is not configured as an allowed redirect URI for the API client, cirata login will fail. The OIDC provider will show an "Invalid redirect_uri" error in the browser, and the CLI will time out waiting for a callback. See the provider-specific guides below for the exact setting in each provider.

As a workaround, users can create an API key manually in the Symphony UI and log in with:

cirata login --address <hostname> --token <paste-token>

After authentication, the CLI uses the OIDC access token to create a Symphony API key (POST /api/v1/keys). Subsequent CLI commands use that API key, not the OIDC token. The API client does not need additional server-side configuration beyond what is listed in this section.

Client Secrets

Some OIDC providers require a client secret for confidential clients. Symphony supports optional client secrets for both the API client (OIDC_CLIENTSECRET) and the UI client (OIDC_UICLIENTSECRET).

Client secrets are required when:

  • Your provider requires client authentication for any token endpoint interaction

Client secrets are not needed when:

  • The UI client is a public (SPA) client—most providers do not issue secrets for public clients
  • The API client is a public (PKCE) client—used by the CLI for browser-based login
  • Your provider is Keycloak with a public client configuration

When a client secret is provided, the setup wizard validates it against the identity provider during the Validate Configuration step. Client secrets are stored in the Symphony configuration file and are never exposed through the API.

Audience

The UI sends audience=https://<hostname>/api/v1 as a query parameter during authorization. Symphony accepts tokens with any of these aud values:

  • https://<hostname>/api/v1—the explicit API audience
  • The API client ID (OIDC_CLIENTID)
  • The UI client ID (OIDC_UICLIENTID)
  • account—Keycloak default

Most providers require explicit audience configuration:

ProviderWhere to Configure
KeycloakNo action needed—Keycloak sets aud to the client ID by default
Auth0Create an API with identifier https://<hostname>/api/v1
OktaSet the Audience field on the authorization server to https://<hostname>/api/v1
Entra IDSet the Application ID URI under "Expose an API" to https://<hostname>/api/v1
GoogleNo custom audience—tokens use the client ID as the audience

Issuer URL Format

The OIDC_ISSUER value must be the domain and path only, without the https:// prefix. Symphony prepends https:// automatically.

# Correct
OIDC_ISSUER=auth.example.com/realms/symphony

# Incorrect — do not include the scheme
OIDC_ISSUER=https://auth.example.com/realms/symphony

Issuer URL Quick Reference

ProviderOIDC_ISSUER FormatExample
Keycloak<host>/realms/<realm>auth.example.com/realms/symphony
Auth0<tenant>.auth0.commycompany.auth0.com
Okta<org>.okta.com/oauth2/<auth-server>mycompany.okta.com/oauth2/default
Microsoft Entra IDlogin.microsoftonline.com/<tenant-id>/v2.0login.microsoftonline.com/abc123-def456/v2.0
Google Workspaceaccounts.google.comaccounts.google.com

Provider-Specific Guides

Keycloak

Keycloak organises clients within realms. Create a realm (e.g. symphony) and register both clients inside it.

Issuer: <keycloak-host>/realms/<realm-name>

1. Create the UI Client

  1. Go to Clients → Create client
  2. Set Client ID to your chosen name (e.g. symphony-ui)
  3. Set Client type to Public
  4. Enable Standard flow (authorization code)
  5. Under Settings:
    • Valid redirect URIs: https://<hostname>/*
    • Valid post logout redirect URIs: https://<hostname>/*
    • Web origins: https://<hostname>
  6. Under Advanced → Advanced Settings:
    • Set Proof Key for Code Exchange Code Challenge Method to S256

2. Create the API Client

  1. Go to Clients → Create client
  2. Set Client ID to your chosen name (e.g. symphony-api)
  3. Set Client type to Public
  4. Enable Standard flow (authorization code)—the CLI uses this for browser-based PKCE login
  5. Under Settings, add Valid redirect URIs: http://localhost:* (for CLI login)
  6. Under Advanced → Advanced Settings, set Proof Key for Code Exchange Code Challenge Method to S256

3. Configure Group Claims

To use Symphony's role-based access control, configure group or role claims in the token. See RBAC Configuration for detailed instructions.


Auth0

Auth0 uses Applications for client registrations and APIs for audience configuration.

Issuer: <tenant>.auth0.com/ (note the trailing slash—Auth0 requires it)

1. Create the UI Application

  1. Go to Applications → Create Application
  2. Choose Single Page Application
  3. Set Name to your chosen name (e.g. Symphony UI)
  4. Under Settings:
    • Allowed Callback URLs: https://<hostname>, https://<hostname>/silent-renew.html
    • Allowed Logout URLs: https://<hostname>
    • Allowed Web Origins: https://<hostname>
  5. Note the Client ID—this is your OIDC_UICLIENTID

2. Create the API

  1. Go to APIs → Create API
  2. Set Identifier (Audience) to https://<hostname>/api/v1
  3. Set Signing Algorithm to RS256
  4. Note the Identifier—this is the audience value

3. Create the API Application

  1. Go to Applications → Create Application
  2. Choose Single Page Application or Native (both support PKCE natively)
  3. Set Name (e.g. Symphony API)
  4. Under Settings:
    • Allowed Callback URLs: http://localhost:*, https://<hostname>/oauth/login (localhost is used for CLI login)
  5. Authorise it for the API you created in step 2
  6. Note the Client ID—this is your OIDC_CLIENTID

Auth0 Notes

  • Auth0 namespaces custom claims. If you configure group claims for RBAC, they will appear as https://your-namespace/groups in the token. See RBAC Configuration for how to configure the groups claim name in Symphony.
  • The trailing slash in the issuer URL is required. Auth0 includes it in the iss claim, and Symphony must match it exactly.

Okta

Okta uses Applications for client registrations and Authorization Servers for audience and scope configuration.

Issuer: <org>.okta.com/oauth2/<authorization-server-id>

Use the default authorization server or create a custom one.

1. Create the UI Application

  1. Go to Applications → Create App Integration
  2. Choose OIDC—OpenID Connect, then Single-Page Application
  3. Set App integration name (e.g. Symphony UI)
  4. Under Sign-in redirect URIs:
    • https://<hostname> (no trailing slash)
    • https://<hostname>/silent-renew.html
  5. Under Sign-out redirect URIs: https://<hostname> (no trailing slash)
  6. Under Trusted Origins: add https://<hostname>
  7. Note the Client ID—this is your OIDC_UICLIENTID

2. Create the API Application

  1. Go to Applications → Create App Integration
  2. Choose OIDC—OpenID Connect, then Native Application (supports PKCE with localhost redirect)
  3. Set App integration name (e.g. Symphony API)
  4. Under Sign-in redirect URIs: http://localhost:*, https://<hostname>/oauth/login (localhost is used for CLI login)
  5. Note the Client ID—this is your OIDC_CLIENTID

3. Configure the Authorization Server

  1. Go to Security → API → Authorization Servers
  2. Select your authorization server (e.g. default) or create a custom one
  3. Under Settings, set the Audience to https://<hostname>/api/v1
  4. Under Scopes, verify that openid, profile, email, and offline_access are available
  5. Under Access Policies, ensure a policy and rule allow the Authorization Code grant type for the scopes above

4. Assign Users to the Applications

  1. Open each application (UI and API) in Applications
  2. Go to the Assignments tab
  3. Click Assign and add the users or groups that should have access
warning

If users are not assigned to the application, Okta will return a 400 "User is not assigned to the client application" error after login. You can assign the Everyone group for broad access.

Okta Notes

  • You must use a custom authorization server (e.g. default), not the org authorization server. The org authorization server (<org>.okta.com without /oauth2/...) issues opaque access tokens that Symphony cannot validate. The issuer URL must be <org>.okta.com/oauth2/default (or your custom server name).
  • Okta matches redirect URIs byte-for-byte. Unlike Auth0 or Entra ID, Okta does not normalize trailing slashes or paths. Register https://<hostname> (no trailing slash) for sign-in and sign-out, not https://<hostname>/—otherwise sign-in fails with a redirect_uri error even though validation passes. Use http://localhost:* for the Native Application so any ephemeral port chosen by cirata login is accepted.
  • The Audience field on the authorization server must match https://<hostname>/api/v1. Without this, Okta issues tokens with a different audience and Symphony will reject them with a 401 error.
  • Ensure the authorization server's Access Policies include a rule that allows the Authorization Code grant type. Without this, Okta returns a "Policy evaluation failed" error after login.

Microsoft Entra ID (Azure AD)

Entra ID uses App registrations for client configuration.

Issuer: login.microsoftonline.com/<tenant-id>/v2.0

1. Create the App Registration

  1. Go to Microsoft Entra ID → App registrations → New registration
  2. Set Name (e.g. Symphony)
  3. Under Supported account types, choose your tenant model
  4. Click Register

2. Configure the UI Platform

  1. Go to Authentication → Add a platform → Single-page application
  2. Set Redirect URIs:
    • https://<hostname> (no trailing slash)
    • https://<hostname>/silent-renew.html
  3. Check Access tokens and ID tokens under Implicit grant
  4. Note the Application (client) ID—this is your OIDC_UICLIENTID

3. Configure the API Audience

  1. Go to Expose an API on the app registration
  2. Set Application ID URI to https://<hostname>/api/v1—this becomes the aud claim in access tokens and must match what Symphony expects
  3. Add a scope (e.g. access_as_user)

4. Create a Second App Registration for the API Client

  1. Create another app registration (e.g. Symphony API)
  2. Under Authentication → Add a platform, choose Mobile and desktop applications (supports PKCE with localhost redirect)
  3. Add Redirect URI: http://localhost (Entra ID handles port wildcards differently—http://localhost matches any port)
  4. Note the Application (client) ID—this is your OIDC_CLIENTID
  5. Under API permissions, add the scope from step 3

Entra ID Notes

  • The tenant ID is found on the Overview page of your Entra ID tenant.
  • Entra ID v2.0 tokens use the ver: "2.0" claim. Ensure you use the /v2.0 issuer endpoint.
  • The Application ID URI set in "Expose an API" becomes the aud claim in access tokens. It must be set to https://<hostname>/api/v1 or Symphony will reject tokens with an audience mismatch error.
  • For group claims, configure Token configuration → Add groups claim in the app registration. See RBAC Configuration for details.

Google Workspace

Issuer: accounts.google.com

Google can be used for authentication, but it does not natively support group claims in OIDC tokens. This means RBAC group mappings will not work out of the box.

1. Create OAuth Credentials

  1. Go to the Google Cloud Console
  2. Navigate to APIs & Services → Credentials → Create Credentials → OAuth client ID
  3. Choose Web application
  4. Set Authorized redirect URIs:
    • https://<hostname> (no trailing slash)
    • https://<hostname>/silent-renew.html
    • https://<hostname>/oauth/login
    • http://localhost:* (for CLI PKCE login)
  5. Note the Client ID values

Google Notes

  • Google does not support custom audience values. Tokens will contain Google's own client ID as the audience.
  • Google does not include group claims in tokens. For RBAC, you will need to use individual user role assignments instead of group mappings. See RBAC Configuration for workarounds.
  • The offline_access scope is not supported by Google. Use access_type=offline as a parameter instead, though silent renewal may behave differently.

Troubleshooting

Using the Setup Wizard Validation

The setup wizard includes a Validate Configuration button that checks your DNS hostname and OIDC settings before you submit. Click the button after filling in all OIDC fields. The validation contacts the provider's discovery endpoint and token endpoint to verify that the discovery document is reachable, required endpoints are present, signing algorithms and scopes are supported, client IDs are recognized, and (when provided) client secrets are valid.

Each check reports pass, info, warning, or fail, along with a description of the issue and instructions for resolving it. Errors should be fixed before proceeding. Warnings should be investigated but do not block setup. After editing any field the result is cleared and you should re-validate.

Additional features:

  • Copy diagnostics: A clipboard icon appears after validation. Click it to copy all check results (including passes) for sharing with support.
  • Disable TLS Verification: A switch next to the OIDC Issuer field disables TLS certificate verification for providers with self-signed or private CA certificates. This setting is saved to the configuration. For production, use OIDC_CA_CERT instead.
  • Comprehensive logging: All validation checks are logged to the container logs during initial setup, including full diagnostic detail and resolution guidance.

For a detailed description of every check, what causes it to fail, and step-by-step resolution instructions, see Setup Wizard Validation.

Issuer URL Format Errors

The most common mistake is including https:// in the OIDC_ISSUER value. Symphony adds the scheme automatically:

# Wrong
OIDC_ISSUER=https://auth.example.com/realms/symphony

# Right
OIDC_ISSUER=auth.example.com/realms/symphony

Trailing slashes on OIDC_ISSUER are normalized automatically—you do not need to worry about whether to include one. This normalization applies only to the issuer URL; redirect URIs are not normalized and must match what the provider has registered character-for-character (see Redirect URI Mismatch).

Discovery Endpoint Not Reachable

Verify that Symphony can reach the OIDC discovery endpoint:

curl -s https://<OIDC_ISSUER>/.well-known/openid-configuration | jq .

If the provider uses a different internal URL (e.g. in Docker or Kubernetes networks), set OIDC_INTERNAL_BASE_URL to the URL reachable from the Symphony server.

Redirect URI Mismatch

OIDC providers strictly validate redirect URIs. Unlike the OIDC_ISSUER value, redirect URIs are not normalized—trailing slashes and paths must match exactly on providers like Okta. Ensure the URIs registered in your provider match what Symphony uses character-for-character:

UI client (SPA):

  • Sign-in: https://<EXTERNAL_HOSTNAME> (no trailing slash)
  • Silent renewal: https://<EXTERNAL_HOSTNAME>/silent-renew.html
  • Post-logout: https://<EXTERNAL_HOSTNAME> (no trailing slash)
  • Trusted origin / Web origin: https://<EXTERNAL_HOSTNAME>

API client (backend + CLI):

  • Web sign-in: https://<EXTERNAL_HOSTNAME>/oauth/login
  • CLI PKCE callback: http://localhost:<port>/callbackcirata login listens on 127.0.0.1 on an ephemeral port chosen by the OS, so the port varies per login attempt. Register http://localhost:* (Okta, Auth0), http://localhost (Entra ID—matches any port), or the equivalent wildcard for your provider. Where the provider does not allow wildcard loopback redirects, users can authenticate with an API key instead (cirata login --token <api-key>).

The setup wizard's Validate Configuration button lists these URIs for your current EXTERNAL_HOSTNAME in the "Additional items to verify" advisory after validation succeeds—copy them directly into your provider.

Common issues:

  • Trailing slash on https://<EXTERNAL_HOSTNAME> that does not match the browser origin (the UI sends window.location.origin, no slash)
  • Registering http://localhost without the port wildcard on providers that require explicit port matching
  • Using http:// instead of https:// for the public hostname
  • Hostname mismatch between EXTERNAL_HOSTNAME and the registered URI

Audience Mismatch / 401 After Login

If you see 401 errors on /api/v1/usertoken after a successful login redirect, the most likely cause is an audience mismatch—the access token's aud claim does not match what Symphony expects. The accepted values are:

  1. https://<EXTERNAL_HOSTNAME>/api/v1
  2. The OIDC_CLIENTID value
  3. The OIDC_UICLIENTID value
  4. account

Another common cause is opaque (non-JWT) access tokens. Some providers issue opaque tokens when no API audience is registered, and Symphony cannot validate these.

Provider-specific fixes:

  • Keycloak: the client ID is used as the audience by default—no additional configuration is needed.
  • Auth0: create an API in Applications → APIs with identifier https://<hostname>/api/v1.
  • Okta: set the Audience field on your custom authorization server (Security → API → your server → Settings) to https://<hostname>/api/v1. You must use a custom authorization server (e.g. default)—the org authorization server issues opaque tokens.
  • Entra ID: set the Application ID URI under "Expose an API" on your app registration to https://<hostname>/api/v1.
  • Google: Google does not support custom audiences. Symphony accepts the client ID as the audience.

CORS Issues

The browser must be able to reach the OIDC provider directly for the authorization code flow. If you see CORS errors in the browser console:

  • Verify that https://<EXTERNAL_HOSTNAME> is listed as an allowed origin (or "Web Origin" / "Trusted Origin") in your OIDC provider's client settings
  • Check that the provider's CORS configuration allows the /.well-known/openid-configuration and token endpoints

Clock Skew

Symphony allows up to 1 minute of clock skew when validating token timestamps. If you see token expiration errors, verify that the clocks on your Symphony server and OIDC provider are synchronised (e.g. using NTP).

Keycloak Login Error Events During Validation

When running the setup wizard validation with Keycloak, you may see CLIENT_LOGIN_ERROR events in Keycloak's event log:

type="CLIENT_LOGIN_ERROR", clientId="symphony-api", error="invalid_client_credentials"
type="CLIENT_LOGIN_ERROR", clientId="symphony-ui", error="invalid_client",
reason="Public client not allowed to retrieve service account"

These are expected and harmless. The validation probe sends a client_credentials grant to verify that client IDs are recognized by the provider. Keycloak logs a warning for each probe because:

  • Confidential clients (like symphony-api) receive a probe without the correct secret, triggering invalid_client_credentials.
  • Public clients (like symphony-ui) cannot use the client_credentials grant type at all, triggering invalid_client.

These events do not indicate a misconfiguration. They only appear during validation and do not recur during normal operation.

See Also