# Workspace single sign-on

> Sign a workspace's people in through Okta, Microsoft Entra ID or Google Workspace with OpenID Connect, prove your email domains, and require it.

Source: https://immiscible.fly.dev/docs/guides/workspace-sso

Your people sign in to the workspace through your identity provider, with OpenID Connect (the authorisation code flow with PKCE, `state` and `nonce`). Owners and admins set it up under **Settings**, then the **Single sign-on** tab, or with [`PUT /api/w/:wid/sso`](https://immiscible.fly.dev/docs/api/put-api-w-wid-sso.md).

SAML 2.0 single sign-on is supported per workspace (SP-initiated, signed assertions required), alongside OIDC with Okta, Entra and Google Workspace. To use SAML, choose **SAML** under **Single sign-on** and follow the SAML setup guide (`docs/setup-saml.md`), which covers Okta, Entra and ADFS field by field; the domains, enforcement and default role below work the same way.

## What every provider needs

- **Redirect URI:** `https://immiscible.fly.dev/sso/callback`. Register exactly this.
- **Scopes:** `openid email profile`. Immiscible asks for these; the ID token must carry `email`.
- **Issuer, client id and client secret** from the provider, entered in Immiscible. The issuer is checked by reading its `/.well-known/openid-configuration` before it is saved.
- **Default role** for someone's first sign-in: admin, member, analyst, auditor or approver. Single sign-on never makes anyone an owner.

## Okta

1. In the Okta admin console, **Applications**, **Create App Integration**, **OIDC - OpenID Connect**, **Web Application**.
2. Sign-in redirect URI: `https://immiscible.fly.dev/sso/callback`. Grant type: Authorization Code. Assign the people or groups who should reach the workspace.
3. Copy the **Client ID** and **Client secret**.
4. Issuer: your Okta domain, for example `https://acme.okta.com` (or a custom authorisation server such as `https://acme.okta.com/oauth2/default`; use the same one the app's tokens come from).

## Microsoft Entra ID

1. In the Entra admin centre, **App registrations**, **New registration**. Platform **Web**, redirect URI `https://immiscible.fly.dev/sso/callback`.
2. Under **Certificates & secrets**, add a client secret and copy its value.
3. Under **Token configuration**, add the optional `email` claim to the ID token. Without it Immiscible cannot tell which account is yours, and says so.
4. Issuer: `https://login.microsoftonline.com/<tenant id>/v2.0`, with your directory (tenant) id.

Immiscible refuses an Entra sign-in whose email domain Entra marks as unverified (the `xms_edov` claim).

## Google Workspace

1. In Google Cloud console, **APIs & Services**, **Credentials**, **Create credentials**, **OAuth client ID**, type **Web application**, authorised redirect URI `https://immiscible.fly.dev/sso/callback`. Make the consent screen **Internal**.
2. Copy the client id and secret.
3. Issuer: `https://accounts.google.com`.

Only Google Workspace accounts sign in: the ID token must carry your Workspace domain (`hd`), so personal Gmail accounts are refused.

## Prove your domains

List the email domains your people use. Each one gets a DNS TXT record to publish:

| Host | Value |
| --- | --- |
| `_immiscible-verification.<your domain>` | `immiscible-domain-verification=<token shown in the console>` |

Then verify it in the console or with [`POST /api/w/:wid/sso/domains/:domain/verify`](https://immiscible.fly.dev/docs/api/post-api-w-wid-sso-domains-domain-verify.md). Until a domain is verified, nobody on it is created by single sign-on, single sign-on cannot be required for it, and sign-ups on it are not routed to you. A domain belongs to one workspace only.

## People and roles

- **First sign-in** creates the account (if needed) and the membership, with the default role, only for an address on a verified domain.
- **After that** people are linked by the provider's subject, never by email again, so a renamed address still reaches the same account.
- **Removed members** are not re-added by signing in again; an owner or admin invites them back.
- **Groups** from the provider can carry entitlements: see [`PUT /api/w/:wid/sso/group-entitlements`](https://immiscible.fly.dev/docs/api/put-api-w-wid-sso-group-entitlements.md). To add and remove people from the provider itself, use [SCIM](https://immiscible.fly.dev/docs/guides/scim).

## Require it

Once a domain is verified, require single sign-on for it (`requireSso` in the [security policy](https://immiscible.fly.dev/docs/guides/security-admin.md#require-single-sign-on)). Password, email-code and personal-provider sign-ins are refused for those addresses, and existing sessions made another way stop reaching the workspace. Owners with a passkey or an authenticator app keep a break-glass path, so a broken identity provider cannot lock you out.

People sign in from **Sign in with SSO** on the sign-in page, or from the workspace's own link, shown in the console: `https://immiscible.fly.dev/sso/start?workspace=<your workspace>`.

## Trying it locally

On a laptop, start the server with `IMMISCIBLE_DEV_VERIFY_DOMAINS=true` to verify a domain without publishing a DNS record. It is refused in production.
