FDA

Project 1 - Admin Panel

Admin Panel - Backend & Auth

The Admin Panel uses a custom Node.js backend with Next.js API routes, JWT in HTTP-only cookies, and server actions for auth and forms.

Stack

  • PostgreSQL via pg
  • JWT for sessions (HTTP-only cookies)
  • bcrypt for PIN hashing
  • Next.js API Routes for REST endpoints
  • Server Actions for form submissions (login, register, etc.)

Auth API Routes

Under src/app/api/auth/:

MethodRoutePurpose
POST/api/auth/send-pinGenerate and send PIN (e.g. for signup); in dev often mock
POST/api/auth/verify-signupVerify PIN and create account
POST/api/auth/signinSign in with phone + PIN
GET/api/auth/meReturn current user profile (requires auth cookie)

Server Actions

In src/lib/auth/actions.ts:

  • sendSignUpPIN() – Generate/send PIN for signup
  • verifySignUpPIN() – Verify PIN and create account
  • signInWithPhoneAndPin() – Sign in
  • sendForgotPinOTP() – Forgot PIN flow
  • signOut() – Sign out

Database Layer

  • src/lib/db/index.ts – DB connection pool
  • src/lib/db/users.ts – User/profile queries

Authentication Flow

Sign up

  1. User enters phone number.
  2. App calls send-pin (or server action); system generates 4-digit PIN (in dev often 1234).
  3. PIN is “sent” (in dev may only be logged).
  4. User enters PIN; app calls verify-signup (or server action).
  5. Account is created with hashed PIN; JWT is set in HTTP-only cookie.

Sign in

  1. User enters phone + PIN.
  2. Server verifies PIN against stored hash, issues JWT, sets cookie.

Protected routes

  • Middleware or server checks JWT from cookie.
  • Role-based access (admin, instructor, user) is enforced in routes and server actions.

Env vars (auth/backend)

DATABASE_URL=postgresql://...
JWT_SECRET=your-strong-secret
JWT_EXPIRES_IN=7d
NEXT_PUBLIC_API_URL=http://localhost:3000

Production notes

  • Use a strong JWT_SECRET (e.g. openssl rand -base64 32).
  • Use HTTPS and secure cookies in production.
  • Integrate a real SMS provider (e.g. Twilio) for PIN delivery.
  • Store temporary PINs in Redis or DB with expiry; add rate limiting on send-pin/verify.
  • Keep error messages user-friendly and avoid leaking internal details.

Testing auth endpoints

# Send PIN (signup)
curl -X POST http://localhost:3000/api/auth/send-pin \
  -H "Content-Type: application/json" \
  -d '{"phone":"1234567890"}'

# Verify signup
curl -X POST http://localhost:3000/api/auth/verify-signup \
  -H "Content-Type: application/json" \
  -d '{"phone":"1234567890","pin":"1234","fullName":"Test User"}'

# Sign in
curl -X POST http://localhost:3000/api/auth/signin \
  -H "Content-Type: application/json" \
  -d '{"phone":"1234567890","pin":"1234"}'

# Current user (use cookie from signin response)
curl -X GET http://localhost:3000/api/auth/me \
  -H "Cookie: auth_token=YOUR_JWT"

See Login & Credentials for how to log in in the UI and use mock PIN in development.

Previous
Database setup