# Single sign-on

> Sign in with your identity provider (OIDC, PKCE) or your directory (LDAP, Active Directory); group mappings to roles and teams; staying in step; mixed mode; how 3AM finds out your directory by itself.

Source: https://docs.3am.si/access/sso · Markdown: https://docs.3am.si/access/sso.md · All docs: https://docs.3am.si/llms.txt
3AM is an on-prem AI on-call engineer for regulated enterprises (https://3am.si).

People sign in with the account they already have, and nobody is invited: someone asks for access the way your
organisation grants it (a ticket, a group), is put in a directory group, opens 3AM, and is in. Every tier has it.
Set it up under **Settings → How people sign in**, or in setup's first step.

|                               |                                                                                                                                                                                                                                                                                                                  |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Identity provider (OIDC)**  | Entra ID, ADFS, Okta, Ping, Keycloak. Authorization code flow with PKCE; the ID token's signature, issuer, audience, expiry and nonce verified against the provider's published keys. Your MFA and access rules apply; 3AM never sees a password. **Redirect address:** `<console address>/v1/auth/sso/callback` |
| **Directory password (LDAP)** | LDAP or Active Directory directly. Checked by binding over TLS (`ldaps://` or StartTLS); never stored or logged. Five tries per name and twenty per client in ten minutes                                                                                                                                        |
| **Groups**                    | A mapping gives a directory group (name, DN or ID) a role, and optionally teams and tags. Several groups: the highest role and every team and tag. No mapped group: no access                                                                                                                                    |

## People from the directory

Managed by it: role, teams and tags follow their groups and can't be edited on Team (a *directory* tag). The last owner
is never demoted or suspended by a group change.

## Staying in step

Every `sync_minutes` (15 by default; **Sync now** in Settings):

| With                                                                          | What happens                                                                                                                                                                                                                                    |
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A directory (an LDAP service account, read-only; works with OIDC sign-in too) | Each mapped group's members are read. People exist before they first sign in, so they can be asked to approve from day one. Leaving every mapped group suspends them within one interval and ends their sessions; back in a group, they're back |
| OIDC with refresh tokens                                                      | Each person is asked about again at the provider; a refusal suspends them                                                                                                                                                                       |
| OIDC without refresh tokens                                                   | Sessions last at most `recheck_hours` (8), so people pass through the provider again                                                                                                                                                            |

A directory that can't be reached changes nobody: an outage never locks people out (`identity.directory_synced`).

**Mixed mode** (on by default): people added by hand still sign in by e-mailed code beside directory people
(contractors, a pilot). Turned off, only the directory signs people in, and a group must give the owner role. The
admin token stays the audited break-glass.

## The directory is found out, not configured

Give its address and 3AM's own account; a server name is enough (`ldaps://` is tried, then `ldap://` with StartTLS;
several names are a failover). 3AM works out the rest:

| What                                  | How                                                                                                                                                                        |
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| The kind of directory                 | Its root entry: Active Directory, 389 DS / FreeIPA, or plain LDAP                                                                                                          |
| Where people are                      | `defaultNamingContext`, else the first naming context, else the `DC=` part of 3AM's account                                                                                |
| A person                              | What they type against `sAMAccountName`, `userPrincipalName`, `uid`, `mail`, `cn`; `DOMAIN\name` and `name@domain` find the same person; disabled AD accounts are left out |
| Their address                         | `mail`, else `userPrincipalName`, else their name at the directory's domain                                                                                                |
| Group membership                      | `memberOf`, `member`, `uniqueMember` or `memberUid`; nested groups eight deep; AD's nested-group match where it exists; ranged results read to the end                     |
| A certificate the host doesn't know   | **Connect** shows it (subject, issuer, fingerprint); one click trusts exactly that certificate                                                                             |
| Paging limits, size limits, referrals | Asked again whole; used and marked partial; skipped                                                                                                                        |

*Show what 3AM worked out* has every one as an override, for the rare directory that needs it. Groups are picked by
typing part of their name.

## API

| Call                                                  | Purpose                                                                |
| ----------------------------------------------------- | ---------------------------------------------------------------------- |
| `GET /v1/status` → `auth.sso`                         | `{mode, label, local}`: what the sign-in page offers                   |
| `GET /v1/auth/sso/start`, `GET /v1/auth/sso/callback` | The trip to the identity provider and back, ending at `/?sso=<ticket>` |
| `POST /v1/auth/sso/redeem {ticket}`                   | The session, once, within 60 s                                         |
| `POST /v1/auth/ldap {username, password}`             | A directory password                                                   |
| `GET`/`PUT /v1/settings/sso`                          | Admin: the settings (secrets write-only)                               |
| `GET /v1/settings/sso/groups?q=`                      | Admin: find groups by name                                             |
| `POST /v1/settings/sso/discover`                      | Admin: try an address and account without saving                       |
| `POST /v1/settings/sso/test`, `/sync`                 | Admin: try the saved settings; bring people in step now                |

Settings and secrets are in `data/identity/sso.json` (0600). 3AM ships as one binary, so its LDAP client is its own.
With a [Keychain](https://docs.3am.si/licensing/keychain), single sign-on set on the home reaches every member.
