Skip to content

Single Sign On

RunWhen supports enterprise single sign-on (SSO) in two deployment models:

ModelIdentity flowWho configures the IdP
RunWhen SaaSYour corporate IdP federates to RunWhen’s Auth0 tenant; users sign in at app.runwhen.comRunWhen provisions the Auth0 connection; you configure your IdP with the SP details RunWhen provides
Self-hostedPAPI 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:

SettingValue
ACS URL / Reply URL / Sign-in URLhttps://auth.runwhen.com/login/callback?connection=<connection-name>
Entity ID / Audience / SP Entity IDurn:auth0:runwhen:<connection-name>
NameID formatEmail 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:

  1. Deploy an internal OIDC issuer you already operate (Keycloak, Okta private cloud, Entra ID app registration, Authelia, etc.).
  2. Register a confidential OIDC client whose redirect URI targets PAPI (see IdP redirect URIs).
  3. Supply the client credentials to the Helm chart via OIDC_IDP_CONFIG (see Helm configuration).
  4. Optionally enable staffAccess so 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)

OptionHelm / configIdP must be reachable fromBest for
Direct upstream OIDC (recommended)secrets.values.oidcIdpConfigOIDC_IDP_CONFIGBrowser (login) + PAPI pods (discovery, token exchange)Enterprise SSO, air-gap with internal Keycloak
Auth0 broker (optional)secrets.values.auth0Url + auth0ClientId + auth0ClientSecretBrowser + PAPI pods to *.auth0.comTeams already standardized on Auth0
Direct social OAuthgoogleClientId / githubClientId / gitlabClientId (+ secrets)Browser + PAPI pods to the providerDev/lab; not a substitute for corporate SSO
Email / passwordAlways available; papi.skipEmailVerification: "true" for lab installs without SMTPn/aBootstrap, 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 methodRedirect 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

SettingRecommended value
Client typeConfidential (client secret required)
Grant typesAuthorization code
PKCESupported by PAPI for Auth0 and social flows; follow your IdP’s defaults for upstream OIDC
Scopesopenid profile email (default in chart examples)
Discovery URLYour 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:

ClientRedirect 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.

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 values
secrets:
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-credentials

Standard Secret keys (via envFrom on backend pods):

Secret keyPurpose
OIDC_IDP_CONFIGJSON array string (upstream IdPs)
SOCIAL_ACCOUNT_PROVIDERS_AUTH0_APP_CLIENT_IDAuth0 client ID (optional)
SOCIAL_ACCOUNT_PROVIDERS_AUTH0_APP_CLIENT_SECRETAuth0 client secret (optional)
AUTH0_URLAuth0 tenant URL (optional; non-secret, can stay in values)

Create the Secret:

Terminal window
kubectl create secret generic rw-oauth-credentials -n <namespace> \
--from-file=OIDC_IDP_CONFIG=./oidc-idps.json

When 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.existingSecret

Register 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 GitLab

Each 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.example

The 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:

Terminal window
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

  1. Confirm DNS resolves app.<domain> and papi.<domain> to your ingress load balancer.
  2. Open https://app.<domain>/login — your configured IdP button (or Enterprise SSO form) should appear; hidden providers should not render.
  3. Complete a login — you should land in the workspace UI with a session cookie scoped to your domain.
  4. Optional: curl -s https://papi.<domain>/api/v3/auth/providers | jq — confirms which providers PAPI exposes.
  5. Optional (MCP): browse to https://mcp.<domain> and complete OAuth — discovery should succeed against https://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.


TopicLink
Self-hosted prerequisites (DNS, TLS, cluster)Self-Hosted Deployment Requirements
User provisioning and workspace accessUser Management
MCP server installOnboarding pack / chart docs/install/
Security architectureSecurity Model Overview