# Single Sign-On (SAML)
Source: https://docs.tensormachine.ai/console-management/sso

## The mental model

Three separate levers, on purpose:

- **A connection is *how* your members sign in.** A SAML connection lets people authenticate
  with your IdP. By itself it admits no one.
- **A domain assignment is *who* signs in that way.** Routing a **verified** domain through an
  active connection sends every `@domain` sign-in to your IdP — and that assignment is also the
  org's consent to **provision** new `@domain` users on their first SSO login (independent of
  the domain's User Auto-Join policy, which only governs password/Google self-signup).
- **Enforce SSO is *whether anything else is accepted*.** With enforcement on, members on the
  assigned domain must have signed in through your IdP to **use this organization**. It never
  blocks the login itself — see [Enforcement](#enforce-sso-per-organization-access) below.

Sign-in is **email-routed**: on the login page, members open **Sign in with SSO**, type their
work email, and are sent to the right IdP automatically (home-realm discovery). There is no
per-connection sign-in URL to distribute.

<Callout type="info">
  Every member also keeps a **personal workspace** on the same login — SSO and enforcement
  apply to your organization, never to the person's account. See
  [Workspaces & organizations](/workspaces).
</Callout>

## Set up a connection

**Settings → Single Sign-On** (organization owner/admin; changing SSO config requires a
recently re-authenticated session). An organization can hold multiple connections — e.g. a
different IdP per email domain.

The flow is **verify your domain → save → test → activate** — activation routes the domain
automatically, so there is no separate assignment step for the common case:

1. **Verify your email domain first** (on the **Domains** page) — with a DNS `TXT` record,
   or by a confirmation email to one of the domain's standard admin mailboxes
   (`admin@`, `postmaster@`, `webmaster@`, …). Verification is mandatory before SSO because
   sign-in routing is global: an unverified domain must never be able to capture another
   company's logins — a successful SAML handshake proves control of an IdP, not of the domain.
2. **Add connection.** Pick the verified domain the connection will authenticate, then the
   page shows the values your IdP admin registers on their side:
   - **ACS URL / Redirect URI**
   - **SP Entity ID**
   - **Name ID format** → `EMAIL`
   - the **attribute mapping** (below), and a downloadable **SP metadata XML**
3. Enter what your IdP publishes — explicit fields, no metadata import:
   - **Connection name** (a label, e.g. "Okta — acme.com")
   - **SAML SSO URL** (the IdP's sign-on endpoint)
   - **SAML Issuer** (the IdP's entity ID)
   - **X.509 signing certificate** (PEM, pasted)
   Save — the connection is now **Configured** but not yet live.
4. **Test SSO** (required before you can activate): enter a test user's email **on the
   connection's domain**, open the test sign-in in a new tab, complete it at your IdP, then
   press **Check result**. A successful round-trip marks the connection **tested**.
5. **Activate.** Activation routes the connection's domain through it immediately — from
   then on, sign-ins for that domain can use your IdP, and new users on it are provisioned
   into the org on first SSO login. (Passwords keep working until you turn on
   **Enforce SSO**.) The **Domain routing** table remains as the override for special cases
   — several connections, or moving a domain between them.

Editing the connection's core fields (URL / issuer / certificate) resets it to *configured* —
re-test before it can be active again.

### Attribute mapping

Send these SAML attributes (email also arrives as the Name ID):

| Send attribute named | Mapped to |
|---|---|
| `email` | user's email |
| `firstName` | given name |
| `lastName` | family name |

## Options on the connection (changeable any time)

These live on the connection card, separate from setup, because the decisions can change later
without re-testing:

- **Enforce SSO** — members on this connection's assigned domains must sign in through the IdP
  to use this org (details below). Turning it on asks for confirmation and reminds you to keep
  a break-glass account.
- **Enable SAML single logout** — send SAML logout to your IdP's SLO endpoint when members
  sign out.
- **Reauthentication cadence** (page-level) — how often members must re-verify with your IdP:
  never / daily / every 3 days / weekly / every 2 weeks.

## Enforce SSO (per-organization access)

Enforcement gates **access to this organization**, never the person's ability to log in:

- Password and Google sign-in **still succeed** — they just can't open the enforced org.
  The member's personal workspace (and any other org) stays fully reachable.
- Opening the enforced org shows a *"requires SSO"* screen with **Continue with SSO** — one
  **fresh** authentication at your IdP (an existing password session is deliberately not
  enough) and the org opens. The org never disappears from the workspace switcher.
- Enforcement binds to the **assigned domain**: a member invited with an off-domain email
  (e.g. a contractor on `gmail.com`) is not gated.
- **Break-glass accounts** (added on the SSO page; **2FA is required**) may still open the org
  with a password — your way back in if the IdP has an outage. Keep at least one.

<Callout type="warn">
  Before enforcing: activate the connection, confirm a real member can sign in through it, and
  register a break-glass admin.
</Callout>

## Provider quick-starts

You need three values from each provider: the **SSO URL**, the **Issuer / Entity ID**, and the
**signing certificate**.

### Okta

*Applications → Create App Integration → SAML 2.0.* Set **Single sign-on URL** = our ACS URL,
**Audience URI** = our SP Entity ID, **Name ID format** = EmailAddress, and add the three
attribute statements (`email`, `firstName`, `lastName`). Then copy Okta's **Identity Provider
Single Sign-On URL**, **Issuer**, and **x.509 certificate** (from *View SAML setup
instructions*) into our form.

### Microsoft Entra ID (Azure AD)

*Enterprise applications → New application → Create your own → SAML.* Set **Reply URL (ACS)**
and **Identifier (Entity ID)** to ours; configure claims for `email`, `firstName`, `lastName`.
Copy the **Login URL**, **Microsoft Entra Identifier**, and the **Certificate (Base64)** into
our form.

### Google Workspace

*Admin → Apps → Web and mobile apps → Add custom SAML app.* Google shows the **SSO URL**,
**Entity ID**, and **Certificate** first — copy those into our form. On **Service provider
details**, paste our **ACS URL** and **Entity ID**, set **Name ID format = EMAIL**, map
`email → Primary email`, `firstName → First name`, `lastName → Last name`, and turn the app
**ON** for the right OU.

Second-factor strength is your IdP's job — configure MFA / hardware-key requirements there.

## Offboarding

SSO provisions; it doesn't deprovision. Connect [SCIM](/console-management/scim) so your
IdP can push the full lifecycle — deactivating a user there revokes their seat **and their API
keys** here. A SCIM-deactivated user is also refused at the next SSO login.

## Troubleshooting

| Symptom | Likely cause |
|---|---|
| "Sign in with SSO" falls back to password for a member's email | The email's domain isn't **verified**, isn't **assigned** to a connection, or the connection isn't **active** |
| Can't activate the connection | Testing is required first — run **Test SSO** and press **Check result** after the round-trip |
| Test sign-in reaches the IdP but the result stays *pending* | Return to the setup tab and press **Check result**; if it persists, the assertion likely failed (check attribute names / clock skew) |
| New `@domain` user signed in via SSO but wasn't added to the org | The domain isn't verified, or isn't assigned to an **active** connection |
| Assertion signature errors | The IdP's signing certificate rotated — paste the new certificate and re-test |
| A member is gated even after "Continue with SSO" | The gate requires a **fresh** IdP authentication — complete the IdP prompt; a cancelled prompt keeps the gate |
| Password login opens the org despite Enforce SSO | The account is **break-glass**, or its email isn't on the assigned domain (enforcement binds to the domain) |
| "Already verified by another organization" (adding a domain) | Ownership is exclusive; whoever proves DNS control holds it — contact support to contest |
| "Public email provider" (adding a domain) | `gmail.com` / `outlook.com` etc. can't be org domains — those users are individual accounts |

## Security

- Routing and enforcement bind to **DNS-verified** domains only — an org can never capture or
  gate a domain it hasn't proven it owns.
- Assertions are signature-validated against the certificate you pasted; requests and
  responses use signed POST bindings.
- The enforcement gate demands a **fresh** IdP authentication — an existing password or Google
  session is never silently upgraded to SSO.
- Changing SSO configuration requires a **stepped-up** (recently re-authenticated) admin
  session, and every change is written to the organization's audit log.
