docs(01): create phase plan
Phase 01: Foundation - 5 plan(s) in 4 wave(s) - 2 parallel (wave 2: auth + tenant provisioning), 3 sequential - Ready for execution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
197
.planning/phases/01-foundation/01-02-PLAN.md
Normal file
197
.planning/phases/01-foundation/01-02-PLAN.md
Normal file
@@ -0,0 +1,197 @@
|
||||
---
|
||||
phase: 01-foundation
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on: ["01-01"]
|
||||
files_modified:
|
||||
- package.json
|
||||
- src/lib/auth.ts
|
||||
- src/lib/auth-options.ts
|
||||
- src/app/api/auth/[...nextauth]/route.ts
|
||||
- src/app/(auth)/login/page.tsx
|
||||
- src/app/(auth)/layout.tsx
|
||||
- src/middleware.ts
|
||||
- src/types/next-auth.d.ts
|
||||
- src/lib/__tests__/auth.test.ts
|
||||
autonomous: true
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "A user can log in with email and password and receive a valid session"
|
||||
- "A logged-in user stays logged in after browser refresh"
|
||||
- "Logging out ends the session and redirects to login"
|
||||
- "The JWT contains tenantId and roles for downstream use"
|
||||
- "Unauthenticated users are redirected to the login page"
|
||||
artifacts:
|
||||
- path: "src/lib/auth-options.ts"
|
||||
provides: "NextAuth configuration with credentials provider"
|
||||
exports: ["authOptions"]
|
||||
- path: "src/app/api/auth/[...nextauth]/route.ts"
|
||||
provides: "NextAuth API route handler"
|
||||
exports: ["GET", "POST"]
|
||||
- path: "src/app/(auth)/login/page.tsx"
|
||||
provides: "Login form UI"
|
||||
min_lines: 40
|
||||
- path: "src/middleware.ts"
|
||||
provides: "Route protection middleware"
|
||||
contains: "middleware"
|
||||
- path: "src/types/next-auth.d.ts"
|
||||
provides: "Extended session/JWT types with tenantId and roles"
|
||||
contains: "tenantId"
|
||||
key_links:
|
||||
- from: "src/lib/auth-options.ts"
|
||||
to: "prisma.user"
|
||||
via: "credential validation query"
|
||||
pattern: "prisma\\.user\\.findFirst"
|
||||
- from: "src/lib/auth-options.ts"
|
||||
to: "src/types/next-auth.d.ts"
|
||||
via: "JWT callback populates tenantId and roles"
|
||||
pattern: "token\\.tenantId"
|
||||
- from: "src/middleware.ts"
|
||||
to: "src/lib/auth-options.ts"
|
||||
via: "getToken checks authentication"
|
||||
pattern: "getToken|withAuth"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Implement email/password authentication using NextAuth.js (Auth.js) with the credentials provider, JWT sessions carrying tenantId and roles, login/logout UI, and route protection middleware.
|
||||
|
||||
Purpose: Every protected feature in the app depends on knowing WHO the user is and WHICH tenant they belong to. Auth is the gateway.
|
||||
Output: Working login/logout flow with JWT-based sessions that persist across refresh.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:\Users\KevinAsprec\.claude/get-shit-done/workflows/execute-plan.md
|
||||
@C:\Users\KevinAsprec\.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/phases/01-foundation/01-CONTEXT.md
|
||||
@.planning/phases/01-foundation/01-01-SUMMARY.md
|
||||
@prisma/schema.prisma
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: NextAuth.js configuration with credentials provider and JWT</name>
|
||||
<files>
|
||||
package.json
|
||||
src/lib/auth-options.ts
|
||||
src/lib/auth.ts
|
||||
src/app/api/auth/[...nextauth]/route.ts
|
||||
src/types/next-auth.d.ts
|
||||
src/middleware.ts
|
||||
</files>
|
||||
<action>
|
||||
Install dependencies: `npm install next-auth@4 bcryptjs` and `npm install -D @types/bcryptjs`.
|
||||
|
||||
Use NextAuth.js v4 (not v5 — v5/Auth.js is still beta-ish for credentials provider with custom JWT). This is a deliberate choice for stability.
|
||||
|
||||
Create `src/types/next-auth.d.ts` to extend NextAuth types:
|
||||
- Session.user includes: id, email, tenantId (string | null), roles (Role[]), isSuperAdmin (boolean), firstName, lastName
|
||||
- JWT includes same fields
|
||||
|
||||
Create `src/lib/auth-options.ts` exporting `authOptions: NextAuthOptions`:
|
||||
- CredentialsProvider with email and password fields
|
||||
- In authorize(): look up user by email using prisma.user.findFirst where email matches AND (tenantId matches an ACTIVE tenant OR user.isSuperAdmin is true). Use bcryptjs.compare for password verification.
|
||||
- If user found and password valid, return user object with id, email, tenantId, roles, isSuperAdmin, firstName, lastName
|
||||
- If not found or password invalid, return null (NextAuth shows error)
|
||||
- JWT callback: persist tenantId, roles, isSuperAdmin, firstName, lastName into token
|
||||
- Session callback: expose tenantId, roles, isSuperAdmin, firstName, lastName on session.user
|
||||
- Session strategy: "jwt"
|
||||
- JWT maxAge: 24 hours (session duration)
|
||||
- Pages: signIn: "/login"
|
||||
|
||||
Create `src/lib/auth.ts` exporting a helper `getServerSession()` wrapper and a `getCurrentUser()` helper that returns the typed session user or null.
|
||||
|
||||
Create `src/app/api/auth/[...nextauth]/route.ts` — standard NextAuth route handler exporting GET and POST.
|
||||
|
||||
Create `src/middleware.ts`:
|
||||
- Use NextAuth's `withAuth` or `getToken` to check for valid JWT
|
||||
- Protect all routes EXCEPT: /login, /signup, /api/auth/*, /_next/*, /favicon.ico, static assets
|
||||
- If no valid token, redirect to /login
|
||||
- If valid token, allow request to continue
|
||||
</action>
|
||||
<verify>
|
||||
Build check: `npx next build` completes without type errors. The auth route is accessible at /api/auth/providers (returns JSON with credentials provider listed).
|
||||
</verify>
|
||||
<done>NextAuth configured with credentials provider. JWT contains tenantId and roles. Middleware redirects unauthenticated users to /login. TypeScript types are extended for session.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Login page UI, logout flow, and seed script for testing</name>
|
||||
<files>
|
||||
src/app/(auth)/login/page.tsx
|
||||
src/app/(auth)/layout.tsx
|
||||
prisma/seed.ts
|
||||
package.json
|
||||
src/lib/__tests__/auth.test.ts
|
||||
</files>
|
||||
<action>
|
||||
Create `src/app/(auth)/layout.tsx` — a centered layout for auth pages (no sidebar/nav, just centered card on neutral background). Use Tailwind.
|
||||
|
||||
Create `src/app/(auth)/login/page.tsx`:
|
||||
- Client component ("use client")
|
||||
- Form with email and password fields, submit button
|
||||
- Uses `signIn("credentials", { email, password, redirect: false })` from next-auth/react
|
||||
- On success: redirect to /dashboard (or / for now)
|
||||
- On error: show error message below form (red text, "Invalid email or password")
|
||||
- Loading state on submit button
|
||||
- Clean, minimal design with Tailwind — no heavy UI library needed
|
||||
- Page title: "Sign in to NetForge"
|
||||
- Include a "Sign up" link pointing to /signup (page will be created in plan 01-03)
|
||||
|
||||
Add logout functionality:
|
||||
- Create a simple header component at `src/components/layout/header.tsx` that shows the user's name and a "Sign out" button
|
||||
- Sign out button calls `signOut({ callbackUrl: "/login" })` from next-auth/react
|
||||
- Include this header in the main app layout (not the auth layout)
|
||||
- Create `src/app/(dashboard)/layout.tsx` as the authenticated layout that includes the header
|
||||
|
||||
Create `prisma/seed.ts`:
|
||||
- Creates a test tenant: "Demo ISP", status ACTIVE
|
||||
- Creates an admin user: admin@demo.com / password "admin123" (bcrypt hashed), role ADMIN, linked to demo tenant
|
||||
- Creates a super-admin user: superadmin@netforge.com / password "super123" (bcrypt hashed), isSuperAdmin true, no tenantId
|
||||
- Uses upsert to be idempotent
|
||||
- Add "prisma.seed" config to package.json pointing to "ts-node prisma/seed.ts" (or use tsx: `npx tsx prisma/seed.ts`)
|
||||
- Add script: "db:seed": "npx tsx prisma/seed.ts"
|
||||
|
||||
Create `src/lib/__tests__/auth.test.ts`:
|
||||
- Test that authOptions has credentials provider configured
|
||||
- Test that JWT callback adds tenantId and roles to token (mock the callback)
|
||||
- Test that session callback exposes tenantId and roles on session
|
||||
- These are unit tests of the auth config, NOT integration tests requiring a running server
|
||||
|
||||
Run the seed script to populate test data.
|
||||
</action>
|
||||
<verify>
|
||||
Run `npm run db:seed` — creates test users without errors. Run `npx vitest run` — auth tests pass. Start the app, navigate to /login, log in with admin@demo.com / admin123 — redirects to dashboard. Click sign out — redirects to /login. Refresh after login — session persists.
|
||||
</verify>
|
||||
<done>Login page renders with email/password form. Seed data creates demo tenant + admin + super-admin. Login with valid credentials works and session persists across refresh. Logout redirects to login. Auth unit tests pass.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
1. /login page renders a clean login form
|
||||
2. Login with admin@demo.com / admin123 succeeds and redirects to authenticated area
|
||||
3. Session persists after browser refresh (no re-login required)
|
||||
4. Sign out button ends session and redirects to /login
|
||||
5. Visiting a protected route while logged out redirects to /login
|
||||
6. `npx vitest run` passes all auth tests
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Email/password authentication works end-to-end (AUTH-01)
|
||||
- JWT contains tenantId and roles for downstream RBAC and tenant scoping
|
||||
- Session persists across browser refresh (AUTH-04)
|
||||
- Unauthenticated users cannot access protected routes
|
||||
- Seed data provides test accounts for development
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/01-foundation/01-02-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user