BeyondTrust/POV

← Back to dashboard

OIDC and single sign-on

Audience: operator · Profile: both · Read this when: you want single sign-on for the dashboard instead of local passwords, or you are working out which of the dashboard's OIDC features you need.

This page sets up single sign-on for the dashboard. The dashboard speaks OIDC in several other places too, each with its own page:

Page Read it when
The dashboard's own identity you want the dashboard to reach AWS, Azure, GCP or a k3s cluster with a short-lived token instead of a stored key. Here the dashboard is the issuer. Its tokens come from the SPIRE server that Remote Agents also uses
Dex you want one issuer that both the dashboard and your on-prem clusters trust
Kubernetes federation with your IdP you want people signing in to managed clusters as themselves, with their IdP groups as RBAC subjects. One page per provider: Entra ID, Okta, PingOne, other OIDC
Identity providers you want to browse or change users and groups in Entra ID, Okta or PingOne, rather than sign in through one. That lives in the Directories section
Sign in with Microsoft (Entra OAuth) you already use the legacy Entra-only sign-in button, or are moving from it to the generic path below

What is it?

The OIDC integration lets people sign in to the dashboard with your existing identity provider (IdP) instead of a local username and password. It speaks generic OpenID Connect, driven entirely by the provider's discovery document (<issuer>/.well-known/openid-configuration), so one implementation covers any compliant IdP — you point it at an issuer, register a client, and it reads the authorize, token and JWKS endpoints from the provider itself. There is no per-provider code path to maintain.

Under the hood it uses the authorization-code flow with PKCE (S256, always sent), and every returned ID token has its signature verified against the provider's JWKS, along with the issuer and audience, before anyone is logged in.

OIDC login sits alongside the dashboard's other sign-in methods and doesn't replace them:

Scope. GitHub is not supported — it isn't an OIDC provider (no discovery document, no ID token). SAML is not supported either. This integration is OIDC only.


Supported providers

Any spec-compliant OIDC provider works. Verified/common ones:

Provider Notes
Keycloak Groups arrive in the groups claim (add a groups mapper to the client scope).
Authentik / Authelia groups claim.
Okta Group memberships often surface as roles rather than groups — set Groups claim accordingly.
Auth0 Groups/roles usually need a custom claim/action to be emitted.
Google Workspace Standard OIDC; Google does not emit groups in the ID token — group→workgroup mapping won't apply.
JumpCloud groups claim.
Ping groups claim (configurable).
GitLab Standard OIDC.
Microsoft Entra ID groups claim carries group object IDs, not display names — map by object ID (see below).

If your IdP publishes <issuer>/.well-known/openid-configuration and issues a standard ID token, it will work even if it isn't in this list.


Prerequisites

Requirement Notes
An OIDC identity provider Must expose a discovery document at <issuer>/.well-known/openid-configuration.
Ability to register an application/client with the IdP Confidential or public client — both work (PKCE is always sent).
A redirect (callback) URI you can register <dashboard-base-url>/api/auth/oauth/oidc/callback (exact value shown in the Settings panel).
The dashboard reachable at a stable base URL The base URL the browser uses must match the redirect URI you register.
Dashboard admin access The OIDC panel and group mappings are admin-only.

Setup

Step 1 — Register an application with your IdP

Create an OIDC application/client in your provider and note the client ID (and client secret, if you use a confidential client). Set the redirect / callback URI to:

<dashboard-base-url>/api/auth/oauth/oidc/callback

For example, https://dashboard.example.com/api/auth/oauth/oidc/callback. The OIDC settings panel prints the exact value derived from the dashboard's current origin — copy it from there to avoid typos. The URI must match exactly, including scheme, host and port.

A public client (no secret) is fine — PKCE protects the code exchange. Leave the client secret blank in that case.

If you want group-based access (recommended), make sure the application is configured to emit a groups (or roles) claim in the ID token. Note which claim name your provider uses.

Step 2 — Configure the dashboard

On a new install, the setup wizard's Admin step has a Single sign-on (OIDC) — optional panel with the first four fields below; the rest keep their defaults until you change them in Settings. Afterwards, or on an existing instance, go to Settings → Integrations → Single sign-on (OIDC) and fill in:

Field Config key Default Notes
Issuer URL oidc_issuer — Base URL only, e.g. https://keycloak.example.com/realms/main. /.well-known/openid-configuration is appended automatically.
Client ID oidc_client_id — From step 1.
Client secret oidc_client_secret — Optional — leave blank for a public client. Stored encrypted; shown as dots on reload. Leave the dots untouched to keep the stored value.
Button label oidc_provider_name issuer host Shown on the login page as "Sign in with …".
Scopes oidc_scopes openid profile email groups Space-separated. openid is always included even if you omit it.
Groups claim oidc_groups_claim groups The ID-token claim that carries the user's group names/IDs, used for workgroup mapping. Set to roles for Okta-style setups.

There is no separate "enable" toggle. SSO goes live the moment an issuer and client ID are saved, and turns off when you clear the issuer. Settings are stored encrypted in the application database and apply immediately — no .env edit and no restart.

Step 3 — Test the connection

Click Test connection in the panel. This fetches the live discovery document and reports the endpoints the provider advertises, its scopes_supported, and whether the groups claim you configured appears in the provider's claims_supported.

The groups-claim check is advisory. Many providers omit claims_supported entirely, and some emit groups without advertising it — so a "not advertised" result is not necessarily a problem. A typo'd issuer, on the other hand, fails here with the exact URL it couldn't reach.

Step 4 — Map groups to workgroups (authorization)

Authentication proves who someone is; group mappings decide what they can do. On the RBAC → Groups tab (/rbac#groups), add a mapping for each provider group:

On every SSO login the dashboard:

  1. Reads the user's group claims and matches them against your mappings.
  2. Assigns the matched workgroups and unions each matched group's default permissions into the user's session permissions (so removing someone from a group in the IdP drops the corresponding access on their next login).
  3. Auto-creates the user on first login if any group matched — no pre-registration needed.
  4. Rejects the login (/login?error=not_authorized) if the user is in none of the mapped groups.

If you configure no group mappings at all, the dashboard falls back to legacy behavior: the user must already exist as a local account, and SSO simply authenticates them.

For the permission model and how group-derived permissions combine with admin-granted ones, see the Entitle user-JIT design doc.


What it enables

Capability Description
SSO login button A "Sign in with <label>" button appears on the login page once configured.
Auto-provisioning Users in a mapped group are created automatically on first login.
Group-driven access Workgroups and permissions are derived from IdP group membership and re-synced on every login.
Permission enforcement Derived permissions feed the dashboard's scope/level checks (vms, aws, k8s, cloud_database, …).
Workload tokens Optional, separate from sign-in: the same (or another) IdP's access tokens for workloads, mapped to service accounts. Configured under Workload tokens in this panel; needs an audience, not a client id.

Dex (optional)

Dex can sit between the dashboard and your IdP, so that on-prem clusters trust the same issuer people sign in to the dashboard with. It is optional for the dashboard, and the only way people reach an on-prem (k3s) cluster. See Dex.

Provider quick reference

Issuer URL format per provider (paste the base into the Issuer URL field):

Provider Issuer URL example Groups claim
Keycloak https://keycloak.example.com/realms/<realm> groups
Okta https://<org>.okta.com groups or roles
Auth0 https://<tenant>.auth0.com/ custom (namespaced)
Google Workspace https://accounts.google.com none in ID token
Microsoft Entra ID https://login.microsoftonline.com/<tenant-id>/v2.0 groups (object IDs)
JumpCloud https://oauth.id.jumpcloud.com/ groups

Troubleshooting

"Could not fetch the OIDC discovery document from …" — the issuer URL is wrong or unreachable from the dashboard container. The error includes the exact URL it tried; open it in a browser. Use Test connection to confirm. Remember: enter the base issuer only — don't include /.well-known/openid-configuration yourself.

"ID token failed validation" — usually an audience or issuer mismatch, or clock skew. The client ID you configured must equal the token's aud. Confirm the dashboard host's clock is correct.

"Token response contained no id_token — is the 'openid' scope granted?" — the openid scope wasn't granted for this client. Ensure openid is in the app's allowed scopes at the IdP (the dashboard always requests it).

Redirect fails at the provider (redirect_uri mismatch) — the registered callback must match <dashboard-base-url>/api/auth/oauth/oidc/callback exactly, including scheme, host and port. Copy the value shown in the Settings panel.

error=not_authorized after login — the user isn't in any mapped group, or the IdP isn't actually emitting the groups claim. Check RBAC → Groups (/rbac#groups), confirm the app emits the claim (Entra sends group object IDs, so map by ID not name), and set the Groups claim field to match your provider (e.g. roles for Okta).

error=not_registered — no group mappings are configured, so login falls back to requiring a pre-existing local account and none matched this user. Add a group mapping, or create the local account first.

error=account_disabled — the matching dashboard account is deactivated. Re-enable it in user management.


Not to be confused with

This page is about dashboard login SSO. Several other features also speak OIDC and are configured elsewhere:

Feature What it is Where
Cluster RBAC from your IdP federating Kubernetes cluster RBAC to Entra, Okta, PingOne or another IdP via OIDC Kubernetes federation with your IdP
Workload tokens a CI job or service calls the dashboard's API with an access token its own IdP issued; no SSO client needed Service accounts → Tokens from your own IdP
SPIFFE workloads a workload presents a JWT-SVID at /api/oauth/token Service accounts → SPIFFE workloads
The dashboard as an issuer the dashboard publishes <issuer>/.well-known/openid-configuration so AWS, Azure, GCP and k3s can trust its own SPIFFE identity The dashboard's own identity
A Workload Lab trust domain as an issuer a SPIRE lab's OIDC Discovery Provider, which the dashboard and a k3s API server verify JWT-SVIDs against SPIFFE and SPIRE
Dex for on-prem kubectl the kubeconfig people download for a cloud=local cluster Dex, and Kubernetes