# §77 — The identity provider

Other people's software signs in to yours. You are issuing the tokens now, and the specification is not optional.

OAuth 2.1 with PKCE, refresh rotation, a device flow for things with no browser, and discovery so clients configure themselves. Registered clients, scopes, and an audit trail come with it.

This is a real commitment. Take it when integrators are asking for it, not because it sounds like the mature choice — everything in §26 is still true underneath, and you now own an expiry sweep as well.

## What you are carrying

- `@atlas/oauth` — Authorize, token, revoke, device, discovery, client admin.
- `@atlas/auth` — The human login the authorize endpoint sits behind.
- `@atlas/db` — Clients, codes, refresh tokens, device codes.
- `@atlas/security` — Rate limits and the audit log you will be asked for.

## Start it

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

## What it looks like

`src/oauth.ts`

```ts
import { oauthRoutes, sweepExpired } from "@atlas/oauth"
import { requireAuth } from "@atlas/auth"

const cfg = {
  db,
  secret: config.secret,
  scopes: ["profile", "email", "offline_access"],
  loadUser: (db, userId) => findUserById(db, userId),
  buildAccessTokenClaims: (user) => ({ sub: String(user.id), email: user.email }),
  // The consent screen is a normal authenticated page; the client admin is not.
  requireUser: requireAuth({ secret: config.secret }),
  requireAdmin: requireStaff,
}

export const routes = oauthRoutes(cfg, { basePath: "/oauth", adminBasePath: "/oauth/clients" })

// Codes, refresh tokens, and device codes all expire. Nothing deletes them
// for you, and the tables only grow.
setInterval(() => void sweepExpired(cfg), 15 * 60_000)
```

## 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
