Skip to content

Single sign-on

Preloop Cloud and Preloop Enterprise support single sign-on per account with OIDC or SAML 2.0. An account administrator configures it under Account settings > Single sign-on; everything there is also available through the API under /api/v1/accounts/{account_id}/sso.

What the SSO feature does:

  • SP-initiated sign-in with OIDC (authorization code with PKCE S256) or SAML 2.0 (HTTP-Redirect request, HTTP-POST ACS).
  • Sign-in discovery from a verified email domain.
  • Just-in-time membership for new users, with a default role that can never be owner.
  • IdP groups mapped to roles and teams on each sign-in.
  • Enforcement: members on verified domains must use SSO; account owners keep an emergency password or passkey sign-in, and every use is audited and emailed to all owners.
  • An administrator test sign-in that must succeed before enforcement can be turned on.

To create, update and remove members from your IdP, see SCIM provisioning.

Provider guides: Okta, Microsoft Entra ID, Google Workspace. Each uses OIDC, with SAML as the alternative. The generic OIDC and SAML instructions are below.

Deployment settings (Preloop Enterprise)

Preloop Cloud is already configured. On a self-hosted Enterprise install, set these Helm values:

environment:
  preloopUrl: "https://preloop.example.com"   # external HTTPS origin
sso:
  enabled: true
  publicUrl: ""              # optional; defaults to environment.preloopUrl
  allowPrivateIssuers: false # see below
Value Effect
sso.enabled Mounts the SSO sign-in and administration routes and shows the console tab. With false, the routes are not mounted. Enforcement already on for an account still applies, so turn enforcement off before you disable SSO.
sso.publicUrl The origin used for the OIDC redirect URI and the SAML entity id and ACS URL. Use it when the browser reaches Preloop on a different origin than environment.preloopUrl. Give an origin only, with no path.
sso.allowPrivateIssuers Lets OIDC issuers and SAML metadata URLs resolve to private (RFC 1918, IPv6 unique-local) or loopback addresses, for an IdP on your internal network. It applies when a connection is saved and at sign-in (OIDC discovery, token and JWKS requests); sign-in connects only to the addresses that passed the check. Link-local, cloud metadata (including the AWS and GCP IPv6 metadata addresses), shared and reserved addresses stay refused. It is a core setting that also relaxes gateway identity providers. Single-tenant installs only.

The Enterprise image includes libxmlsec1 and the python3-saml and xmlsec packages that SAML needs. The image startup check fails if they are missing. The browser must send a same-origin Origin or Referer when starting sign-in. Keep the default referrer policy on the sign-in page.

Before you start: verify a domain

  1. In Verified domains, add your email domain (for example example.com). Public mailbox domains are refused.
  2. Publish the TXT record shown, preloop-verification=<token>, on the domain itself, then click Verify DNS.

A domain can be verified by only one Preloop account. Sign-in discovery, email-based linking of existing members and SAML just-in-time membership all depend on a verified domain.

URLs to register with your IdP

Each connection shows its own URLs after you save it (<id> is the connection id):

Protocol URL
OIDC redirect URI https://<origin>/api/v1/auth/sso/<id>/callback
SAML SP entity id and metadata https://<origin>/api/v1/auth/sso/<id>/metadata
SAML ACS (HTTP-POST) https://<origin>/api/v1/auth/sso/<id>/acs

<origin> is sso.publicUrl, or environment.preloopUrl if that is not set. On Preloop Cloud it is https://preloop.ai.

Generic OIDC

Your IdP must:

  • publish /.well-known/openid-configuration over HTTPS and support PKCE S256. Preloop always sends S256. If the document lists code_challenge_methods_supported, the list must include S256; if the field is missing (as with Entra ID), S256 is assumed;
  • use an exact, tenant-specific issuer (multi-tenant endpoints such as Entra common or organizations are refused);
  • sign ID tokens with RS256 or ES256;
  • issue ID tokens with a lifetime (exp - iat) of at most 24 hours. The usual one-hour tokens from Okta, Entra ID and Google are accepted. exp, iat and nbf are checked against the clock with 60 seconds of skew.

Steps:

  1. Create a confidential web application in the IdP that uses the authorization code flow. Leave the redirect URI empty for now.
  2. In Preloop, click Add OIDC connection. Enter the issuer, client id, client secret and scopes (openid email profile, plus the scope that carries groups if your IdP needs one). Choose a default role and whether new users get a membership on first sign-in. Save.
  3. Copy the connection's callback URL into the IdP as the redirect URI.
  4. Optional: set Groups claim or attribute to the ID-token claim that lists the user's groups, then add group mappings (see Groups).
  5. Click Test sign-in and complete the sign-in as yourself.

The email comes from the email claim. To read another claim, set attribute_mapping to {"email": "<claim>"} with PATCH /api/v1/accounts/{account_id}/sso/connections/{id}; the console does not edit this field. An email is linked to an existing member only when the member's email is verified in Preloop and the IdP reports email_verified: true or the domain is verified on the account.

Generic SAML

Preloop requires signed assertions by default; if you turn that off, the Response must still be signed. SHA-1 signatures and digests are refused. Unsolicited (IdP-initiated) responses are refused, so users always start from the Preloop sign-in page. At most 60 seconds of clock skew is allowed.

  1. Click Add SAML connection and save a name. The connection then shows its SP metadata URL, ACS URL and, once you set an SP signing key, the public SP certificate.
  2. In the IdP, create a SAML application from the SP metadata URL, or enter the entity id and ACS URL by hand. Set the NameID to a stable, unique identifier for the user.
  3. Send these attributes:
  4. email, the user's address on your verified domain (required, exactly one value);
  5. displayName (optional);
  6. a groups attribute (optional).

If your IdP uses other names, set attribute_mapping (keys email, display_name) with PATCH /api/v1/accounts/{account_id}/sso/connections/{id}. 4. Back in Preloop, under Import identity provider metadata, paste the IdP's metadata URL or XML, click Preview metadata, then Apply metadata to editor and save. The import reads one IdP only, refuses DTDs and redirects, and is capped at 256 KiB. The IdP entity ID and sign-in URL can also be typed in; signing certificates come from the metadata (or the saml_idp_certs API field). Several certificates are accepted so that you can rotate the IdP certificate without downtime. 5. Optional: set an SP signing key, an RSA key of at least 2048 bits in unencrypted PEM. Preloop then signs AuthnRequests and can decrypt encrypted assertions. The key cannot be read back. Each time you set or rotate it, re-import the SP metadata into the IdP. 6. Click Test sign-in.

SAML has no email-verified flag. A new membership or a link to an existing member therefore requires the user's email domain to be verified on the account, and linking also requires the member's email to be verified in Preloop.

Groups, roles and teams

Set the connection's Groups claim or attribute, then under Group mappings map group values to a role, a team, or both. On each normal sign-in:

  • matching grants are added, and grants that this mapping created earlier are removed when the group disappears;
  • roles and team memberships that an administrator assigned by hand are kept;
  • if several roles match and one includes all the others' permissions, it wins; if they cannot be compared, the connection's default role applies and an ambiguous_role_mapping event is audited;
  • the owner role and owner-only permissions cannot be mapped.

Administrator test sign-ins do not apply group mappings. When Entra ID sends a group overage marker instead of the full list, the default role applies; there is no Microsoft Graph lookup.

Enforcement and break-glass

Require single sign-on turns enforcement on for the account. You can turn it on only after you have completed a Test sign-in in the last 24 hours on an enabled connection whose configuration has not changed since.

While enforcement is on, members whose email is on a verified account domain cannot use password, social or passkey sign-in, a refresh of a non-SSO session, account switching, or CLI and MCP OAuth exchanges derived from a non-SSO session. When you turn enforcement on, their existing non-SSO sessions are revoked. SSO sessions, and memberships in other accounts, are not affected. API keys and runtime credentials follow their own policies.

Break-glass: every current account owner keeps password and passkey sign-in. Each emergency sign-in records sso.break_glass.used in the audit log and emails all owners. To reach an enforced account after using another one, sign in again with a password or passkey and select that account during the sign-in (the console uses /login?account_id=<account id>). Switching accounts from an existing session does not grant the exception. The break_glass_owner_ids policy field is legacy and informational only; it does not limit which owners keep emergency access.

Turning enforcement off restores the other sign-in methods. Identity links, passwords and passkeys are kept; sessions that were already revoked stay revoked.

Troubleshooting

Symptom Check
The email step does not offer SSO The domain is verified and the account has exactly one enabled connection. With several connections, use the connection's own sign-in link.
"Start single sign-on from the sign-in page" The browser sent no same-origin Origin or Referer. Start from the Preloop sign-in page and check proxies that strip Referer.
"Unable to start single sign-on" (OIDC) Discovery failed. Check that the issuer is exact, the endpoints use HTTPS on public addresses (or private ones with sso.allowPrivateIssuers), and that code_challenge_methods_supported, if present, includes S256.
/console#sso_error=sign_in_failed The audit log has an sso.login.failed event with a reason code, for example lifetime_exceeded, bad_audience, missing_email or replayed_state.
The IdP rejects the redirect URI or ACS The URL registered in the IdP must match the connection's URL exactly, including sso.publicUrl.

Post-deploy probe (Preloop Cloud operations)

scripts/prod_sso_probe.py runs after each staging and production deploy. Set SSO_PROBE_OIDC_EMAIL_<ENV> to enable the enterprise check: the probe signs in as that user through a probe account whose single OIDC connection points at a mock IdP that approves without interaction. It runs discover, start, the IdP redirect and the callback, then calls /api/v1/auth/users/me with the issued token. SSO_PROBE_SAML_CONNECTION_ID_<ENV> adds a check of that connection's SP metadata. A failure names the step and fails the job.