6.0 KiB
6.0 KiB
Login Authentication Design (Next.js, PostgreSQL, Brevo SMTP)
1. Overview & Tech Stack
| Component | Technology | Reason |
|---|---|---|
| Frontend / SSR | Next.js 13 (App Router) | File‑based routing, SSR/SSG, built‑in API routes |
| Database | PostgreSQL (self‑hosted, EU datacenter) | ACID, at‑rest encryption, works with Prisma |
| ORM | Prisma 2 | Type‑safe queries, easy migrations |
| Password hashing | bcrypt (cost = 12) | Industry‑standard, slow enough to deter brute‑force |
| Tokens | JWT (HS256) | Access token ~15 min, Refresh token ~7 days |
| Session cookies | HttpOnly + Secure + SameSite=Lax (chosen by user) | XSS/CSRF protection |
| Email service | Brevo SMTP | GDPR‑compatible, simple configuration |
| Deployment | Vercel (server‑less API routes) | Automatic builds, EU region, zero‑config |
| Testing | Jest + React Testing Library, Supertest for API | Unit & integration coverage |
2. Database Schema (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 >2 days) | – (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)
- Registration creates a
VerificationToken(UUID + HMAC) stored with an expiry (24 h). - Email template (HTML + text) contains a link:
https://<your‑app>.vercel.app/api/auth/verify-email?token=<<token>> - The verification endpoint validates the token, marks the user as verified and deletes the token.
- Password‑reset works analogously with a one‑hour token and a
usedflag.
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) |
15 min | Lax (as requested) | true | true |
refreshToken |
JWT (sub, exp) |
7 days (user chose "remember me") | Lax | true | true |
Refresh token is only set when the front‑end sends rememberMe: true.
6. Role & Permission System
requireAuthmiddleware validates the access token and attachesreq.user.requireRole('ADMIN')middleware checksreq.user.role.- Roles are stored in the
rolecolumn ofUserand encoded into the JWT payload.
7. GDPR & Data‑Minimisation
| 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). |
| At‑rest encryption | PostgreSQL instance encrypted by the managed provider (EU region). |
| Audit log | Optional AuditLog table with pseudonymised entries, retained 30 days. |
8. Deployment Pipeline (Vercel)
- Connect the GitHub repo to Vercel.
- Vercel runs
npm install && npm run build && npm teston each push. - Preview deployments for PRs (
pr‑<num>--<repo>.vercel.app). - Production deploy on merge to
main(https://<app>.vercel.app). - 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 + SameSite Lax cookies
- ✅ Refresh‑token rotation on each refresh
- ✅ Rate limiting on
/loginand/request-password-reset - ✅ CSP, X‑Content‑Type‑Options, Referrer‑Policy headers via
next.config.js - ✅ Helmet‑like 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.