Web field Plate 77 Route §77
Plate 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

bun add @wess/atlas

What it looks like

src/oauth.tsts
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)

Read next

@wess/atlas/oauth @wess/atlas/auth
Type to search guides and package references.