Single Sign On
RunWhen supports enterprise single sign-on (SSO) in two deployment models:
| Model | Identity flow | Who configures the IdP |
|---|---|---|
| RunWhen SaaS | Your corporate IdP federates to RunWhen’s Auth0 tenant; users sign in at app.runwhen.com | RunWhen provisions the Auth0 connection; you configure your IdP with the SP details RunWhen provides |
| Self-hosted | PAPI acts as the OAuth 2.1 entry point and trusts your OIDC issuer directly (Keycloak, Okta, Entra ID, Authelia, etc.) | You create an OIDC client in your IdP and wire credentials into the runwhen-platform Helm chart |
Email/password login and optional direct social OAuth (Google, GitHub, GitLab) are also available on both models. Self-hosted installs can run with zero external identity dependencies when email/password (or an internal IdP) is sufficient.
RunWhen SaaS — enterprise SSO
RunWhen SaaS uses Auth0 as the enterprise identity broker. RunWhen provisions a dedicated Auth0 connection for your organization and walks you through IdP-side configuration.
Get started
Contact support@runwhen.com to request SSO onboarding. Include:
- Your identity provider (Okta, Entra ID / Azure AD, Google Workspace, Ping, OneLogin, custom SAML/OIDC, etc.)
- Email domains that should map to the connection
- Whether you need just-in-time (JIT) user provisioning or explicit allowlists
RunWhen returns a connection name (for example acme-corp) and the exact values your IdP admin must enter.
SAML federation (most enterprise IdPs)
Register RunWhen’s Auth0 service provider in your IdP using the connection name RunWhen assigns:
| Setting | Value |
|---|---|
| ACS URL / Reply URL / Sign-in URL | https://auth.runwhen.com/login/callback?connection=<connection-name> |
| Entity ID / Audience / SP Entity ID | urn:auth0:runwhen:<connection-name> |
| NameID format | Email address (RunWhen keys users by email) |
Replace <connection-name> with the identifier RunWhen gives you (for example acme-corp).
Additional SAML attributes, group mappings, and signing certificate rotation are handled during onboarding — do not guess attribute names; use the mapping RunWhen provides for your IdP.
OIDC federation (when your IdP speaks OIDC to Auth0)
Some IdPs connect to Auth0 over OIDC instead of SAML. Redirect URI, issuer, and client metadata depend on the Auth0 connection type. RunWhen supplies the exact redirect URI and scopes during onboarding — do not point your IdP at app.runwhen.com; all browser SSO flows terminate at Auth0 first.
Google Workspace
Google Workspace SSO runs through Auth0. Your Google Cloud admin enables the Admin SDK API, creates a Web application OAuth client with redirect URI https://auth.runwhen.com/login/callback, and shares the client ID and secret with RunWhen support.
Step-by-step screenshots and field values: Google Workspace SSO setup.
After SSO is live
- Users with matching email domains see Enterprise SSO on the login page and enter their organization identifier (the connection name or alias RunWhen configured).
- New users are provisioned on first successful login unless your contract specifies otherwise.
- For workspace access control after login, see User Management.
Self-hosted — SSO overview
Self-hosted deployments run PAPI at https://papi.<domain> (where <domain> is your global.domain Helm value). PAPI is both the platform API and the OAuth 2.1 authorization server for the web UI, MCP server, and other OAuth clients.
Recommended enterprise pattern for self-hosted:
- Deploy an internal OIDC issuer you already operate (Keycloak, Okta private cloud, Entra ID app registration, Authelia, etc.).
- Register a confidential OIDC client whose redirect URI targets PAPI (see IdP redirect URIs).
- Supply the client credentials to the Helm chart via
OIDC_IDP_CONFIG(see Helm configuration). - Optionally enable
staffAccessso SSO users in your domain receive platform staff privileges.
Auth0 is not required for self-hosted. You may optionally point PAPI at an Auth0 tenant the same way RunWhen SaaS does, but most air-gapped and enterprise installs use a direct upstream OIDC issuer instead.
See Self-Hosted Deployment Requirements for cluster, DNS, TLS, and registry prerequisites before configuring SSO.
Authentication options (self-hosted)
| Option | Helm / config | IdP must be reachable from | Best for |
|---|---|---|---|
| Direct upstream OIDC (recommended) | secrets.values.oidcIdpConfig → OIDC_IDP_CONFIG | Browser (login) + PAPI pods (discovery, token exchange) | Enterprise SSO, air-gap with internal Keycloak |
| Auth0 broker (optional) | secrets.values.auth0Url + auth0ClientId + auth0ClientSecret | Browser + PAPI pods to *.auth0.com | Teams already standardized on Auth0 |
| Direct social OAuth | googleClientId / githubClientId / gitlabClientId (+ secrets) | Browser + PAPI pods to the provider | Dev/lab; not a substitute for corporate SSO |
| Email / password | Always available; papi.skipEmailVerification: "true" for lab installs without SMTP | n/a | Bootstrap, break-glass, small teams |
For air-gapped networks, only local email/password and internal OIDC issuers are viable — public IdPs (Auth0, Google, GitHub) are unreachable. Plan SSO during the air-gap review in your onboarding pack (see Self-Hosted Deployment Requirements — Network summary).
IdP redirect URIs (self-hosted)
Register these exact redirect / callback URIs in your OIDC application. Replace <domain> with global.domain and <name> with the name field from your oidcIdpConfig entry.
| Login method | Redirect URI to register in your IdP |
|---|---|
| Upstream OIDC (Keycloak, Okta, Entra, …) | https://papi.<domain>/oauth/authorize/callback/<name> |
| Auth0 broker (optional) | https://papi.<domain>/accounts/auth0/login/callback/ |
| Google (direct social) | https://papi.<domain>/accounts/google/login/callback/ |
| GitHub (direct social) | https://papi.<domain>/accounts/github/login/callback/ |
| GitLab (direct social) | https://papi.<domain>/accounts/gitlab/login/callback/ |
Typical OIDC client settings
| Setting | Recommended value |
|---|---|
| Client type | Confidential (client secret required) |
| Grant types | Authorization code |
| PKCE | Supported by PAPI for Auth0 and social flows; follow your IdP’s defaults for upstream OIDC |
| Scopes | openid profile email (default in chart examples) |
| Discovery URL | Your issuer’s .well-known/openid-configuration (must be reachable from PAPI pods over HTTPS) |
PAPI publishes its own OAuth metadata at https://papi.<domain>/.well-known/openid-configuration. MCP and other OAuth clients discover PAPI there — that URL is not the redirect URI you register in your corporate IdP.
MCP server OAuth callback
When the MCP server is enabled (mcp.<domain>), the platform bootstrap job registers this redirect URI on PAPI’s internal mcp-server OAuth client:
| Client | Redirect URI |
|---|---|
| MCP server (IDE / AI agent OAuth) | https://mcp.<domain>/auth/callback |
You do not register the MCP callback in your corporate IdP — it is between MCP clients and PAPI’s authorization server.
Helm configuration
All OAuth/OIDC secret material is rendered into the platform-secrets Secret (or a bring-your-own Secret) by the runwhen-platform chart. Production installs should never commit client secrets in plain-text values files.
Direct upstream OIDC (recommended)
Each IdP is one object in the OIDC_IDP_CONFIG JSON array. The chart accepts a YAML list under secrets.values.oidcIdpConfig and JSON-encodes it at render time.
# values overlay excerpt — prefer auth.existingSecret for the secret valuessecrets: values: oidcIdpConfig: - name: "keycloak" # must match redirect URI path segment display_name: "Acme SSO" # login button label discovery_url: "https://sso.acme.example/realms/acme/.well-known/openid-configuration" client_id: "runwhen-platform" client_secret: "replace-me" # use auth.existingSecret in production scopes: "openid profile email" icon: "lock"Equivalent JSON (when loading from a Secret file):
[ { "name": "keycloak", "display_name": "Acme SSO", "discovery_url": "https://sso.acme.example/realms/acme/.well-known/openid-configuration", "client_id": "runwhen-platform", "client_secret": "replace-me", "scopes": "openid profile email", "icon": "lock" }]After helm upgrade, users see an Acme SSO button on the login page. PAPI completes the OIDC code flow and provisions users on first login (same email-based identity model as SaaS).
Bring-your-own Secret (production)
Store credentials in a Kubernetes Secret and reference it from the chart’s auth block instead of secrets.values:
auth: existingSecret: rw-oauth-credentialsStandard Secret keys (via envFrom on backend pods):
| Secret key | Purpose |
|---|---|
OIDC_IDP_CONFIG | JSON array string (upstream IdPs) |
SOCIAL_ACCOUNT_PROVIDERS_AUTH0_APP_CLIENT_ID | Auth0 client ID (optional) |
SOCIAL_ACCOUNT_PROVIDERS_AUTH0_APP_CLIENT_SECRET | Auth0 client secret (optional) |
AUTH0_URL | Auth0 tenant URL (optional; non-secret, can stay in values) |
Create the Secret:
kubectl create secret generic rw-oauth-credentials -n <namespace> \ --from-file=OIDC_IDP_CONFIG=./oidc-idps.jsonWhen auth.existingSecret is set, the chart does not write those keys to platform-secrets; the BYO Secret is merged via envFrom on PAPI and related backend pods.
Optional Auth0 broker (self-hosted)
Set all three values to surface the Enterprise SSO identifier form (connection hint flow) in addition to any upstream OIDC buttons:
secrets: values: auth0Url: "https://<tenant>.us.auth0.com" auth0ClientId: "<client-id>" auth0ClientSecret: "<client-secret>" # prefer auth.existingSecretRegister redirect URI https://papi.<domain>/accounts/auth0/login/callback/ in the Auth0 application.
To expose Auth0-managed social connections (Google, GitHub, …) as branded buttons, add an OIDC_IDP_CONFIG entry with auth0_social_connections (comma-separated Auth0 connection names). See the chart’s values.yaml comment block under secrets.values.oidcIdpConfig for the full schema.
Direct social OAuth (supplementary)
secrets: values: googleClientId: "..." googleClientSecret: "..." githubClientId: "..." githubClientSecret: "..." gitlabClientId: "..." gitlabClientSecret: "..." # gitlabUrl: "https://gitlab.acme.example" # for self-hosted GitLabEach provider uses the matching /accounts/<provider>/login/callback/ redirect URI from the table above.
TLS, trust bundles, and browser origins
Internal CA / TLS inspection
If papi.<domain> or your IdP is signed by a private CA — or outbound HTTPS passes through a TLS-inspecting proxy — PAPI pods must trust that CA or OIDC discovery and token exchange fail.
Set the chart-wide trust bundle so first-party pods validate JWTs, OIDC discovery, and webhook destinations correctly:
global: trustBundle: | -----BEGIN CERTIFICATE----- ... your corporate / cluster CA ... -----END CERTIFICATE-----This is required for MCP OAuth when PAPI uses a chart-managed CA (runwhenCA.deploy: true) and the MCP server performs OIDC discovery against PAPI_PUBLIC_URL.
CORS, CSRF, and login redirects
The chart auto-derives browser-trusted origins from global.domain via the top-level browserOrigins block (CORS, CSRF, and login host allowlists). Defaults cover app.<domain>, papi.<domain>, and agentfarm.<domain>.
If users access the UI from additional hostnames (staging aliases, split DNS), add them under browserOrigins.extraOrigins or the per-service overrides documented in values-example-cluster.yaml in the onboarding pack.
Staff and admin access
SSO users who self-register on first login are not platform staff by default. Staff users can reach /rwsupport endpoints and administrative UI surfaces.
For enterprise SSO where everyone in your email domain should be staff, enable the chart’s staff-access reconciler:
staffAccess: enabled: true domains: - acme.example # emails: # - admin@acme.exampleThe bootstrap job and optional CronJob promote matching users to is_staff=true in Postgres. See staffAccess in the chart values.yaml for safety guards (dryRun, maxDemotionsPerRun, refuseEmptyAllowlist).
Quick one-off promotion:
kubectl exec -n <namespace> <postgres-pod> -- \ psql -d core -c "UPDATE users SET is_staff=true WHERE email='you@acme.example'"Verify SSO end-to-end
- Confirm DNS resolves
app.<domain>andpapi.<domain>to your ingress load balancer. - Open
https://app.<domain>/login— your configured IdP button (or Enterprise SSO form) should appear; hidden providers should not render. - Complete a login — you should land in the workspace UI with a session cookie scoped to your domain.
- Optional:
curl -s https://papi.<domain>/api/v3/auth/providers | jq— confirms which providers PAPI exposes. - Optional (MCP): browse to
https://mcp.<domain>and complete OAuth — discovery should succeed againsthttps://papi.<domain>/.well-known/openid-configuration.
If discovery fails with TLS errors, revisit global.trustBundle. If login succeeds but API calls fail with CORS/CSRF errors, revisit browserOrigins.
Related documentation
| Topic | Link |
|---|---|
| Self-hosted prerequisites (DNS, TLS, cluster) | Self-Hosted Deployment Requirements |
| User provisioning and workspace access | User Management |
| MCP server install | Onboarding pack / chart docs/install/ |
| Security architecture | Security Model Overview |