# v7.0 Phase 5.4 — WorkOS SSO setup Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development.

**Goal:** Ship hardened Option A (Admin Portal + webhook + settings UI). Reuse Phase 5.1 RPC pattern + Phase 3 webhook claim/lease/complete pattern.

**Tech Stack:** Next.js 16 + Supabase + `@workos-inc/node` SDK. Vercel deploy. NEW env vars: `WORKOS_API_KEY`, `WORKOS_WEBHOOK_SECRET`.

**Skip Vercel preview deploy verification** — agent env doesn't have Vercel access; CI does the real verification.

## File structure

### New
- `data/deltas/20260508180000_phase5_4_workos_setup.sql`
- `apps/web/lib/workos/client.ts` — WorkOS SDK singleton + webhook signature verifier
- `apps/web/app/api/dashboard/orgs/[orgId]/sso/setup/route.ts`
- `apps/web/app/api/dashboard/orgs/[orgId]/sso/route.ts` (DELETE)
- `apps/web/app/api/workos/webhook/route.ts`
- `apps/web/app/dashboard/admin/sso/page.tsx`
- `apps/web/components/admin/SsoSetupCard.tsx` (client)
- 5 test files

### Modified
- `apps/web/app/dashboard/admin/layout.tsx` — sidebar adds SSO link
- `apps/web/__tests__/_helpers/supabase-stub.ts` — `record_sso_setup_initiated` RPC handler + processed_workos_events table behavior
- `apps/web/lib/dashboard/membership-guard.ts` — MAP includes `workos_org_already_bound: 422`, `webhook_signature_invalid: 401`
- `package.json` — `@workos-inc/node` dep + bump to 6.3.0-pre.10
- `CHANGELOG.md`
- `apps/web/.env.example`

## Tasks

### Task 0: Pull spec
```
git fetch origin spec/v7.0-phase5.4-workos-setup
git checkout origin/spec/v7.0-phase5.4-workos-setup -- docs/specs/v7.0-phase5.4-workos-setup.md
git add docs/specs/v7.0-phase5.4-workos-setup.md
git commit -m "docs: pull Phase 5.4 spec from PR #131 for impl reference"
```

### Task 1: Migration
See spec for full SQL. Schema delta + RPC + REVOKE/GRANT. Privilege grep test asserts all hardening.

### Task 2: WorkOS client wrapper
`apps/web/lib/workos/client.ts`:
- `getWorkOS()` — singleton via `new WorkOS(process.env.WORKOS_API_KEY)`. Throws if env not set.
- `verifyWorkOSSignature(rawBody, signatureHeader)` — wraps `workos.webhooks.constructEvent`. Returns `{ ok: true, event } | { ok: false, reason }`. Tolerance 5 min.
- Lazy load — don't call WorkOS API on client/SSR until route fires.

### Task 3: Setup route
Implements the 6-step sequence from spec. Server-creates WorkOS org via `workos.organizations.createOrganization({ name: org.name, externalId: orgId })` if not yet stored. Reuses on idempotent retry. Returns `{ portalUrl }` only after RPC succeeds. `Cache-Control: private, no-store`.

### Task 4: DELETE disconnect route
Two-step: set status='disabled' first, then call WorkOS `DELETE /connections/:id`, leave connection_id intact. Owner-only via membership check.

### Task 5: Webhook route
Runtime nodejs. Read raw body via `req.text()`. Verify signature. Claim/lease/complete via processed_workos_events. Apply lifecycle ordering rules. Update org_settings + audit append in one transaction (via SECURITY DEFINER RPC `apply_workos_event` — adds idempotency lock + state transition + audit in single function).

### Task 6: Admin SSO page + SsoSetupCard component
Server Component owner-gated. Reads org_settings.sso_connection_status. Client component handles button click → POST setup → opens portalUrl in new tab `rel="noopener noreferrer"`.

### Task 7: Tests
22 spec tests + 5 helper tests. Stub extensions for the 2 new RPCs + processed_workos_events table behavior.

### Task 8: Sidebar + version bump + push + PR
- admin/layout.tsx: add "SSO" link
- package.json: 6.3.0-pre.9 → 6.3.0-pre.10 + add @workos-inc/node
- CHANGELOG entry
- Run typecheck + build + tests
- Push + open PR

## Skip directives
- Skip Vercel preview deploy
- Skip running migration locally — CI's Delegance migrate flow regression validates
- Skip end-to-end browser test — jsdom is the ceiling
- Skip live WorkOS API integration test — use stub fixture for webhook signature verification (real keys not in agent env)
