- 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>
10 KiB
Tasks: Google OAuth Authentication Screen
Input: Design documents from /specs/001-google-oauth-auth/
Prerequisites: plan.md ✅, spec.md ✅, research.md ✅, data-model.md ✅, contracts/ ✅
Tests: Not explicitly requested - manual testing via quickstart.md
Organization: Tasks grouped by user story to enable independent implementation and testing.
Format: [ID] [P?] [Story] Description
- [P]: Can run in parallel (different files, no dependencies)
- [Story]: Which user story this task belongs to (e.g., US1, US2, US3)
- Include exact file paths in descriptions
Path Conventions
- Web app:
backend/src/,frontend/src/
Phase 1: Setup (Shared Infrastructure)
Purpose: Install dependencies and configure project for OAuth authentication
- T001 Install backend auth dependencies:
cd backend && npm install @nestjs/passport @nestjs/jwt passport passport-google-oauth20 bcrypt - T002 Install backend auth type definitions:
cd backend && npm install -D @types/passport-google-oauth20 @types/bcrypt - T003 [P] Add OAuth environment variables template to backend/.env.example (GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_CALLBACK_URL, JWT_SECRET, JWT_ACCESS_EXPIRATION, JWT_REFRESH_EXPIRATION, FRONTEND_URL)
- T004 [P] Update backend/src/app.module.ts to import ConfigModule with .env support
- T005 Configure CORS in backend/src/main.ts with credentials: true and FRONTEND_URL origin
Phase 2: Foundational (Blocking Prerequisites)
Purpose: Core entities, auth module structure, and database schema that ALL user stories depend on
⚠️ CRITICAL: No user story work can begin until this phase is complete
- T006 Create User entity in backend/src/entities/user.entity.ts with fields: id (UUID), googleId, email, displayName, avatarUrl, createdAt, lastLoginAt
- T007 [P] Create RefreshToken entity in backend/src/entities/refresh-token.entity.ts with fields: id (UUID), userId (FK), token, expiresAt, createdAt, revokedAt
- T008 Register User and RefreshToken entities in backend/src/app.module.ts TypeORM configuration
- T009 Create auth module scaffold in backend/src/modules/auth/ with auth.module.ts, auth.controller.ts, auth.service.ts
- T010 [P] Create DTOs in backend/src/modules/auth/dto/: auth-response.dto.ts, user-response.dto.ts
- T011 [P] Create auth types in frontend/src/types/auth.ts: User interface, AuthState interface
- T012 Create auth store in frontend/src/store/authStore.ts using Zustand with: user, accessToken, isLoading, setAuth, clearAuth, setLoading
- T013 [P] Create auth API client in frontend/src/api/auth.ts with axios instance and methods: refreshToken, getMe, logout
Checkpoint: Foundation ready - user story implementation can now begin
Phase 3: User Story 1 - First-Time User Sign In (Priority: P1) 🎯 MVP
Goal: New user can authenticate via Google OAuth and access the tool page
Independent Test: Visit /tool as logged-out user → redirected to login → click "Sign in with Google" → complete Google OAuth → redirected to /tool with active session
Backend Implementation for User Story 1
- T014 [US1] Implement GoogleStrategy in backend/src/modules/auth/google.strategy.ts using passport-google-oauth20
- T015 [US1] Implement JwtStrategy in backend/src/modules/auth/jwt.strategy.ts for access token validation
- T016 [P] [US1] Create GoogleAuthGuard in backend/src/modules/auth/guards/google-auth.guard.ts
- T017 [P] [US1] Create JwtAuthGuard in backend/src/modules/auth/guards/jwt-auth.guard.ts
- T018 [US1] Implement AuthService.validateOAuthUser() in backend/src/modules/auth/auth.service.ts - creates/updates user, issues tokens
- T019 [US1] Implement AuthService.generateTokens() for JWT access token and refresh token generation with bcrypt hashing
- T020 [US1] Add GET /auth/google route in backend/src/modules/auth/auth.controller.ts with GoogleAuthGuard
- T021 [US1] Add GET /auth/google/callback route in backend/src/modules/auth/auth.controller.ts - handle OAuth callback, set HttpOnly cookie, redirect to frontend
- T022 [US1] Add GET /auth/me route in backend/src/modules/auth/auth.controller.ts with JwtAuthGuard
- T023 [US1] Register AuthModule in backend/src/app.module.ts imports
Frontend Implementation for User Story 1
- T024 [P] [US1] Create LoginPage component in frontend/src/pages/LoginPage.tsx with "Sign in with Google" button using shadcn/ui Button and Card
- T025 [P] [US1] Create AuthCallbackPage component in frontend/src/pages/AuthCallbackPage.tsx to handle OAuth callback token from URL
- T026 [US1] Implement useAuth hook in frontend/src/hooks/useAuth.ts with checkAuth, login redirect, and loading state
- T027 [US1] Create AuthGuard component in frontend/src/components/AuthGuard.tsx - redirects to /login if not authenticated, preserves intended destination in location.state
- T028 [US1] Update frontend/src/App.tsx - add /login route, /auth/callback route, wrap /tool with AuthGuard
- T029 [US1] Handle OAuth errors in LoginPage - display error message from URL params if authentication failed
Checkpoint: User Story 1 complete - new users can sign in via Google and access protected routes
Phase 4: User Story 2 - Returning User Session (Priority: P2)
Goal: Returning user with valid session bypasses login; expired session redirects to login
Independent Test: Sign in → close browser → return within 7 days → automatically access /tool without login prompt
Backend Implementation for User Story 2
- T030 [US2] Add POST /auth/refresh route in backend/src/modules/auth/auth.controller.ts - validate HttpOnly refresh cookie, rotate token, return new access token
- T031 [US2] Implement AuthService.refreshTokens() in backend/src/modules/auth/auth.service.ts - validate stored token, check expiry, issue new pair
- T032 [US2] Implement AuthService.revokeRefreshToken() for token rotation (mark old token as revoked)
Frontend Implementation for User Story 2
- T033 [US2] Add silent token refresh on app mount in frontend/src/hooks/useAuth.ts - call /auth/refresh on startup
- T034 [US2] Add Axios response interceptor in frontend/src/api/auth.ts - on 401, attempt refresh, retry original request
- T035 [US2] Update AuthGuard in frontend/src/components/AuthGuard.tsx - show loading state during session check, then redirect or render
Checkpoint: User Story 2 complete - returning users have seamless session persistence
Phase 5: User Story 3 - User Sign Out (Priority: P3)
Goal: Authenticated user can sign out, which clears session and returns to login
Independent Test: Sign in → click sign out in user menu → redirected to login → cannot access /tool
Backend Implementation for User Story 3
- T036 [US3] Add POST /auth/logout route in backend/src/modules/auth/auth.controller.ts with JwtAuthGuard - revoke refresh token, clear cookie
Frontend Implementation for User Story 3
- T037 [P] [US3] Create UserMenu component in frontend/src/components/UserMenu.tsx - display user avatar, name, sign out button using shadcn/ui
- T038 [US3] Implement logout function in frontend/src/store/authStore.ts - call /auth/logout, clear local state
- T039 [US3] Add UserMenu to ToolPage header in frontend/src/pages/ToolPage.tsx
Checkpoint: User Story 3 complete - users can sign out securely
Phase 6: Polish & Cross-Cutting Concerns
Purpose: Error handling, edge cases, and final validation
- T040 [P] Add error handling for OAuth failures in backend/src/modules/auth/auth.controller.ts - redirect to frontend with error param
- T041 [P] Add error display component for auth errors in frontend/src/pages/LoginPage.tsx - show user-friendly messages
- T042 Update Vite proxy configuration in frontend/vite.config.ts if needed for /api/auth routes
- T043 Validate implementation against quickstart.md test scenarios
- T044 Run ESLint and fix any linting errors in new files
Dependencies & Execution Order
Phase Dependencies
- Setup (Phase 1): No dependencies - can start immediately
- Foundational (Phase 2): Depends on Setup completion - BLOCKS all user stories
- User Stories (Phase 3-5): All depend on Foundational phase completion
- Polish (Phase 6): Depends on all user stories being complete
User Story Dependencies
- User Story 1 (P1): Can start after Foundational (Phase 2) - Core sign-in flow
- User Story 2 (P2): Can start after Foundational (Phase 2) - Extends US1 with token refresh, but independently testable
- User Story 3 (P3): Can start after Foundational (Phase 2) - Adds logout to US1, independently testable
Within Each User Story
- Backend before frontend (API must exist before UI calls it)
- Entity/strategy before service
- Service before controller
- Controller before frontend integration
Parallel Opportunities
Phase 1 (Setup):
T003 [P] + T004 [P] can run in parallel
Phase 2 (Foundational):
T006 + T007 [P] can run in parallel (different entity files)
T010 [P] + T011 [P] + T013 [P] can run in parallel (DTOs, types, API client)
Phase 3 (User Story 1):
T016 [P] + T017 [P] can run in parallel (different guard files)
T024 [P] + T025 [P] can run in parallel (different page files)
Implementation Strategy
MVP First (User Story 1 Only)
- Complete Phase 1: Setup (T001-T005)
- Complete Phase 2: Foundational (T006-T013)
- Complete Phase 3: User Story 1 (T014-T029)
- STOP and VALIDATE: Test sign-in flow per quickstart.md
- Deploy/demo if ready - users can now sign in!
Incremental Delivery
- Complete Setup + Foundational → Foundation ready
- Add User Story 1 → Test independently → Deploy (MVP with sign-in)
- Add User Story 2 → Test independently → Deploy (session persistence)
- Add User Story 3 → Test independently → Deploy (full auth with logout)
- Complete Polish → Final validation → Production ready
Task Count by Story
| Phase | Task Count |
|---|---|
| Setup | 5 |
| Foundational | 8 |
| User Story 1 | 16 |
| User Story 2 | 6 |
| User Story 3 | 4 |
| Polish | 5 |
| Total | 44 |
Notes
- [P] tasks = different files, no dependencies
- [Story] label maps task to specific user story for traceability
- Each user story is independently completable and testable
- Backend entities use UUID primary keys per constitution
- Frontend uses shadcn/ui components per constitution
- HttpOnly cookies for refresh tokens per research.md security recommendations