fix(auth): sign dev-session cookie + audit-log dev-bypass attribution + isolation doc#3471
Merged
Merged
Conversation
… + isolation doc Three security-medium items deferred from PR #3448 review: - Sign the dev-session cookie. Was the literal user key string ("admin" / "member"); anyone who could write a cookie on the domain (XSS, sibling subdomain) could pick a privileged dev user. Now HMAC-signed with a per-process secret generated at boot — invalid signatures fail verification on read. Format: ${userKey}.${base64url-hmac}. Timing- safe comparison. Cookies don't survive a server restart, which is the desired property: a cookie minted on someone else's box won't work on yours. - Audit-log dev-bypass attribution. resolveUserOrgMembership now returns via_dev_bypass: boolean. Routes that write registry_audit_log tag dev-bypass writes with details.auth_method = 'dev-bypass' so post- incident triage can distinguish them from real-user writes (synthetic user_dev_* IDs don't resolve in WorkOS). Applied to org rename + org settings PATCH; pattern documented for new audit-log writers. - New ops/dev-mode-isolation.md doc — operational invariants the bypass assumes (no shared dev/prod DB, no shared .env.local, prod-boot guard verification on every deploy). Incident response steps if dev mode ever activates in prod. Tests: 6 unit tests for cookie signing (round-trip, legacy format rejected, tampered sig rejected, forged user-key with valid sig from other key rejected, unknown user rejected, empty/missing rejected). 86/86 pass on the impacted integration suite. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The dev-session-signing test had a vi.mock with `...actual` spread — effectively a no-op, but vi.mock factories are hoisted ahead of setupFiles, so the auth.js import via importOriginal evaluated with WORKOS_API_KEY undefined and crashed at the top-level `new WorkOS(process.env.WORKOS_API_KEY!)`. Drop the mock; setupFiles sets the mock API key in time when the import runs normally. Plus merge origin/main: the audit-log-IP/UA capture from main and the dev-bypass tagging from this branch combine cleanly in details.
…need WorkOS env CI runs from repo root and picks up the root vitest.config.ts (no setupFiles), not server/vitest.config.ts. So the WORKOS_API_KEY mock that makes auth.ts loadable in local tests isn't set in CI. Stub the WorkOS module directly so the import doesn't depend on env-var ordering.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three security-medium hardening items deferred from the PR #3448 review.
Sign the dev-session cookie. Pre-PR the cookie was the literal user-key string (
admin,member); anyone who could set a cookie on the domain could pick a privileged dev user. Now HMAC-signed with a per-process secret generated at boot — invalid signatures fail verification on read, and cookies don't survive a server restart. Format:${userKey}.${base64url-hmac}. Timing-safe comparison.Audit-log dev-bypass attribution.
resolveUserOrgMembershipnow returnsvia_dev_bypass: boolean. Routes that write toregistry_audit_logtag dev-bypass writes withdetails.auth_method = 'dev-bypass'so post-incident triage can distinguish them from real-user writes (syntheticuser_dev_*IDs don't resolve via WorkOS). Applied to the highest-impact mutating routes (org rename, settings PATCH); pattern documented for new audit-log writers.ops/dev-mode-isolation.md— operational invariants the bypass assumes (no shared dev/prod DB, no shared.env.local, prod-boot guard verification on every deploy). Incident response steps if dev mode ever activates in prod.Test plan
npm run typecheckcleanresolve-user-org-membership.test.tsupdated for the newvia_dev_bypassfield;membership-webhook,find-paying-org-for-domain,primary-organization-resolutionunaffected)dev-session=adminno longer authenticates🤖 Generated with Claude Code