Skip to content

Authentication System

Feature: F0.6 — Authentication System
Status: 🟢 COMPLETED
Date: 2026-08-29

What It Does

Complete session-based authentication for Vexlyx. Users can register (email/password), log in, log out, and access protected panel pages. Passwords are hashed with Argon2id. Sessions are stored in Redis for fast lookup and PostgreSQL for audit/admin invalidation. HTTP-only cookies prevent XSS. Rate limiting protects against brute-force attacks.

Architecture

Auth Flow

Register/Login → Argon2id verify → Create session (Redis + DB) → Set HTTP-only cookie

Every request → Read cookie → Lookup session in Redis → Populate request.userId

Protected routes → requireAuth preHandler → 401 if no session

Logout → Delete session (Redis + DB) → Clear cookie

Key Design Decisions

DecisionRationale
Custom auth over LuciaLucia Auth deprecated March 2025. Built our own following Lucia's patterns.
Argon2idOWASP-recommended, resistant to side-channel and GPU attacks
Redis + PostgreSQL dual storageRedis for speed (every-request lookup), PostgreSQL for audit trail and admin session management
HTTP-only cookiesPrevents XSS from reading session tokens. No JWT in localStorage.
SameSite=LaxCSRF protection without dedicated token mechanism
Auto-promote first userFirst registered user becomes ADMIN. Production uses ALLOW_REGISTRATION=false
Scoped rate limitingOnly auth endpoints (5/15min/IP), not global, so other APIs aren't affected

File Structure

apps/api/src/
├── config/
│   └── env.ts                    # SESSION_SECRET, ALLOW_REGISTRATION
├── modules/auth/
│   ├── schema.ts                 # Zod: RegisterSchema, LoginSchema
│   ├── service.ts                # AuthService: register, login, getCurrentUser
│   └── routes.ts                 # POST register/login/logout, GET me
├── plugins/
│   └── auth.ts                   # Session plugin + createSession/destroySession
└── index.ts                      # Registers auth plugin + rate-limited routes

apps/dashboard/src/
├── app/(auth)/
│   ├── layout.tsx                # Centered card layout (no sidebar)
│   ├── login/page.tsx            # Login page
│   └── register/page.tsx         # Register page
├── app/(panel)/
│   └── layout.tsx                # Auth guard (validates session server-side)
├── components/auth/
│   ├── LoginForm.tsx             # Email + password form
│   └── RegisterForm.tsx          # Name + email + password + confirm form
├── hooks/
│   └── useAuth.ts                # Auth state + login/register/logout
└── lib/
    └── api.ts                    # fetchAPI with credentials + error handling

API Endpoints

MethodPathAuthDescription
POST/api/auth/registerNoCreate user, start session
POST/api/auth/loginNoValidate credentials, start session
POST/api/auth/logoutNoDestroy session, clear cookie
GET/api/auth/meYesReturn current user

Rate Limiting

Auth endpoints are rate-limited to 5 requests per 15 minutes per IP. Exceeding returns 429 Too Many Requests.

Environment Variables

VariableRequiredDefaultDescription
SESSION_SECRETYes32+ char secret for session signing
ALLOW_REGISTRATIONNotrueEnable/disable public registration

How to Test

bash
# 1. Start Docker services
docker-compose up -d

# 2. Run migration (already applied)
cd apps/api && pnpm db:migrate

# 3. Start dev servers
pnpm dev

# 4. Test register
curl -X POST http://localhost:5000/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"name":"Test","email":"test@example.com","password":"password123","confirmPassword":"password123"}'

# 5. Test login (use -c to save cookies)
curl -X POST http://localhost:5000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","password":"password123"}' \
  -c cookies.txt

# 6. Test me (use -b to send cookies)
curl http://localhost:5000/api/auth/me -b cookies.txt

# 7. Test logout
curl -X POST http://localhost:5000/api/auth/logout -b cookies.txt

# 8. Verify session destroyed
curl http://localhost:5000/api/auth/me -b cookies.txt
# Should return 401

# 9. Visit http://localhost:3000/dashboard without auth
# Should redirect to /login

# 10. Check Redis sessions
docker exec -it vexlyx-redis redis-cli keys "session:*"

How to Extend

Adding OAuth providers

  1. Create apps/api/src/modules/auth/oauth.ts
  2. Add OAuth routes (e.g., GET /api/auth/github, GET /api/auth/github/callback)
  3. Use the same createSession() helper after OAuth verification

Adding password reset

  1. Create POST /api/auth/forgot-password — generates reset token, sends email
  2. Create POST /api/auth/reset-password — validates token, updates password
  3. Store tokens in Redis with short TTL (15 minutes)

Admin session management

Query PostgreSQL sessions table to see all active sessions, revoke by deleting from both DB and Redis.

Disabling registration in production

Set ALLOW_REGISTRATION=false in .env. Admin creates users manually via a future admin API.

Released under the MIT license.