# §38 — The API behind single sign-on

Their IT department runs the identity provider. You are the relying party: you trust an id token, you map a `sub` to a local user, and you never see a password at all.

This is the one that gets asked for in procurement and is usually quoted as a quarter of work. It is `mountSso` plus a function that turns claims into a local user.

Back-channel logout is mounted for you. When their IdP says a session is over, it is over here too — which is the actual reason the requirement exists.

## What you are carrying

- `@atlas/sso` — OIDC discovery, PKCE, code exchange, id-token verification.
- `@atlas/db` — State rows and the local user mapping.
- `@atlas/security` — The revocable session the back-channel logout kills.
- `@atlas/server` — The three routes it mounts.

## Start it

```bash
bun add @wess/atlas
```

## What it looks like

`src/sso.ts`

```ts
import { mountSso, ensureSsoStateTable } from "@atlas/sso"

await ensureSsoStateTable(db)

export const ssoRoutes = mountSso({
  db,
  issuerUrl: process.env.OIDC_ISSUER!,
  clientId: process.env.OIDC_CLIENT_ID!,
  clientSecret: process.env.OIDC_CLIENT_SECRET!,
  // Claims in, your user out. This is the whole integration.
  onAuthenticated: async (db, claims) => {
    const user = await upsertUserBySub(db, claims.sub, claims.email)
    return { localUserId: user.id, displayName: claims.name }
  },
  issueSession: (conn, user) => putSessionCookie(conn, user),
})
```

## Where now

- Put it online — turn to §100 (Appendix A)
- Hand it to an agent — turn to §102 (Appendix C)
- Walk it again from the start — turn to §1
