auth-demo
By (author)Qasim Khan
read more
Read Me
# auth-demo A hardened JWT authentication API built on Express + MongoDB/Mongoose. This is an upgraded version of a basic single-token auth demo, with the pieces you'd actually want before shipping something like this: - **Access + refresh token pair** instead of one long-lived JWT - Short-lived access token (15m default) returned in the response body, meant to be kept in memory on the client - Refresh token (7d default) stored as an `httpOnly`, `SameSite=Strict` cookie scoped to `/api/auth`, so client-side JS can never read it - **Refresh token rotation + reuse detection** — every `/refresh` call issues a brand new refresh token and revokes the old one. If a previously-rotated or revoked token is ever presented again (a strong signal of theft), every token for that user is revoked immediately. - **Password change invalidates existing sessions** — access tokens carry a `pwdChangedAt` claim checked against the user's real `passwordChangedAt`, and refresh tokens are revoked on password change. - **Account lockout** after 5 failed logins, 15 minute cooldown. - **Role-based access control** (`authorize("admin")` middleware) with an example `/api/auth/admin-only` route. - **Rate limiting** on register/login/refresh (`express-rate-limit`). - **Hardened HTTP headers** via `helmet`. - **CORS locked to an explicit origin allowlist** (`CORS_ORIGINS`), with `credentials: true` so the refresh cookie is actually sent cross-origin by browsers that need it. - **Input validation** via `express-validator` (email format, password complexity) instead of hand-rolled regex checks. - **Startup environment validation** — the server refuses to boot with missing or duplicate JWT secrets instead of failing confusingly later. - **Request logging** via `morgan`. ## Getting started ```bash npm install cp .env.example .env # then fill in real secrets npm run dev # nodemon # or npm start ``` Requires a running MongoDB instance reachable at `MONGODB_URI`. ## Environment variables See `.env.example`. The two JWT secrets **must** be different, long, random strings — the server will refuse to start otherwise. ## API All endpoints are under `/api/auth`. | Method | Path | Auth required | Description | |--------|-------------------|----------------|---------------------------------------------------| | POST | `/register` | no | Create an account, returns access token + sets refresh cookie | | POST | `/login` | no | Authenticate, returns access token + sets refresh cookie | | POST | `/refresh` | refresh cookie | Rotates the refresh token, returns a new access token | | POST | `/logout` | refresh cookie | Revokes the current refresh token, clears cookie | | POST | `/logout-all` | access token | Revokes every refresh token for the user (all devices) | | GET | `/me` | access token | Returns the current user | | PATCH | `/change-password`| access token | Changes password, revokes all existing sessions | | GET | `/admin-only` | access token, `admin` role | Example RBAC-gated route | Send the access token as `Authorization: Bearer `. The refresh token is handled automatically via cookie — just make sure your client sends requests `credentials: "include"`. ### Example flow ```bash # Register (cookie jar keeps the refresh token) curl -c cookies.txt -X POST http://localhost:5000/api/auth/register \ -H "Content-Type: application/json" \ -d '{"name":"Ada Lovelace","email":"ada@example.com","password":"Str0ngPass1"}' # Use the access token from the response curl http://localhost:5000/api/auth/me \ -H "Authorization: Bearer " # When the access token expires, get a new one curl -b cookies.txt -c cookies.txt -X POST http://localhost:5000/api/auth/refresh # Log out (revokes the refresh token) curl -b cookies.txt -X POST http://localhost:5000/api/auth/logout ``` ## What's intentionally still out of scope This is still a demo, not a full identity system. Not included: email verification, password-reset-via-email flow, OAuth/social login, MFA, and a persistent audit log of login attempts. The pieces above (token model, lockout, RBAC middleware) are built so any of those would slot in cleanly.