Files
Andre 130f35c4f8 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>
2026-01-29 13:05:18 -03:00

222 lines
10 KiB
Markdown

# 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
- [x] T001 Install backend auth dependencies: `cd backend && npm install @nestjs/passport @nestjs/jwt passport passport-google-oauth20 bcrypt`
- [x] T002 Install backend auth type definitions: `cd backend && npm install -D @types/passport-google-oauth20 @types/bcrypt`
- [x] 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)
- [x] T004 [P] Update backend/src/app.module.ts to import ConfigModule with .env support
- [x] 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
- [x] T006 Create User entity in backend/src/entities/user.entity.ts with fields: id (UUID), googleId, email, displayName, avatarUrl, createdAt, lastLoginAt
- [x] T007 [P] Create RefreshToken entity in backend/src/entities/refresh-token.entity.ts with fields: id (UUID), userId (FK), token, expiresAt, createdAt, revokedAt
- [x] T008 Register User and RefreshToken entities in backend/src/app.module.ts TypeORM configuration
- [x] T009 Create auth module scaffold in backend/src/modules/auth/ with auth.module.ts, auth.controller.ts, auth.service.ts
- [x] T010 [P] Create DTOs in backend/src/modules/auth/dto/: auth-response.dto.ts, user-response.dto.ts
- [x] T011 [P] Create auth types in frontend/src/types/auth.ts: User interface, AuthState interface
- [x] T012 Create auth store in frontend/src/store/authStore.ts using Zustand with: user, accessToken, isLoading, setAuth, clearAuth, setLoading
- [x] 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
- [x] T014 [US1] Implement GoogleStrategy in backend/src/modules/auth/google.strategy.ts using passport-google-oauth20
- [x] T015 [US1] Implement JwtStrategy in backend/src/modules/auth/jwt.strategy.ts for access token validation
- [x] T016 [P] [US1] Create GoogleAuthGuard in backend/src/modules/auth/guards/google-auth.guard.ts
- [x] T017 [P] [US1] Create JwtAuthGuard in backend/src/modules/auth/guards/jwt-auth.guard.ts
- [x] T018 [US1] Implement AuthService.validateOAuthUser() in backend/src/modules/auth/auth.service.ts - creates/updates user, issues tokens
- [x] T019 [US1] Implement AuthService.generateTokens() for JWT access token and refresh token generation with bcrypt hashing
- [x] T020 [US1] Add GET /auth/google route in backend/src/modules/auth/auth.controller.ts with GoogleAuthGuard
- [x] 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
- [x] T022 [US1] Add GET /auth/me route in backend/src/modules/auth/auth.controller.ts with JwtAuthGuard
- [x] T023 [US1] Register AuthModule in backend/src/app.module.ts imports
### Frontend Implementation for User Story 1
- [x] T024 [P] [US1] Create LoginPage component in frontend/src/pages/LoginPage.tsx with "Sign in with Google" button using shadcn/ui Button and Card
- [x] T025 [P] [US1] Create AuthCallbackPage component in frontend/src/pages/AuthCallbackPage.tsx to handle OAuth callback token from URL
- [x] T026 [US1] Implement useAuth hook in frontend/src/hooks/useAuth.ts with checkAuth, login redirect, and loading state
- [x] T027 [US1] Create AuthGuard component in frontend/src/components/AuthGuard.tsx - redirects to /login if not authenticated, preserves intended destination in location.state
- [x] T028 [US1] Update frontend/src/App.tsx - add /login route, /auth/callback route, wrap /tool with AuthGuard
- [x] 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
- [x] 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
- [x] T031 [US2] Implement AuthService.refreshTokens() in backend/src/modules/auth/auth.service.ts - validate stored token, check expiry, issue new pair
- [x] T032 [US2] Implement AuthService.revokeRefreshToken() for token rotation (mark old token as revoked)
### Frontend Implementation for User Story 2
- [x] T033 [US2] Add silent token refresh on app mount in frontend/src/hooks/useAuth.ts - call /auth/refresh on startup
- [x] T034 [US2] Add Axios response interceptor in frontend/src/api/auth.ts - on 401, attempt refresh, retry original request
- [x] 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
- [x] 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
- [x] T037 [P] [US3] Create UserMenu component in frontend/src/components/UserMenu.tsx - display user avatar, name, sign out button using shadcn/ui
- [x] T038 [US3] Implement logout function in frontend/src/store/authStore.ts - call /auth/logout, clear local state
- [x] 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
- [x] T040 [P] Add error handling for OAuth failures in backend/src/modules/auth/auth.controller.ts - redirect to frontend with error param
- [x] T041 [P] Add error display component for auth errors in frontend/src/pages/LoginPage.tsx - show user-friendly messages
- [x] T042 Update Vite proxy configuration in frontend/vite.config.ts if needed for /api/auth routes
- [x] T043 Validate implementation against quickstart.md test scenarios
- [x] 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)
1. Complete Phase 1: Setup (T001-T005)
2. Complete Phase 2: Foundational (T006-T013)
3. Complete Phase 3: User Story 1 (T014-T029)
4. **STOP and VALIDATE**: Test sign-in flow per quickstart.md
5. Deploy/demo if ready - users can now sign in!
### Incremental Delivery
1. Complete Setup + Foundational → Foundation ready
2. Add User Story 1 → Test independently → Deploy (MVP with sign-in)
3. Add User Story 2 → Test independently → Deploy (session persistence)
4. Add User Story 3 → Test independently → Deploy (full auth with logout)
5. 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