Feature: Secure Cookie Authentication with Session Management
Metadata
- Issue ID: FEAT-31
- Status: Done
- Owner: beansint
- Related PRs: StudyBoostIO/StudyboostV2#49
Overview
Implements secure authentication using HTTP-only cookies, short-lived access tokens, rotating refresh tokens, and database-backed session management for multi-device login. This is the baseline for protected features such as uploads, AI usage, premium access, and user personalization.
Frontend Behavior
- Dedicated auth pages are the only auth entry points:
/login,/signup. - Login page accepts
email or usernamepluspassword. - Signup page captures
email,username,password, andconfirm password(optional first/last name). - Auth requests include
credentials: 'include'so cookie-based sessions work. - Successful login/signup stores auth state in cookies only (no localStorage token usage).
- Logout clears auth cookies and ends the active session.
- Unauthenticated access to protected routes/actions redirects to
/login?next=<target>. - No in-page auth modal fallback in FEAT-31.
Backend Behavior
POST /auth/signup— validates payload, hashes password with bcrypt, creates user inusers, creates session inauth_sessions, sends email verification token viaemail_verification_tokens, setsaccess_token+refresh_tokencookies.POST /auth/login— authenticates byidentifier(emailorusername) +password, creates a new session row per login (multi-device support), sets auth cookies.POST /auth/refresh— validates the refresh token from the cookie, rotates the refresh token and updates the session hash, issues a new short-lived access token.POST /auth/logout— clears auth cookies, invalidates the active session inauth_sessions.GET /auth/me— requires authentication, returns the current user profile.POST /auth/verify-email— marks the user email verified when the token is valid.POST /auth/resend-verification— reissues a verification token for unverified users.
Cookie and token policy:
access_token: short-lived (default 15 minutes).refresh_token: long-lived (7-30 days, default 30 days).- Tokens stored in HTTP-only cookies only (no localStorage).
- Hosted env: cookie domain shared on
.studyboost.com; local dev: host-only cookie domain for localhost compatibility.
Security and guard behavior:
- Access token validated from cookies; expired access token auto-refreshes via a valid refresh token.
- Session validation checks
auth_sessionson protected routes. - Refresh tokens are stored hashed only and rotated on every refresh.
- Rate limiting enabled on auth endpoints.
Protected routes in this scope: notifications controller endpoints; subscriptions/my-subscription, subscriptions/my-usage, usage mutation debug routes; documents/upload and documents/summarize.
QA Test Scenarios
| Scenario ID | Description | Steps | Input | Expected Result |
|---|---|---|---|---|
| FEAT-31-01 | Signup creates session and cookies | Call POST /auth/signup with valid payload | Valid email, username, password | Returns success, creates users + auth_sessions rows, sets auth cookies |
| FEAT-31-02 | Login creates separate device sessions | Login from two clients | Same valid credentials on two devices | Two valid auth_sessions rows for the same user |
| FEAT-31-03 | Access token allows protected route | Call protected endpoint with valid cookies | Valid access_token cookie | Request succeeds |
| FEAT-31-04 | Expired access token auto-refreshes | Use expired access token with valid refresh token on protected route | Expired access + valid refresh | Request succeeds and cookies are rotated |
| FEAT-31-05 | Refresh replay is blocked | Reuse old refresh token after rotation | Stale refresh token | Request fails with unauthorized |
| FEAT-31-06 | Logout invalidates active session | Call POST /auth/logout then call protected route with old cookies | Valid auth cookies then reused cookies | Cookies cleared, session removed, protected route returns unauthorized |
| FEAT-31-07 | Missing auth cookies are rejected | Call protected route without cookies | No cookies | 401 unauthorized response |
| FEAT-31-08 | Login throttling | Repeated failed login attempts within throttle window | Wrong password repeatedly | Rate-limit response after threshold |
| FEAT-31-09 | Verify email flow | Call verify endpoint with a valid token | Valid verification token | User email marked verified; token marked used |
| FEAT-31-10 | Subdomain cookie policy | Test hosted env cookie settings | Hosted env request | Cookies include .studyboost.com domain and secure policy |
Edge Cases
- Stolen or replayed refresh token after rotation is rejected.
- Expired session row while the access token is still present.
- Missing/invalid cookies on protected endpoints.
- Unverified email account logging in before verification.
- CORS origin not in the allow-list for credentialed requests.
Notes
- Feature flags: None.
- Dependencies:
@nestjs/jwt,@nestjs/throttler, Prisma models (auth_sessions,email_verification_tokens), Mail module. - Known limitations: No account lockout or CAPTCHA in this scope.
- Dependency contract: Future AI/billing/premium/upload protections must reuse FEAT-31 cookie/session guards and include FEAT-level auth QA scenarios.
OAuth and Reset Integration
OAuth callbacks (Google in FEAT-105) resolve/link the user account, then issue auth cookies through the same createSessionForUser pipeline used by email/password login. Password reset completion in FEAT-105 revokes every row in auth_sessions for the affected user after writing the new password hash, enforcing global sign-out across devices.
Related Docs
- Features contract:
/docs/features - QA requirements:
/docs/features/qa-requirements - Database schema:
/docs/database-schema - Core systems:
/docs/core-systems - Implementation-level source of truth:
docs/features/FEAT-31-secure-cookie-auth-session-management.md