Auth Worker
The auth worker provides email/password authentication using D1 for persistent user storage and KV for revocable opaque session tokens.
API Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /api/auth/register | — | Create account |
POST | /api/auth/login | — | Authenticate, receive session cookie |
POST | /api/auth/logout | ✅ | Destroy session |
GET | /api/auth/me | ✅ | Return current user |
POST | /api/auth/password | ✅ | Change password |
GET | /api/users/:id | — | Public user profile |
All endpoints return JSON. CORS preflight is handled automatically.
Authentication
Session Cookie
Set-Cookie: session={token}; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=604800; Domain=redecasas.com.br- Token: Random UUID stored in KV as
session:{token} - TTL: 7 days (refreshed on
GET /api/auth/meandPOST /api/auth/password) - Storage: KV namespace shared across all workers
- Logout: Cookie cleared with
Max-Age=0; KV key deleted
Auth Middleware Flow
- Read
Cookie: session={token} const session = await env.KV.get(session:${token})- Parse JSON, check
expiresAt - Query D1:
SELECT * FROM users WHERE id = ? - Attach user to context for the handler
POST /api/auth/register
Create a new user account. No auth required.
Request body
{ "email": "user@example.com", "password": "min8chars", "name": "João Silva", "phone": "11999999999"}| Field | Type | Required | Rules |
|---|---|---|---|
email | string | ✅ | Valid email format |
password | string | ✅ | 8–128 characters |
name | string | ❌ | Trimmed whitespace |
phone | string | ❌ | 10–11 digits (Brazilian format) |
Response 201 Created
{ "user": { "id": "uuid", "email": "user@example.com", "name": "João Silva", "phone": "11999999999" }}- Sets
sessioncookie automatically on success - Returns
409 Conflictif email or phone already exists
POST /api/auth/login
Authenticate and receive a session cookie. No auth required.
Request body
{ "email": "user@example.com", "password": "min8chars"}Response 200 OK
{ "user": { "id": "uuid", "email": "user@example.com", "name": "João Silva", "phone": "11999999999" }}- Sets
sessioncookie - Returns
401 Unauthorizedif credentials are wrong - Returns
403 Forbiddenif accountstatus === 'suspended' - Rate-limited per IP
POST /api/auth/logout
Destroy the current session. Requires auth.
Response 200 OK
{ "message": "Logged out successfully"}GET /api/auth/me
Return the current authenticated user. Requires auth.
Response 200 OK
{ "user": { "id": "uuid", "email": "user@example.com", "name": "João Silva", "phone": "11999999999", "status": "active", "email_verified_at": "2026-01-15T10:00:00.000Z", "stripe_customer_id": "cus_xxx", "subscription_status": "active", "subscription_period_end": "2026-07-01T00:00:00.000Z", "created_at": "2026-01-01T00:00:00.000Z", "updated_at": "2026-01-15T10:00:00.000Z" }}- Refreshes session TTL on success
subscription_statusvalues:active,inactive,past_due,cancelledstatusvalues:active,suspended,pending
POST /api/auth/password
Change the current user’s password. Requires auth.
Request body
{ "oldPassword": "currentpass", "newPassword": "newpass123"}Response 200 OK
{ "message": "Password updated successfully"}GET /api/users/:id
Public user profile (no auth required).
Response 200 OK
{ "user": { "id": "uuid", "name": "João Silva", "phone": "11999999999" }}- Returns
404if user not found - Does NOT expose email, subscription status, or Stripe data
Security
Password Hashing
- Algorithm: PBKDF2-HMAC-SHA-256
- Iterations: 100,000
- Salt: 32 bytes (random)
- Key length: 32 bytes
This uses the Web Crypto API — no external dependencies.
Rate Limiting
- 5 attempts per 15-minute window per IP on
registerandlogin - Implemented in
src/ratelimit.ts
CORS
Access-Control-Allow-Credentials: true- Dynamic origin validation in
shared/src/cors.ts
Session Storage (KV)
- Key:
session:{token} - Value:
{ userId, email, createdAt, expiresAt } - TTL: 7 days
Sessions are instantly revocable by deleting the KV key.
Development
cd auth-workernpm run devDeployment
cd auth-worker && npm run deploy