just a simple express app I built to practice jwt authentication and token rotation. it uses prisma and postgres.
- register and login flows
- short-lived access tokens (15m) and long-lived refresh tokens (7 days)
- token rotation (deletes the old refresh token when a new one is requested)
- simple rbac (admin vs user routes)
- rate limiting on the login route so people don't brute force it
- input validation with zod
npm install- create a
.envfile (look atsrc/config/env.tsfor what variables you need) npx prisma db pushnpm run devto start the server on port 3002npx jestif you want to run the tests
%%{init: {'theme': 'default'}}%%
sequenceDiagram
autonumber
actor Client
participant API as Express API
participant DB as PostgreSQL (Prisma)
rect rgb(240, 248, 255)
Note over Client, DB: Registration & Login
Client->>API: POST /register (email, password)
API->>API: Validate input (Zod)
API->>DB: Check if user exists & Save User (hashed password)
DB-->>API: User Created
API-->>Client: 201 Created (User Data)
Client->>API: POST /login (email, password)
API->>API: Rate Limiting Check (Max 5/15m)
API->>API: Validate input (Zod)
API->>DB: Fetch User
DB-->>API: User Data
API->>API: Compare passwords
API->>API: Generate Access Token (15m) & Refresh Token (7d)
API->>DB: Save Refresh Token
DB-->>API: Saved
API-->>Client: 200 OK (AccessToken, RefreshToken)
end
rect rgb(255, 240, 245)
Note over Client, DB: Accessing Protected Routes
Client->>API: GET /profile (Authorization: Bearer AccessToken)
API->>API: Validate AccessToken (authenticate middleware)
alt Token Valid
API-->>Client: 200 OK (Profile Data)
else Token Expired/Invalid
API-->>Client: 401 Unauthorized
end
Client->>API: GET /admin-data (Authorization: Bearer AccessToken)
API->>API: Validate AccessToken (authenticate)
API->>API: Check Role (authorize('admin'))
alt Role is Admin
API-->>Client: 200 OK (Admin Data)
else Role is User
API-->>Client: 403 Forbidden
end
end
rect rgb(240, 255, 240)
Note over Client, DB: Token Rotation Flow
Client->>API: POST /refresh (RefreshToken)
API->>API: Validate input (Zod)
API->>DB: Validate RefreshToken exists
alt Valid & Exists
DB-->>API: Token Valid
API->>DB: Delete Old RefreshToken (Rotation)
API->>API: Generate New AccessToken & RefreshToken
API->>DB: Save New RefreshToken
API-->>Client: 200 OK (New AccessToken, New RefreshToken)
else Invalid/Reused
API-->>Client: 403 Forbidden
end
end
rect rgb(255, 250, 240)
Note over Client, DB: Logout Flow
Client->>API: POST /logout (RefreshToken)
API->>API: Validate input (Zod)
API->>DB: Delete RefreshToken
API-->>Client: 200 OK (Logged out)
end