feat: implement Google OAuth authentication
- 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>
This commit is contained in:
114
specs/001-google-oauth-auth/research.md
Normal file
114
specs/001-google-oauth-auth/research.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# 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-oauth20` is 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**:
|
||||
1. Frontend links to `/api/auth/google` (backend initiates OAuth)
|
||||
2. NestJS redirects to Google consent screen
|
||||
3. Google redirects to `/api/auth/google/callback`
|
||||
4. Backend validates tokens, creates/updates user, issues JWT
|
||||
5. Backend redirects to frontend `/auth/callback?token=...`
|
||||
6. 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 `googleId` for OAuth lookups
|
||||
- Index `email` for user queries
|
||||
Reference in New Issue
Block a user