> ## Documentation Index
> Fetch the complete documentation index at: https://internal.softcrum.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Social Login and Enterprise SSO

> After this ships, a team signs in with the Google account they already use, and an enterprise customer routes their whole organization through their own identity provider.

## Context

This is the **inbound half of the OAuth duality** (FS-IDN-0007 is the outbound half): here Softcrum
is the client, signing users in through somebody else's identity provider. Both directions are
configuration, and a tenant may well use both — routing its own staff through its corporate IdP
while exposing "Sign in with Softcrum" to its customers.

Two different needs share that mechanism.

**Social login** removes a password from the signup flow, which measurably improves activation. It
is a convenience feature and it is cheap.

**Enterprise SSO** is a sales requirement. Above a certain company size, "does it do SAML or OIDC?"
appears on the checklist before the product is evaluated, and a "no" ends the conversation. It is
the standard enterprise-tier gate across the industry, and pricing it that way is expected rather
than resented.

The security consequence that shapes the design: when an organization enforces SSO, **password and
magic link must stop working for its members**. An SSO enforcement that leaves a password path open
is not enforcement — the customer's offboarding process assumes that revoking someone in their IdP
revokes their access here.

## Scope *(normative)*

* Social providers: Google, Microsoft and GitHub.
* Account linking: an existing user adding a social provider to the same account.
* Enterprise SSO over OIDC, per organization.
* Domain-based routing: a user on a claimed domain is sent to their organization's IdP.
* SSO enforcement, disabling other methods for that organization.
* Just-in-time membership provisioning on first SSO sign-in.

## Non-scope *(normative)*

* **SAML in F1b.** OIDC first, since it covers the modern IdPs; SAML arrives when a customer who
  needs it signs. Adding it is a provider, not a redesign.
* SCIM provisioning — F2. JIT provisioning covers onboarding; deprovisioning still relies on the
  admin until SCIM lands, and that limitation is stated to the customer rather than hidden.
* Role mapping from IdP groups — F2, and it needs the group model to be stable first.
* Social login for members, who authenticate in `core`.

## Behaviour *(normative)*

1. A social sign-in matching an existing **verified** email links to that user. An unverified match
   does **not** link automatically — that is account takeover through an unverified claim.
2. A user may link several providers and unlink any of them, provided at least one authentication
   method remains.
3. An organization claims a domain by **DNS verification**. Unverified domains never route.
4. When SSO is enforced, password and magic link are **disabled for every member of that
   organization**, existing sessions are revoked, and members are told why on their next attempt.
5. JIT provisioning creates a user and a membership on first SSO sign-in, with a default role the
   organization configures. It never grants a sensitive permission — a role with one has to be
   assigned deliberately by a human.
6. An organization owner is **exempt from SSO enforcement** and keeps a password path. If the IdP
   breaks, someone has to be able to get in and turn enforcement off; without the exemption, an IdP
   outage is a total lockout with no remedy.
7. SSO configuration changes require re-authentication and are audit-logged.
8. An IdP that stops returning a user does **not** deactivate their membership. Deprovisioning is
   explicit until SCIM exists, and silently deactivating on a failed lookup would turn an IdP blip
   into mass lockout.

## Data *(normative)*

| Table                      | Key invariants                                                                                                                                                           |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `identity.sso_connections` | `organization_id`; `protocol` oidc; issuer, client id, encrypted client secret; `claimed_domains` array, DNS-verified; `is_enforced`; `jit_default_role_id`; `is_active` |

Social provider links live in `identity.auth_accounts` (FS-IDN-0003) rather than here — they are a
method on a user, not a connection of an organization.

## API *(normative)*

| Endpoint                                          | Class      | Permission                             | Budget       |
| ------------------------------------------------- | ---------- | -------------------------------------- | ------------ |
| `GET /v1/identity/auth/oauth/{provider}`          | Runtime    | public                                 | p95 \<300 ms |
| `GET /v1/identity/auth/oauth/{provider}/callback` | Runtime    | public                                 | p95 \<1 s    |
| `POST /v1/identity/auth/sso/discover`             | Runtime    | public, rate-limited                   | p95 \<300 ms |
| `GET/PUT /v1/identity/sso-connection`             | Management | `identity.sso.{read\|update}`, re-auth | p95 \<1 s    |
| `POST /v1/identity/sso-connection/verify-domain`  | Management | `identity.sso.update`                  | p95 \<1 s    |

## Events *(normative)*

None beyond the membership events FS-IDN-0001 already emits when JIT provisioning creates one.

## Acceptance criteria *(normative)*

1. Social sign-in with a verified matching email links to the existing user; with an unverified one
   it does not, and says why.
2. Unlinking the last authentication method is rejected.
3. A domain routes to an IdP only after DNS verification.
4. Enforcing SSO disables password and magic link for that organization's members and revokes their
   sessions.
5. An owner retains a password path under enforcement.
6. JIT provisioning creates user and membership with the configured default role, never a role
   holding a sensitive permission.
7. An IdP returning an error does not deactivate any membership.
8. **Negative:** no social provider can link to an account whose email is unverified on either side.

## Execution

Single slice, synchronous command. F1b. Providers through Better Auth; each new provider is a
configuration, not an integration — a genuinely new protocol (SAML) would need its own ADR.

## Open questions

| # | Question                                                               | Decides | By                       |
| - | ---------------------------------------------------------------------- | ------- | ------------------------ |
| 1 | Is enterprise SSO an enterprise-tier gate, or available on every plan? | Daniel  | before commercial launch |
| 2 | Which social providers ship first — all three, or Google only?         | Daniel  | before approval          |

## Changelog

| Version | Date       | Change        | Why | Author                 |
| ------- | ---------- | ------------- | --- | ---------------------- |
| 0.1.0   | 2026-08-17 | Initial draft | —   | daniel + claude-opus-5 |

## Delivery record

*Not implemented yet.*
