Skip to content

OAuth / OIDC Single Sign-On

PuppyGraph supports single sign-on (SSO) through any OpenID Connect (OIDC) identity provider (IdP), including Okta, Auth0, Google Workspace, Azure AD / Microsoft Entra ID, and Keycloak. With SSO enabled, users authenticate against your IdP and their PuppyGraph role can be assigned automatically from IdP groups (see Role-Based Access Control).

The login flow is the standard OIDC Authorization Code flow with PKCE: PuppyGraph redirects the browser to your IdP, exchanges the returned authorization code for an ID token, and verifies the token's signature, issuer, audience, and nonce. The configured SSO_CLAIM_AS_USER_ID claim becomes the user's identity (prefixed sso:); the configured groups claim drives RBAC role assignment.

Want a runnable demo first?

Two step-by-step tutorials walk through the flow against a local Keycloak so you can try it without a production identity provider:

The rest of this page is the configuration reference you'll need when wiring your own IdP.

Configuration

Set the following environment variables on the control plane (the node started with CONTROLPLANE=true, or the controlplane service in a multi-service compose file). SSO request handling lives only on the control plane. Leader and compute nodes do not need the SSO_* variables, but they still need the cluster-wide auth settings (RBAC_ENABLED, GREMLINSERVER_AUTHENTICATION_ENABLED, BOLTSERVER_AUTHENTICATION_ENABLED, AUTHENTICATION_JWT_SECRETKEY) so JWTs minted by the control plane validate on the query path.

Required variables

Variable Description
SSO_ENABLED Set to true to turn SSO on.
SSO_CLIENT_ID Client ID issued by your IdP.
SSO_CLIENT_SECRET Client secret issued by your IdP. Treat as a secret.
SSO_ISSUER The IdP's issuer URL (e.g. https://your-tenant.okta.com/oauth2/default). Used to verify the ID token's iss claim.
SSO_CALLBACK_URL The publicly reachable callback URL, e.g. https://puppy.example.com/sso_callback. Must exactly match the redirect URI registered with the IdP.

When registering PuppyGraph in your IdP, set the redirect URI to <your-puppygraph-base-url>/sso_callback, allow the authorization_code grant, and authorize the openid, profile, and email scopes (plus your provider's groups scope if you plan to use group mapping).

Endpoint configuration

PuppyGraph does not auto-discover endpoints from the OIDC discovery document. There are exactly two ways for PuppyGraph to know where the authorization and token endpoints are:

  1. Okta-style fallback. If SSO_URL and SSO_ACCESS_TOKEN_URL are unset, PuppyGraph constructs them from SSO_ISSUER using Okta's path convention:
  2. Authorization endpoint: <SSO_ISSUER>/v1/authorize
  3. Token endpoint: <SSO_ISSUER>/v1/token

This fallback works for Okta and other providers that happen to follow the same URL shape. It does not work for Auth0, Google, Azure AD, Keycloak, etc.

  1. Explicit URLs. For every other provider, set the endpoint URLs explicitly:
Variable Description
SSO_URL Authorization endpoint URL (where the browser is redirected to log in).
SSO_ACCESS_TOKEN_URL Token endpoint URL (used server-to-server to exchange the authorization code).
SSO_USERINFO_URL Userinfo endpoint URL. Optional; consulted only when SSO_CLAIM_AS_USER_ID is missing from the ID token. Other claims (including the groups claim used for role mapping) are read from the ID token, never from userinfo.
SSO_JWKS_URL JWKS endpoint URL for ID token signature verification. If unset, PuppyGraph falls back to the default Okta-style verifier driven by SSO_ISSUER and SSO_CLIENT_ID.

Look these URLs up in your IdP's admin console or its <issuer>/.well-known/openid-configuration document.

Optional variables

Variable Default Description
SSO_SCOPE openid profile email Space-separated OAuth scopes. Add your provider's groups scope (e.g. openid profile email groups) if you use group mapping.
SSO_CLAIM_AS_USER_ID email JWT claim that becomes the PuppyGraph user identifier. Typical values: email, preferred_username, sub.
SSO_GROUPS_CLAIM groups JWT claim containing the user's group memberships. Used for group-based role mapping.
SSO_SESSION_NAME sso_session Cookie name for the SSO session.
SSO_RESPONSE_TYPE code OIDC response type. Only code is supported.
SSO_HTTP_TIMEOUT 10s Timeout for outbound HTTP calls to the IdP.
SSO_JWKS_FETCH_TIMEOUT 10s Timeout for fetching the JWKS document.
SSO_JWKS_CACHE_TTL 5m How long PuppyGraph caches the JWKS document.

Provider recipes

The values below are templates; substitute your tenant ID, domain, and client credentials. For Keycloak, the Setting Up SSO with Keycloak tutorial is a fully worked example.

Okta

SSO_ENABLED=true
SSO_CLIENT_ID=0oa...example
SSO_CLIENT_SECRET=...
SSO_ISSUER=https://your-tenant.okta.com/oauth2/default
SSO_CALLBACK_URL=https://puppy.example.com/sso_callback
SSO_SCOPE="openid profile email groups"
SSO_CLAIM_AS_USER_ID=email
SSO_GROUPS_CLAIM=groups

Add a Groups claim to the Okta authorization server so the groups claim is included in the ID token. Endpoints are derived from the issuer automatically.

Auth0

SSO_ENABLED=true
SSO_CLIENT_ID=...
SSO_CLIENT_SECRET=...
SSO_ISSUER=https://your-tenant.auth0.com/
SSO_URL=https://your-tenant.auth0.com/authorize
SSO_ACCESS_TOKEN_URL=https://your-tenant.auth0.com/oauth/token
SSO_JWKS_URL=https://your-tenant.auth0.com/.well-known/jwks.json
SSO_CALLBACK_URL=https://puppy.example.com/sso_callback
SSO_CLAIM_AS_USER_ID=email

Auth0 does not include groups on the ID token by default. Add an Action or Rule that copies app_metadata.groups (or your custom claim) onto the ID token under the claim name you set in SSO_GROUPS_CLAIM.

Google Workspace

SSO_ENABLED=true
SSO_CLIENT_ID=...apps.googleusercontent.com
SSO_CLIENT_SECRET=...
SSO_ISSUER=https://accounts.google.com
SSO_URL=https://accounts.google.com/o/oauth2/v2/auth
SSO_ACCESS_TOKEN_URL=https://oauth2.googleapis.com/token
SSO_JWKS_URL=https://www.googleapis.com/oauth2/v3/certs
SSO_CALLBACK_URL=https://puppy.example.com/sso_callback
SSO_CLAIM_AS_USER_ID=email

Google does not emit a groups claim on the ID token. Use explicit role assignment per user in Settings → Users, or proxy groups through a custom claim provider.

Azure AD / Microsoft Entra ID

SSO_ENABLED=true
SSO_CLIENT_ID=...
SSO_CLIENT_SECRET=...
SSO_ISSUER=https://login.microsoftonline.com/<tenant-id>/v2.0
SSO_URL=https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize
SSO_ACCESS_TOKEN_URL=https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token
SSO_JWKS_URL=https://login.microsoftonline.com/<tenant-id>/discovery/v2.0/keys
SSO_CALLBACK_URL=https://puppy.example.com/sso_callback
SSO_CLAIM_AS_USER_ID=preferred_username
SSO_GROUPS_CLAIM=groups

In the Azure AD application registration, configure the optional groups claim under Token configuration so group object IDs (or names) appear on the ID token.

Relationship to RBAC

SSO governs authentication (who you are). Role-Based Access Control governs authorization (what you can do). After a successful SSO login, the user's role is determined in this order:

  1. Explicit role assignment by an admin in Settings → Users.
  2. Group mapping based on the IdP's groups claim (see Configuring SSO group mappings).
  3. Default role from RBAC_DEFAULT_ROLE (defaults to Analyst).

SSO users always appear in the Users tab with a sso: prefix on their internal identifier so they cannot collide with local users.

Troubleshooting

Symptom Likely cause What to check
Browser shows redirect_uri_mismatch The IdP's registered redirect URI does not match SSO_CALLBACK_URL exactly. Confirm the scheme, host, port, and path match. The path must be /sso_callback.
SSO authorization endpoint is not configured error SSO_ISSUER is empty and SSO_URL is not set. Set SSO_ISSUER for issuers with the Okta URL convention, or set SSO_URL explicitly.
invalid issuer: … after IdP redirect The iss claim in the ID token does not match SSO_ISSUER. Inspect the ID token at the IdP and copy the exact issuer value (trailing slash matters for some providers).
Token verification fails Signing key cannot be found. Set SSO_JWKS_URL to your IdP's JWKS endpoint. Confirm the JWKS document is reachable from PuppyGraph.
Users log in but always get the default role Groups claim missing from the ID token or under a different name. PuppyGraph reads groups from the ID token claims (not from the userinfo endpoint). Confirm the IdP includes the groups claim on the ID token and that SSO_GROUPS_CLAIM matches the claim name.
Users log in with the wrong identifier (e.g. an opaque sub) SSO_CLAIM_AS_USER_ID does not match the claim you want as the username. Set SSO_CLAIM_AS_USER_ID to email, preferred_username, or whichever claim your IdP exposes for end-user identity.