Skip to content

feat: secure Service Account session config using signed JWT cookies - #67

Open
dylan-klein wants to merge 3 commits into
mainfrom
feat/jwt-session-cookies
Open

feat: secure Service Account session config using signed JWT cookies#67
dylan-klein wants to merge 3 commits into
mainfrom
feat/jwt-session-cookies

Conversation

@dylan-klein

@dylan-klein dylan-klein commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

MOTIVATION

Previously, Service Account configuration parameters (customerId and impersonatedUser) were stored as plain-text cookies in the browser. This made the application vulnerable to session tampering, where a user could manually edit the cookie values in the browser console to switch their tenant context and access other Workspace domains.

MAIN CHANGES

  • JWT Utility: Implemented a lightweight, zero-dependency HS256 JWT utility in src/lib/jwt.ts using Node's native crypto module.
  • Session Packaging: Bundled the configuration parameters into a single signed cep_sa_session cookie using BETTER_AUTH_SECRET as the key.
  • Validation: Updated getServiceAccountConfig in src/lib/sa-session.ts to verify the signature of the session JWT before returning values.
  • Review Fixes: Added an exp (expiration) claim to the JWT payload set to 30 days, matching the browser cookie lifetime, and enforced expiration verification to block token replay attacks.
  • API & Route Updates: Modified /api/auth/sa-config to write the signed JWT on configuration setup and clear it on reset, deleting legacy plain-text cookies.

DESIGN DECISIONS

  • Instead of moving the tenant state to database tables or the URL path (which would require extensive routing refactors), we opted for cryptographically signed JWT cookies. This solves the tampering vulnerability while keeping the application serverless and avoiding database writes on transient sessions.

TAG=agy
CONV=b3471264-2592-4924-b031-267b4bf62326

TAG=agy
CONV=b3471264-2592-4924-b031-267b4bf62326
…verification

- Calculate and add exp claim (30 days) to the signed JWT payload during sa-config setup POST.
- Enforce expiration validation in verifyJwt, returning null if current time is past the exp claim.
- Update unit tests in sa-session.test.ts to verify fallback to env variables on expired JWT session cookie.
- Fix integration tests in sa-config-route.test.ts to verify dynamic exp claims inside signed JWT payloads.

TAG=agy
CONV=b3471264-2592-4924-b031-267b4bf62326
- Updated Next.js middleware in src/proxy.ts to read and cryptographically verify COOKIE_SA_SESSION (cep_sa_session JWT) instead of the deprecated raw cep_sa_customer_id cookie when checking if a Service Account customer is configured.
- Updated integration tests in src/__tests__/integration/proxy.test.ts to sign and pass mock JWT cookies in test requests, ensuring the MCP reachability gate tests don't fail-fast redirect to setup.
- Added new integration tests validating that correct JWT SA sessions allow dashboard access, while invalid/unregistered JWTs redirect to setup.
- Fixed auto-formatting on src/__tests__/unit/auth-errors.test.ts, src/lib/activity-data.ts, and src/lib/session.ts.

TAG=agy
CONV=b3471264-2592-4924-b031-267b4bf62326
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant