---
phase: "02-admin-area-interactive-features"
plan: 01
subsystem: "auth"
tags: [next-auth, credentials, jwt, admin, session, middleware]
dependency_graph:
  requires: []
  provides: [admin-session-auth, admin-login-page, proxy-admin-guard]
  affects: [src/proxy.ts, src/lib/auth.ts]
tech_stack:
  added: [next-auth@4]
  patterns: [CredentialsProvider, JWT session strategy, edge proxy guard]
key_files:
  created:
    - src/lib/auth.ts
    - src/app/api/auth/[...nextauth]/route.ts
    - src/app/admin/login/page.tsx
  modified:
    - src/proxy.ts
    - package.json
    - package-lock.json
    - .env.local
decisions:
  - "Keep proxy export named 'proxy' (not 'middleware') — Next.js 16 renamed the middleware concept back to proxy, breaking the plan's rename instruction"
  - "Wrap useSearchParams() in Suspense boundary — required by Next.js App Router for static prerendering"
  - "Single admin user, env-var credentials only — no DB users table (stateless JWT)"
metrics:
  duration: "~15 minutes"
  completed: "2026-05-15T08:42:35Z"
  tasks_completed: 2
  files_changed: 7
---

# Phase 02 Plan 01: Auth.js Admin Session + Proxy Guard Summary

Auth.js v4 CredentialsProvider with JWT sessions gates the entire /admin/* area using env-var credentials (no DB users table), with an edge proxy guard in src/proxy.ts that validates sessions via getToken() before any admin page code runs.

## What Was Built

### Task 1: next-auth@4 installation and auth config
- Installed `next-auth@4` (stable; v5 still RC as of 2026-05-15)
- Created `src/lib/auth.ts` — NextAuthOptions with CredentialsProvider reading `ADMIN_EMAIL` + `ADMIN_PASSWORD` from env vars; JWT session strategy (stateless, no DB adapter)
- Created `src/app/api/auth/[...nextauth]/route.ts` — NextAuth catch-all handler (GET + POST)
- Updated `.env.local` with `NEXTAUTH_URL`, `NEXTAUTH_SECRET` (32-byte base64), `ADMIN_EMAIL`, `ADMIN_PASSWORD`

### Task 2: Proxy guard and login page
- Extended `src/proxy.ts` with `/admin/*` session guard using `getToken()` from `next-auth/jwt`
- `/admin/login` and `/api/auth/*` exempted from the guard (pass-through)
- Unauthenticated `/admin/*` requests redirect to `/admin/login?callbackUrl=<original-path>`
- `/c/:path*` client token validation logic preserved verbatim from Phase 1
- matcher updated: `["/admin/:path*", "/c/:path*"]`
- Created `src/app/admin/login/page.tsx` — email+password Client Component with `signIn('credentials')`, inline error display ("Email o password non corretti."), redirect on success

## Commits

| Task | Commit | Description |
|------|--------|-------------|
| 1 | 5d363a6 | feat(02-01): install next-auth@4, configure CredentialsProvider auth |
| 2 | 69f8a7e | feat(02-01): extend proxy.ts with admin session guard, add login page |

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 1 - Bug] Next.js 16 proxy export name is 'proxy', not 'middleware'**
- **Found during:** Task 2 first build attempt
- **Issue:** The plan instructed renaming the export to `middleware` (Next.js 15 convention), but this project runs Next.js 16.2.6, which introduced the `proxy` concept and requires the function to be named `proxy`. The build failed with: "Proxy is missing expected function export name"
- **Fix:** Kept the export name as `proxy` — consistent with the existing Phase 1 file and Next.js 16 API
- **Files modified:** `src/proxy.ts`

**2. [Rule 1 - Bug] useSearchParams() requires Suspense boundary in App Router**
- **Found during:** Task 2 second build attempt
- **Issue:** `useSearchParams()` in a Client Component causes a build failure during static page generation without a Suspense boundary. Error: "useSearchParams() should be wrapped in a suspense boundary at page /admin/login"
- **Fix:** Extracted the form into `AdminLoginForm` component; wrapped it in `<Suspense>` inside the default export `AdminLoginPage`
- **Files modified:** `src/app/admin/login/page.tsx`

## Known Stubs

None — all implemented functionality is complete and functional.

## Threat Surface Scan

No new security surface beyond what was planned in the threat model:
- T-02-01: Mitigated — CredentialsProvider validates against env vars server-side
- T-02-02: Mitigated — JWT signed with NEXTAUTH_SECRET, verified via getToken() on every /admin request
- T-02-03: Mitigated — ADMIN_PASSWORD stored only in .env.local (gitignored) and Vercel secrets
- T-02-04: Accepted — /api/auth/* exempt by design, NextAuth handles its own CSRF
- T-02-05: Accepted — No rate limiting in v1

## Self-Check: PASSED

All created files exist on disk. Both task commits (5d363a6, 69f8a7e) verified in git log.
