# Implementation Plan: User Profile ## Overview This plan implements the user profile feature in three phases: backend API routes first (profile endpoint and password change endpoint on the existing auth router), then the frontend components (UserProfilePanel modal and UserMenu theming/integration), and finally wiring everything together. Each task builds incrementally on the previous one, and testing tasks are placed close to the code they validate. ## Tasks - [ ] 1. Add backend profile and password change routes to `routes/auth.js` - [x] 1.1 Add `GET /api/auth/profile` route - Add a new route inside `createAuthRouter` that queries the `users` table for `id, username, email, user_group, created_at, last_login` using the session user's ID - Return `{ id, username, email, group, created_at, last_login }` on success - Return 401 if the account is inactive (with `is_active = 0`), clearing the session cookie - Use the existing `requireAuth(db)` middleware for authentication - _Requirements: 4.1, 4.2, 4.3_ - [x] 1.2 Add `POST /api/auth/change-password` route with rate limiting - Add `express-rate-limit` middleware scoped to this route: 5 requests per 15-minute window, keyed by `req.cookies.session_id` - Validate request body has `currentPassword` and `newPassword` fields; return 400 if missing - Validate `newPassword` is at least 8 characters; return 400 if too short - Query the user's `password_hash` and `is_active` from the database; return 401 if account is inactive - Use `bcrypt.compare` to verify `currentPassword`; return 401 if incorrect - Hash the new password with `bcrypt.hash(newPassword, 10)` and update the `password_hash` column - Call `logAudit` with action `password_change`, entityType `auth` - Return `{ message: 'Password changed successfully' }` on success - Return 429 with appropriate message when rate limit is exceeded - _Requirements: 2.2, 2.3, 2.7, 2.8, 5.1, 5.2, 5.3, 5.4_ - [x] 1.3 Write property tests for password change round-trip (backend) - **Property 3: Password change round-trip** — For any valid current password and any new password of 8+ characters, after a successful change, `bcrypt.compare(newPassword, storedHash)` returns true - **Validates: Requirements 2.2, 2.7** - [x] 1.4 Write property tests for incorrect password rejection (backend) - **Property 4: Incorrect current password is always rejected** — For any password string that does not match the user's current password, the endpoint returns 401 and the stored hash remains unchanged - **Validates: Requirements 2.3** - [x] 1.5 Write property tests for short password rejection (backend) - **Property 6 (server-side): Short passwords are rejected** — For any string of length 0–7, `POST /api/auth/change-password` returns 400 and the stored hash remains unchanged - **Validates: Requirements 2.5, 5.4** - [x] 2. Checkpoint — Verify backend routes - Ensure all tests pass, ask the user if questions arise. - [x] 3. Create `UserProfilePanel.js` frontend component - [x] 3.1 Create the `UserProfilePanel` modal component - Create `frontend/src/components/UserProfilePanel.js` - Accept `isOpen` and `onClose` props - On open, fetch `GET /api/auth/profile` with `credentials: 'include'` and display loading state - Render profile info section showing: username, email, group, created_at (formatted), last_login (formatted) - Use dark theme inline styles matching the design system (intel-card gradient background, accent borders, light text colors from `DESIGN_SYSTEM.md`) - Include a close button (X icon from lucide-react) and click-outside-to-close behavior - Display error state with retry option if profile fetch fails - Use icons from lucide-react (User, Mail, Shield, Calendar, Clock) - _Requirements: 1.1, 1.2, 1.3, 1.4_ - [x] 3.2 Add password change form to `UserProfilePanel` - Add a password change section below the profile info with three fields: current password, new password, confirm new password - Implement client-side validation: new password must match confirm password; new password must be >= 8 characters - Display inline validation errors below the relevant fields - On submit, call `POST /api/auth/change-password` with `{ currentPassword, newPassword }` - Handle API error responses (401 wrong password, 429 rate limited, 400 validation, 500 server error) and display appropriate messages - On success, show success message and clear all form fields - Style the form with dark theme inline styles (intel-input styling, intel-button-primary for submit) - _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6_ - [x] 3.3 Write property test for profile panel field rendering - **Property 1: Profile panel displays all required fields** — For any valid profile object with arbitrary username, email, group, created_at, and last_login values, rendering UserProfilePanel displays all five values in the output - **Validates: Requirements 1.2** - [x] 3.4 Write property test for mismatched password confirmation - **Property 5: Mismatched password confirmation is rejected client-side** — For any two distinct strings used as newPassword and confirmPassword, the form displays a validation error and does not submit a request - **Validates: Requirements 2.4** - [x] 3.5 Write property test for short password client-side rejection - **Property 6 (client-side): Short passwords are rejected** — For any string of length 0–7, the form displays a minimum-length validation error and does not submit a request - **Validates: Requirements 2.5** - [x] 4. Checkpoint — Verify frontend component - Ensure all tests pass, ask the user if questions arise. - [x] 5. Modify `UserMenu.js` for dark theming and profile integration - [x] 5.1 Convert `UserMenu.js` from light theme to dark theme - Replace Tailwind light-theme classes with inline dark-theme style objects - Button: `hover:bg-gray-100` → `rgba(14, 165, 233, 0.1)` hover background - Username text: `text-gray-900` → `#F8FAFC` (design system `--text-primary`) - Group label text: `text-gray-500` → `#E2E8F0` (design system `--text-secondary`) - Chevron icon: `text-gray-500` → `#E2E8F0` - Dropdown panel: `bg-white` → intel-card gradient background; `border-gray-200` → `rgba(14, 165, 233, 0.3)` - Dropdown items: `text-gray-700` → `#F8FAFC`; `hover:bg-gray-50` → `rgba(14, 165, 233, 0.1)` - Sign out text: `text-red-600` → `#F87171`; `hover:bg-red-50` → `rgba(239, 68, 68, 0.1)` - Dropdown header: `text-gray-900` → `#F8FAFC`; `text-gray-500` → `#94A3B8`; `border-gray-100` → `rgba(14, 165, 233, 0.2)` - Retain existing group badge color-coding logic - _Requirements: 3.1, 3.2, 3.3, 3.4, 6.1, 6.2, 6.3, 6.4, 6.5_ - [x] 5.2 Add "My Profile" menu item and wire `UserProfilePanel` - Import `UserProfilePanel` component - Add `showProfile` state variable - Add a "My Profile" menu item with `User` icon between the dropdown header and the admin-only actions - On click, close the dropdown and set `showProfile` to `true` - Render ` setShowProfile(false)} />` in the component output - _Requirements: 1.1, 1.3_ - [x] 5.3 Write property test for profile API data completeness - **Property 2: Profile API returns complete user data matching database** — For any active user record, a GET request to `/api/auth/profile` with that user's valid session returns an object with `id`, `username`, `email`, `group`, `created_at`, and `last_login` fields matching the database - **Validates: Requirements 4.1** - [x] 6. Final checkpoint — Ensure all tests pass - Ensure all tests pass, ask the user if questions arise. ## Notes - Tasks marked with `*` are optional and can be skipped for faster MVP - Each task references specific requirements for traceability - Checkpoints ensure incremental validation - Property tests use `fast-check` (already installed in `frontend/package.json` as a devDependency) - Backend tests for properties 3, 4, and 6 (server-side) will need `fast-check` added to the root `package.json` devDependencies - The existing `express-rate-limit` package (already in root `package.json`) is used for the password change rate limiter - No database migrations are needed — the existing `users` table has all required columns - All styling follows the dark theme design system documented in `DESIGN_SYSTEM.md`