Skip to content

feat(server): cookie lifecycle & CSRF enforcement (#749)#1117

Open
theothersideofgod wants to merge 9 commits intomainfrom
feat/cookie-lifecycle-csrf-749
Open

feat(server): cookie lifecycle & CSRF enforcement (#749)#1117
theothersideofgod wants to merge 9 commits intomainfrom
feat/cookie-lifecycle-csrf-749

Conversation

@theothersideofgod
Copy link
Copy Markdown
Contributor

Summary

  • Cookie management: Auto-set constructive_session cookie on auth mutation success, clear on sign-out
  • CSRF protection: Double-submit cookie pattern for cookie-authenticated requests
  • Device tokens: Set constructive_device_token for trusted device tracking
  • Remember me: Support rememberMeDuration for extended session cookies

Implementation

Uses grafserv plugin instead of Express middleware because grafserv bypasses res.json() and res.cookie().

Client → Express middleware → grafserv → PostgreSQL
                                ↓
                        AuthCookiePlugin
                        (intercepts BufferResult,
                         injects Set-Cookie headers)

Files

File Change
middleware/cookie.ts Cookie utilities
plugins/auth-cookie-plugin.ts grafserv middleware plugin
server.ts Wire CSRF middleware
middleware/graphile.ts Add plugin to preset
types.ts + api.ts Add rememberMeDuration

Tests

85 tests covering:

  • Auth failure scenarios (7 tests)
  • Cookie clearing completeness (6 tests)
  • grafserv Buffer parsing (16 tests)
  • CSRF integration (14 tests)

Closes #749

🤖 Generated with Claude Code

@theothersideofgod theothersideofgod force-pushed the feat/cookie-lifecycle-csrf-749 branch 2 times, most recently from fa376a0 to e5e066b Compare May 11, 2026 07:38
theothersideofgod and others added 9 commits May 11, 2026 18:44
- Add SESSION_COOKIE_NAME and DEVICE_TOKEN_COOKIE_NAME constants
- Add getSessionCookieConfig() with rememberMe support
- Add getDeviceTokenCookieConfig() for 90-day device tokens
- Add cookie serialization helpers (set/clear)
- Add parseCookieValue() and request token extractors

Closes #749

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add @constructive-io/csrf dependency
- Wire csrfSetToken middleware (httpOnly=false for SPA access)
- Wire csrfProtect middleware on /graphql endpoint
- Skip CSRF for Bearer token auth (not vulnerable)
- Skip CSRF for anonymous requests (no session cookie)
- Add integration tests for CSRF skip conditions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add rememberMeDuration field to AuthSettings interface
- Query remember_me_duration from app_settings_auth table
- Used by cookie config when rememberMe=true in sign-in

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Implement grafserv middleware plugin to set/clear auth cookies
- Intercept signIn/signUp/SSO/MFA mutations to set session cookie
- Intercept signOut/revokeSession to clear cookies
- Handle device token cookies for trusted device tracking
- Parse grafserv BufferResult and inject Set-Cookie headers
- Support both camelCase and snake_case token fields
- Support nested result objects

Includes comprehensive tests:
- Auth failure scenarios (errors, null data, invalid token types)
- Cookie clearing completeness (session + device token)
- Environment-based security attributes
- Grafserv Buffer parsing and header merging

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add AuthCookiePlugin to graphile preset plugins array
- Remove Express middleware approach (doesn't work with grafserv)
- Add CSRF middleware after authenticate, before graphile
- Update server.ts middleware order

Middleware chain:
cors → api → authenticate → captcha → csrf → graphile (with AuthCookiePlugin)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add CSRF error detection to error handler so CSRF validation failures
return proper 403 Forbidden status instead of generic 500.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add cookie-parser middleware to support CSRF double-submit pattern
- Parse GraphQL body from grafserv's getBody() buffer in AuthCookiePlugin
- Set cookies directly on Express response to ensure proper HTTP headers
- Fix NaN maxAge by handling unparseable authSettings values

The AuthCookiePlugin now correctly intercepts auth mutations and sets
session cookies via the Express response, ensuring multiple Set-Cookie
headers are sent separately as required by HTTP spec.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Read constructive_device_token cookie in auth middleware
- Attach to req.deviceToken for downstream access
- Pass as jwt.claims.device_token to DB procedures
- Enables trusted device recognition in sign-in flows

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Test device token cookie parsing in auth middleware
- Test device token context passing in graphile preset
- Covers edge cases: missing cookie, URL encoding, special chars

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@theothersideofgod theothersideofgod force-pushed the feat/cookie-lifecycle-csrf-749 branch from 3f7ab3d to 9b0e35f Compare May 11, 2026 10:45
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