## ADDED Requirements ### Requirement: Auth.js v5 configuration The system SHALL configure Auth.js v5 (next-auth@5) in `src/auth.ts` with JWT session strategy, Credentials provider, and Google OAuth provider. The configuration SHALL export `handlers`, `auth`, `signIn`, and `signOut` functions. #### Scenario: Auth config exports - **WHEN** `src/auth.ts` is imported - **THEN** it exports `handlers` (GET/POST), `auth` (session getter), `signIn`, and `signOut` functions #### Scenario: JWT session strategy - **WHEN** a user authenticates successfully - **THEN** a JWT token is issued containing `user.id`, `user.email`, and `user.name` - **AND** no database session record is created #### Scenario: JWT callbacks embed user ID - **WHEN** the JWT callback fires after sign-in - **THEN** the `token.id` is set to the user's UUID from the database - **AND** the session callback maps `token.id` to `session.user.id` ### Requirement: Credentials provider with email/password The Credentials provider SHALL accept `email` and `password` fields. The `authorize` function SHALL look up the user by email in the `users` table, verify the password against the stored `password_hash` using bcryptjs, and return the user object on success or `null` on failure. #### Scenario: Valid credentials - **WHEN** a user submits correct email and password - **THEN** the authorize function returns the user object with `id`, `email`, `name` - **AND** a JWT session is created #### Scenario: Invalid password - **WHEN** a user submits correct email but wrong password - **THEN** the authorize function returns `null` - **AND** no session is created #### Scenario: Non-existent email - **WHEN** a user submits an email that does not exist in the database - **THEN** the authorize function returns `null` #### Scenario: OAuth-only user attempts password login - **WHEN** a user who registered via Google (no password_hash) submits their email with any password - **THEN** the authorize function returns `null` ### Requirement: Google OAuth provider The Google OAuth provider SHALL be configured with `AUTH_GOOGLE_ID` and `AUTH_GOOGLE_SECRET` environment variables. On first Google sign-in, the system SHALL create a new user record with `provider: 'google'` and `provider_account_id` set to the Google sub ID. On subsequent sign-ins, the system SHALL find the existing user by email. #### Scenario: First Google sign-in creates user - **WHEN** a user signs in with Google for the first time - **THEN** a new user record is created with `provider: 'google'`, `password_hash: null`, `provider_account_id` set to the Google sub ID, and `name`/`email`/`image` from the Google profile #### Scenario: Returning Google sign-in - **WHEN** a user signs in with Google and their email already exists in the database - **THEN** the existing user is returned and no duplicate is created #### Scenario: Missing Google credentials - **WHEN** `AUTH_GOOGLE_ID` or `AUTH_GOOGLE_SECRET` environment variables are not set - **THEN** the Google provider SHALL not be included in the providers list and email/password login remains available ### Requirement: Auth API route handler The system SHALL create `src/app/api/auth/[...nextauth]/route.ts` that exports the GET and POST handlers from `src/auth.ts`. #### Scenario: Auth routes respond - **WHEN** a request is made to `/api/auth/signin`, `/api/auth/signout`, `/api/auth/session`, or `/api/auth/callback/google` - **THEN** the NextAuth handler processes the request ### Requirement: Registration API endpoint The system SHALL provide a `POST /api/auth/register` endpoint that creates a new user account. The endpoint SHALL accept `{ name, email, password }` in the request body. The password SHALL be hashed with bcryptjs (salt rounds: 10) before storage. The email SHALL be checked for uniqueness. #### Scenario: Successful registration - **WHEN** POST /api/auth/register is called with valid name, email, and password (min 8 characters) - **THEN** a new user is created with hashed password, `provider: 'credentials'` - **AND** the response is HTTP 201 with `{ id, email, name }` #### Scenario: Duplicate email - **WHEN** POST /api/auth/register is called with an email that already exists - **THEN** the response is HTTP 409 with `{ error: "Email already registered" }` #### Scenario: Invalid password length - **WHEN** POST /api/auth/register is called with a password shorter than 8 characters - **THEN** the response is HTTP 400 with `{ error: "Password must be at least 8 characters" }` #### Scenario: Missing required fields - **WHEN** POST /api/auth/register is called without email or password - **THEN** the response is HTTP 400 with `{ error: "Email and password are required" }` ### Requirement: Auth proxy for route protection The system SHALL create a `proxy.ts` file at the project root that uses Auth.js `auth()` to check JWT sessions. The proxy SHALL enforce authentication on protected routes. #### Scenario: Unauthenticated access to /app - **WHEN** an unauthenticated user requests any path under `/app` or `/app/*` - **THEN** the proxy redirects to `/login` #### Scenario: Unauthenticated access to /api - **WHEN** an unauthenticated user requests any path under `/api/*` except `/api/auth/*` and `/api/health` - **THEN** the proxy returns HTTP 401 #### Scenario: Authenticated access to /login - **WHEN** an authenticated user requests `/login` or `/register` - **THEN** the proxy redirects to `/app` #### Scenario: Public routes pass through - **WHEN** any user requests `/`, `/login`, `/register`, `/api/auth/*`, or `/api/health` - **THEN** the proxy allows the request to proceed without auth check ### Requirement: Auth helper for API routes The system SHALL provide a `getAuthUser()` helper function in `src/lib/auth.ts` that extracts the authenticated user from the current session. All protected API routes SHALL call this function. #### Scenario: Authenticated request - **WHEN** `getAuthUser()` is called in an API route with a valid JWT session - **THEN** it returns `{ id, email, name }` from the session #### Scenario: Unauthenticated request - **WHEN** `getAuthUser()` is called in an API route without a valid session - **THEN** it returns `null` ### Requirement: SessionProvider wrapper The system SHALL wrap the app layout with `next-auth/react` `SessionProvider` so client components can use the `useSession()` hook to access auth state. #### Scenario: Session available in client components - **WHEN** a client component calls `useSession()` inside the protected app layout - **THEN** it receives `{ data: session, status: "authenticated" }` with `session.user.id`, `session.user.email`, `session.user.name` ### Requirement: npm dependencies for auth The project SHALL add `next-auth@5` (Auth.js v5), `bcryptjs`, and `@types/bcryptjs` to the dependencies. #### Scenario: Dependencies installed - **WHEN** `package.json` is inspected - **THEN** `next-auth`, `bcryptjs` are in `dependencies` and `@types/bcryptjs` is in `devDependencies`