Skip to main content
← Back to list
01Issue
FeatureClosedSwamp Club
Assigneesstack72

Relationships

#1014 serve-auth: enable OIDC Provider + Device Authorization plugins in swamp-club

Opened by stack72 · 7/7/2026

Parent

Sub-issue of swamp #662 (serve authentication & authorization). Track A, item 1.

Summary

Enable the built-in `oidcProvider` and `deviceAuthorization` better-auth plugins in swamp-club so it functions as an OAuth 2.0 authorization server. This allows swamp serve to authenticate users via the device grant flow — the first step toward replacing `mode: token` (manual token minting) with `mode: oauth` (users log in via swamp-club).

What to build

1. Enable `oidcProvider` plugin

In `lib/auth.ts`, add the OIDC provider plugin (already in better-auth 1.4.18, no new dependency):

```typescript import { oidcProvider } from "better-auth/plugins/oidc-provider";

// Add to plugins array: oidcProvider({ loginPage: "/login", getAdditionalUserInfoClaim: async (user, scopes, client) => { // Phase 1: return empty groups (no SSO yet) // Phase 2 (issue 2) will populate from stored idpGroups return { groups: [], }; }, }), ```

This exposes:

  • `GET /api/auth/.well-known/openid-configuration` — OIDC discovery
  • `GET /api/auth/oauth2/authorize` — authorization endpoint
  • `POST /api/auth/oauth2/token` — token endpoint
  • `GET /api/auth/oauth2/userinfo` — userinfo endpoint (returns sub, email, name + custom claims)
  • `POST /api/auth/oauth2/register` — client registration
  • `POST /api/auth/oauth2/consent` — consent approval

2. Enable `deviceAuthorization` plugin

Also already in better-auth 1.4.18:

```typescript import { deviceAuthorization } from "better-auth/plugins/device-authorization";

// Add to plugins array: deviceAuthorization({ verificationUri: "/device", expiresIn: "30m", interval: "5s", }), ```

This exposes:

  • `POST /api/auth/device/code` — request device code + user code
  • `POST /api/auth/device/token` — poll for access token (returns `authorization_pending` until approved)
  • `GET /api/auth/device?user_code=XXXX` — verify a user code
  • `POST /api/auth/device/approve` — user approves (requires authenticated session)
  • `POST /api/auth/device/deny` — user denies

3. Create or adapt the device verification page

The device authorization plugin uses `verificationUri: "/device"` as the page where users enter their code and approve. The existing `routes/login.tsx` has custom device code interstitial logic — either:

  • Adapt `routes/login.tsx` to handle the plugin's verification flow (call `/api/auth/device/approve` on confirmation)
  • Create a new `routes/device.tsx` page that shows the code entry form and approve/deny buttons

The page flow:

  1. User visits `/device` (linked from the CLI output)
  2. If not authenticated → redirect to login (email/password or social), then back to `/device`
  3. User enters the code shown in their terminal
  4. Page calls `GET /api/auth/device?user_code=XXXX` to verify the code is valid
  5. User clicks "Approve" → page calls `POST /api/auth/device/approve` with the user code
  6. CLI polling on `/api/auth/device/token` gets the access token

4. Register swamp-serve as an OAuth client

Use the `registerOAuthApplication` endpoint to create a client for swamp serve:

```bash curl -X POST https://swamp-club.com/api/auth/oauth2/register \ -H "Content-Type: application/json" \ -d '{ "client_name": "swamp-serve", "redirect_uris": [], "grant_types": ["urn:ietf:params:oauth:grant-type:device_code"], "scope": "openid profile groups" }' ```

This returns a `client_id` and `client_secret` that swamp serve uses in the device grant flow.

For testing, you can also use the `trustedClients` option on the oidcProvider config to hardcode a test client:

```typescript oidcProvider({ trustedClients: [{ clientId: "swamp-serve-dev", clientSecret: "dev-secret", redirectUrls: "", type: "confidential", skipConsent: true, }], }), ```

swamp-serve is a first-party app — the user shouldn't see a "Do you authorize swamp-serve to access your data?" consent screen. Use `skipConsent: true` on the trusted client, or configure the consent page to auto-approve for the swamp-serve client ID.

Testing

  1. Start swamp-club locally
  2. Create a test user (email/password)
  3. Verify OIDC discovery: `curl http://localhost:8000/api/auth/.well-known/openid-configuration\`
  4. Request a device code: `curl -X POST http://localhost:8000/api/auth/device/code -d '{"client_id":"swamp-serve-dev"}'`
  5. Open browser, visit the verification URL, enter the user code
  6. Sign in (email/password), approve the device
  7. Poll for token: `curl -X POST http://localhost:8000/api/auth/device/token -d '{"grant_type":"urn:ietf:params:oauth:grant-type:device_code","device_code":"...","client_id":"swamp-serve-dev"}'`
  8. Call userinfo with the access token: `curl -H "Authorization: Bearer " http://localhost:8000/api/auth/oauth2/userinfo\`
  9. Verify userinfo returns `sub`, `email`, `name`, `groups: []`

Scope

  • Enable two plugins in `lib/auth.ts` (no new dependencies — both in better-auth 1.4.18)
  • Device verification page (new or adapted from existing)
  • Register a test OAuth client
  • Skip consent for first-party apps

Out of scope

  • SSO / IdP federation — Track A issue 2
  • Storing IdP groups — Track A issue 2
  • Populating groups on userinfo — Track A issue 3
  • swamp CLI changes — Track A2 (swamp repo)

References

  • Auth config: `~/code/swamp-club/swamp-club/lib/auth.ts`
  • Login page: `~/code/swamp-club/swamp-club/routes/login.tsx`
  • Auth routes: `~/code/swamp-club/swamp-club/routes/api/auth/[...path].ts`
  • OIDC provider source: `node_modules/.deno/better-auth@1.4.18/node_modules/better-auth/dist/plugins/oidc-provider/`
  • Device authorization source: `node_modules/.deno/better-auth@1.4.18/node_modules/better-auth/dist/plugins/device-authorization/`
02Bog Flow
OPENTRIAGEDIN PROGRESSCLOSED+ 1 MOREASSIGNED+ 1 MOREPLANNING

Closed

7/7/2026, 2:18:10 PM

No activity in this phase yet.

03Sludge Pulse
stack72 assigned stack727/7/2026, 2:05:01 PM
Editable. Press Enter to edit.

stack72 commented 7/7/2026, 2:18:10 PM

Closing — rescoped. SSO and IdP groups removed from initial scope. New issue will cover the minimal OAuth flow: OIDC provider + device authorization plugins, collective-based admission, no SSO dependency.

Sign in to post a ripple.