- Add Google OAuth 2.0 login flow with passport-google-oauth20 - Create User and RefreshToken entities for session management - Implement JWT access tokens (15min) + HttpOnly refresh cookies (7 days) - Add auth endpoints: /google, /google/callback, /refresh, /me, /logout - Create LoginPage with Google sign-in button (shadcn/ui) - Add AuthGuard for protected routes with redirect preservation - Implement silent token refresh on app mount - Add UserMenu component with avatar and sign-out Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
3.9 KiB
3.9 KiB
Research: Google OAuth Authentication
Feature: 001-google-oauth-auth Date: 2026-01-29
1. NestJS Passport Google OAuth Setup
Decision: Use @nestjs/passport with passport-google-oauth20 strategy
Rationale:
- Official NestJS integration provides decorator-based guards and strategies
passport-google-oauth20is the actively maintained Google OAuth package- Seamless integration with NestJS dependency injection and module system
- Built-in validate method simplifies user profile handling
Alternatives Considered:
passport-google-oauth2: Older package, less TypeScript support- Custom OAuth implementation: Too much boilerplate, harder to maintain
2. Authentication Strategy for SPAs
Decision: Hybrid approach - JWT access tokens (short-lived, in-memory) + HttpOnly cookie refresh tokens (7-day)
Rationale:
- Access tokens in memory provide stateless, scalable API authentication
- HttpOnly refresh tokens prevent XSS attacks while enabling token rotation
- Best balance between security, UX, and distributed system requirements
- Industry standard for modern SPAs
Alternatives Considered:
- Pure JWT (stateless): Can't invalidate tokens on logout/compromise
- Pure sessions: Doesn't scale well, requires sticky sessions
- localStorage for tokens: Vulnerable to XSS attacks
3. Token Storage in React
Decision: Access tokens in Zustand store (memory), refresh tokens in HttpOnly cookies
Rationale:
- HttpOnly cookies are inaccessible to JavaScript, preventing XSS token theft
- In-memory storage for short-lived access tokens (15-30 min)
- CSRF protection via
sameSite: 'strict'and CORS configuration - Silent refresh mechanism restores access token on page reload
Alternatives Considered:
- localStorage: Vulnerable to XSS attacks
- sessionStorage: Data lost on tab close, still XSS vulnerable
4. React Router Protected Routes
Decision: Layout-based protection using AuthGuard wrapper component with Navigate
Rationale:
- React Router v7 provides clean, declarative route protection
- Easy to preserve intended destination via
location.state.from - Simple loading state handling during auth check
Alternatives Considered:
- Higher-Order Component (HOC): More verbose in modern React
- Route-level guards: Duplicates auth logic across routes
5. OAuth Redirect Flow Implementation
Decision: Backend-initiated flow with frontend callback handler
Flow:
- Frontend links to
/api/auth/google(backend initiates OAuth) - NestJS redirects to Google consent screen
- Google redirects to
/api/auth/google/callback - Backend validates tokens, creates/updates user, issues JWT
- Backend redirects to frontend
/auth/callback?token=... - Frontend stores token, clears URL, redirects to intended destination
Rationale:
- NestJS handles OAuth state management and token exchange securely
- Supports TypeORM user creation/lookup with transaction safety
- Token cleared from URL immediately for security
Alternatives Considered:
- Frontend-initiated PKCE flow: More complex state management
- Popup-based flow: Poor UX on mobile, blocked by some browsers
6. Session Validity & Token Expiration
Decision:
- Access tokens: 15 minutes (short-lived for security)
- Refresh tokens: 7 days (per spec assumptions)
- Automatic token refresh via 401 interceptor in Axios
Rationale:
- Short access tokens limit exposure window if compromised
- 7-day refresh aligns with spec requirements
- Auto-refresh provides seamless UX
Implementation Notes
CORS Configuration Required:
origin: FRONTEND_URL
credentials: true (allow cookies)
Environment Variables Needed:
- GOOGLE_CLIENT_ID
- GOOGLE_CLIENT_SECRET
- GOOGLE_CALLBACK_URL
- JWT_SECRET
- JWT_ACCESS_EXPIRATION (15m)
- JWT_REFRESH_EXPIRATION (7d)
- FRONTEND_URL
TypeORM Indexes:
- Index
googleIdfor OAuth lookups - Index
emailfor user queries