Files
login-example-app/docs/superpowers/specs/2026-05-21-login-auth-design.md
2026-05-21 18:44:41 +02:00

121 lines
6.0 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Login Authentication Design (Next.js, PostgreSQL, Brevo SMTP)
## 1. Overview & Tech Stack
| Component | Technology | Reason |
|-----------|------------|--------|
| Frontend / SSR | Next.js 13 (App Router) | Filebased routing, SSR/SSG, builtin API routes |
| Database | PostgreSQL (selfhosted, EU datacenter) | ACID, atrest encryption, works with Prisma |
| ORM | Prisma 2 | Typesafe queries, easy migrations |
| Password hashing | bcrypt (cost=12) | Industrystandard, slow enough to deter bruteforce |
| Tokens | JWT (HS256) | Access token ~15min, Refresh token ~7days |
| Session cookies | HttpOnly + Secure + SameSite=Lax (chosen by user) | XSS/CSRF protection |
| Email service | Brevo SMTP | GDPRcompatible, simple configuration |
| Deployment | Vercel (serverless API routes) | Automatic builds, EU region, zeroconfig |
| Testing | Jest + React Testing Library, Supertest for API | Unit & integration coverage |
## 2. Database Schema (Prisma)
```prisma
model User {
id String @id @default(cuid())
email String @unique
passwordHash String
role Role @default(USER)
emailVerified Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
verificationTokens VerificationToken[]
passwordResetTokens PasswordResetToken[]
}
model VerificationToken {
id String @id @default(cuid())
token String @unique
expiresAt DateTime
userId String
user User @relation(fields: [userId], references: [id])
createdAt DateTime @default(now())
}
model PasswordResetToken {
id String @id @default(cuid())
token String @unique
expiresAt DateTime
used Boolean @default(false)
userId String
user User @relation(fields: [userId], references: [id])
createdAt DateTime @default(now())
}
enum Role {
ADMIN
USER
}
```
## 3. API Routes (Next.js `/pages/api/...`)
| Route | Method | Purpose | Input | Output |
|------|--------|---------|-------|--------|
| `/api/auth/register` | POST | Create user, hash password, send verification mail | `{ email, password }` | 201 Created, message "Verification mail sent" |
| `/api/auth/verify-email` | GET | Validate verification token, set `emailVerified` | `?token=` | 200 OK, "Email verified" |
| `/api/auth/login` | POST | Check credentials, issue access & refresh JWT, set cookies | `{ email, password, rememberMe? }` | 200 OK, cookies set |
| `/api/auth/logout` | POST | Clear cookies, optionally blacklist refresh token | | 200 OK |
| `/api/auth/refresh-token` | POST | Verify refresh JWT, issue new access JWT (rotate refresh if >2days) | (refresh cookie) | 200 OK, new cookies |
| `/api/auth/request-password-reset` | POST | Create reset token, send mail | `{ email }` | 200 OK, message "Mail sent" |
| `/api/auth/reset-password` | POST | Validate reset token, store new bcrypt hash, invalidate token | `{ token, newPassword }` | 200 OK |
| `/api/auth/me` | GET | Return current user data (no password) | | 200 OK, `{ email, role, emailVerified }` |
All endpoints enforce `Content-Type: application/json` and `Cache-Control: no-store`.
## 4. Email Workflow (Brevo SMTP)
1. Registration creates a `VerificationToken` (UUID + HMAC) stored with an expiry (24h).
2. Email template (HTML + text) contains a link:
`https://<yourapp>.vercel.app/api/auth/verify-email?token=<<token>>`
3. The verification endpoint validates the token, marks the user as verified and deletes the token.
4. Passwordreset works analogously with a onehour token and a `used` flag.
SMTP credentials (`BREVO_SMTP_HOST`, `BREVO_SMTP_USER`, `BREVO_SMTP_PASS`) are stored as Vercel environment variables.
## 5. Session & Cookie Settings
| Cookie | Content | Max Age | SameSite | Secure | HttpOnly |
|--------|---------|---------|----------|--------|----------|
| `accessToken` | JWT (`sub`, `role`, `exp`) | 15min | Lax (as requested) | true | true |
| `refreshToken` | JWT (`sub`, `exp`) | 7days (user chose "remember me") | Lax | true | true |
Refresh token is only set when the frontend sends `rememberMe: true`.
## 6. Role & Permission System
- `requireAuth` middleware validates the access token and attaches `req.user`.
- `requireRole('ADMIN')` middleware checks `req.user.role`.
- Roles are stored in the `role` column of `User` and encoded into the JWT payload.
## 7. GDPR & DataMinimisation
| Measure | Implementation |
|---------|----------------|
| Minimal data | Store only email, hashed password, role, verification flag. |
| Consent | Registration form includes a mandatory "I accept the privacy policy" checkbox. |
| Right to be forgotten | `DELETE /api/users/me` removes the user row and all related tokens. |
| Data retention | Verification and reset tokens have explicit `expiresAt`; nightly Prisma job deletes expired rows. |
| Transport security | All traffic forced via HTTPS (Vercel provides TLS). |
| Atrest encryption | PostgreSQL instance encrypted by the managed provider (EU region). |
| Audit log | Optional `AuditLog` table with pseudonymised entries, retained 30days. |
## 8. Deployment Pipeline (Vercel)
1. Connect the GitHub repo to Vercel.
2. Vercel runs `npm install && npm run build && npm test` on each push.
3. Preview deployments for PRs (`pr<num>--<repo>.vercel.app`).
4. Production deploy on merge to `main` (`https://<app>.vercel.app`).
5. Environment variables (`DATABASE_URL`, `NEXTAUTH_SECRET`, Brevo SMTP creds) are configured in Vercel > Settings > Environment Variables same values for Preview and Production.
## 9. Security Checklist
- ✅ bcrypt cost=12
- ✅ HttpOnly+Secure+SameSiteLax cookies
- ✅ Refreshtoken rotation on each refresh
- ✅ Rate limiting on `/login` and `/request-password-reset`
- ✅ CSP, XContentTypeOptions, ReferrerPolicy headers via `next.config.js`
- ✅ Helmetlike security headers
- ✅ Regular dependency audit (`npm audit`) and CI fail on high severity issues
---
*This spec is ready for review. Once approved, the next step will be to create a detailed implementation plan.*