fix(api,web): separate workspace context from auth session #551

Merged
jason.woltje merged 2 commits from feat/workspace-context-api into main 2026-02-28 15:14:30 +00:00
Owner

Summary

  • Add GET /api/workspaces endpoint (AuthGuard only, no WorkspaceGuard) that returns user workspace memberships
  • Auto-provisions a default "My Workspace" for new users with zero memberships (race-condition guarded)
  • Frontend auth-context now fetches workspaces after session check and persists the default to localStorage
  • Remove dead session.user.workspaceId / currentWorkspaceId reads that were always undefined
  • Deprecate phantom workspace fields on AuthUser shared type

Root Cause

BetterAuth returns only identity fields on session responses. The workspaceId, currentWorkspaceId, and workspaceRole fields on AuthUser were always undefined because they don't exist in the database User model and BetterAuth has no additionalFields config for them. This caused persistWorkspaceId() to silently skip, leaving localStorage empty, which made every WorkspaceGuard-protected endpoint fail with "Workspace ID is required".

Test plan

  • API: 10 new workspace tests pass (service: 7, controller: 3)
  • API: All 3325+ existing tests pass
  • Web: 28 auth-context tests pass (5 rewritten for workspace persistence)
  • Web: All 1446+ existing tests pass
  • Typecheck clean (all 3 packages)
  • Lint clean (all 3 packages)
  • Browser: Login → verify localStorage workspace-id returns a UUID
  • Browser: Navigate to projects → create project succeeds
  • Browser: No "Workspace ID is required" error

Closes #534

🤖 Generated with Claude Code

## Summary - Add `GET /api/workspaces` endpoint (AuthGuard only, no WorkspaceGuard) that returns user workspace memberships - Auto-provisions a default "My Workspace" for new users with zero memberships (race-condition guarded) - Frontend auth-context now fetches workspaces after session check and persists the default to localStorage - Remove dead `session.user.workspaceId` / `currentWorkspaceId` reads that were always undefined - Deprecate phantom workspace fields on `AuthUser` shared type ## Root Cause BetterAuth returns only identity fields on session responses. The `workspaceId`, `currentWorkspaceId`, and `workspaceRole` fields on `AuthUser` were always `undefined` because they don't exist in the database User model and BetterAuth has no `additionalFields` config for them. This caused `persistWorkspaceId()` to silently skip, leaving localStorage empty, which made every `WorkspaceGuard`-protected endpoint fail with "Workspace ID is required". ## Test plan - [x] API: 10 new workspace tests pass (service: 7, controller: 3) - [x] API: All 3325+ existing tests pass - [x] Web: 28 auth-context tests pass (5 rewritten for workspace persistence) - [x] Web: All 1446+ existing tests pass - [x] Typecheck clean (all 3 packages) - [x] Lint clean (all 3 packages) - [ ] Browser: Login → verify localStorage workspace-id returns a UUID - [ ] Browser: Navigate to projects → create project succeeds - [ ] Browser: No "Workspace ID is required" error Closes #534 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jason.woltje added 1 commit 2026-02-28 15:05:58 +00:00
fix(api,web): separate workspace context from auth session (#534)
Some checks failed
ci/woodpecker/push/api Pipeline failed
ci/woodpecker/push/orchestrator Pipeline failed
ci/woodpecker/push/web Pipeline failed
023949f1e0
BetterAuth session responses contain only identity fields — workspace
context (workspaceId, currentWorkspaceId) was never returned, causing
"Workspace ID is required" on every guarded endpoint after login.

Add GET /api/workspaces endpoint (AuthGuard only, no WorkspaceGuard)
that returns user workspace memberships with auto-provisioning for
new users. Frontend auth-context now fetches workspaces after session
check and persists the default to localStorage. Race condition in
auto-provisioning is guarded by re-querying inside the transaction.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
jason.woltje added 1 commit 2026-02-28 15:10:33 +00:00
fix: override serialize-javascript to >=7.0.3 for audit compliance
All checks were successful
ci/woodpecker/push/orchestrator Pipeline was successful
ci/woodpecker/push/web Pipeline was successful
ci/woodpecker/push/api Pipeline was successful
a829271b66
Newly disclosed RCE vulnerability (GHSA-5c6j-r48x-rmvq) in
serialize-javascript <=7.0.2, pulled in as a transitive devDependency
via @nestjs/cli > webpack > terser-webpack-plugin. pnpm override bumps
it to the patched version.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
jason.woltje merged commit 128431ba58 into main 2026-02-28 15:14:30 +00:00
jason.woltje deleted branch feat/workspace-context-api 2026-02-28 15:14:30 +00:00
Sign in to join this conversation.