READING / adr-004-workos-authkit.md

A published OKF snapshot. Follow the map, then read at your pace.

Knowledge mapSee how this bundle connects1 concepts · No internal links yetOpen mapHide map
Reading ADR-004 — WorkOS AuthKit for Bugpup login
Decision
01
Single concept

This bundle is ready for its next connection.

You are reading the whole bundle. Add another markdown concept and link it from this one to turn the map into a knowledge graph.

0 internal links

Nodes are colored by OKF type. Add internal markdown links to reveal relationships.

Decisionunverifiedstable
Generated legacy timestamp ·

ADR-004: WorkOS AuthKit for Bugpup login

Status: Accepted 2026-07-25 Project: Bugpup

Context

Bugpup was using a hand-rolled GitHub OAuth flow with a custom JWT session secret. Pain points:

  1. Pinned to one OAuth provider (GitHub). Users without GitHub accounts couldn't sign in.
  2. JWT issuance/refund/rotation logic was our responsibility to maintain.
  3. Email verification, MFA, and session management needed to be built from scratch.
  4. The cookie format of the Next.js session and the Go API session diverged, forcing a dual-cookie scheme.

Decision

Adopt WorkOS AuthKit for both the dashboard (Next.js) and the API (Go).

Architecture

Browser → Next.js (apps/web) → Go API (apps/api)
            │                       │
            │ AuthKit middleware     │ JWT middleware
            │ reads sealed cookie    │ validates Bearer token
            │ extracts accessToken   │ against WorkOS JWKS
            │ forwards as Bearer     │ fetches user via Mgmt API
            │                       │ upserts local user

Why forward the access token, not the cookie

The Next.js SDK seals the session cookie with iron-session (uses a different key derivation + envelope than the Go SDK's AES-256-GCM). Trying to re-implement iron-session decryption in Go was not worth the complexity. Forwarding the JWT access token bypasses the incompatibility entirely — the browser already has it inside the cookie.

JWT validation

Why not /userinfo

The authkit domain's /oauth2/userinfo endpoint requires the access token to carry specific scopes (specifically openid profile email) that the Next.js SDK-managed sessions don't grant. The Management API uses the API key instead, which is reliable for both staging and production.

Why api.workos.com for both staging and production

We initially assumed sk_test_ keys should hit api.workos-test.com. WorkOS returns 404 for that host on the /sso/jwks and /user_management/users endpoints. Both endpoints live on api.workos.com regardless of sk_test_ / sk_live_ prefix. The sk_test_ prefix is a usage convention, not a host router.

Consequences

GitHub repo access (separate from login)

Bugpup's "Import GitHub repo" flow needs a per-user GitHub access token to list repos and read package.json. This is separate from WorkOS login — a user can sign in with Google and still need to authorize GitHub.

We use a custom GitHub OAuth App (not a GitHub App, not WorkOS GitHub OAuth) so the code stays simple and the token surface is minimal. The flow:

Browser → /api/auth/github/connect → GitHub OAuth → /api/auth/github/callback
                                              ↓
                                     exchange code → token
                                              ↓
                                     POST /api/v1/profile/github
                                              ↓
                                     store token on user

Why not WorkOS GitHub OAuth / WorkOS Pipes

Why not a GitHub App

GitHub App user-to-server tokens (installation tokens) require a separate App installation flow and the resulting tokens have broader repository scope than we need. GitHub OAuth App tokens are friendlier for the Vercel-style "Connect GitHub" UX.

Token storage

The token is stored on the users.github_access_token column (encrypted at rest in production). The token is held server-side only; the API never echoes it back to the browser. The session cookie is the only credential the browser ever sees.

Env vars

# WorkOS AuthKit (login)
WORKOS_API_KEY=sk_test_...          # Mgmt API key, used by Go for user lookup
WORKOS_CLIENT_ID=client_...         # OAuth application ID
WORKOS_AUTHKIT_DOMAIN=https://...   # env-specific authkit domain (used for hosted login)
WORKOS_COOKIE_PASSWORD=...          # ≥32 chars, shared between web and api
NEXT_PUBLIC_WORKOS_REDIRECT_URI=http://localhost:3000/callback

# GitHub OAuth App (repo access on /projects/new)
GITHUB_OAUTH_CLIENT_ID=...          # OAuth App client ID
GITHUB_OAUTH_CLIENT_SECRET=...      # OAuth App client secret
GITHUB_OAUTH_REDIRECT_URI=http://localhost:3000/api/auth/github/callback

Related

FOCUS READER

Speed read

Paused

Ready

300 wpm

Space play/pause · arrows step or change rate · Escape closes