http://localhost:5000
All routes are prefixed:
- Auth routes:
/auth - User routes:
/user
The backend sends emails with links pointing to your frontend. You must implement the following routes in your frontend application:
| Frontend Route | Used by | Description |
|---|---|---|
/verify-email/:token |
Signup, Resend verification | Extract :token from the URL and send it to POST /auth/verifyEmail |
/reset-password/:token |
Forgot password | Extract :token from the URL and send it to POST /auth/resetPassword |
Example email link format:
https://yourapp.com/verify-email/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
https://yourapp.com/reset-password/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Tokens are stored as httpOnly cookies — the browser sends them automatically. You do not need to set Authorization headers.
| Cookie | Contents | Max-Age |
|---|---|---|
access_token |
Signed JWT | 30 minutes |
refresh_token |
Signed JWT | 7 days |
When the access token expires, call POST /auth/get-access-token to silently rotate both tokens using the refresh token cookie.
Most endpoints require a device field. This must be a valid UUID v4 that uniquely identifies the client device/browser. Generate one once and persist it (e.g. localStorage).
"device": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
This enables per-device session management — each device gets its own refresh token, allowing multiple simultaneous sessions.
{
"success": true,
"message": "Human-readable message",
"data": { }
}{
"success": false,
"message": "Human-readable message",
"type": "ACCESS_TOKEN_EXPIRED"
}{
"success": false,
"message": "Validation failed",
"type": "VALIDATION_ERROR",
"details": [
{ "field": "email", "message": "Invalid email address" },
{ "field": "password", "message": "Must contain at least one uppercase letter" }
]
}The type field in error responses is a string. Use these values on the client to handle specific scenarios programmatically.
type |
Description |
|---|---|
"ACCESS_TOKEN_EXPIRED" |
Access token has expired. Call POST /auth/get-access-token to refresh. |
"REFRESH_TOKEN_EXPIRED" |
Refresh token has expired or been revoked. User must log in again. |
"INVALID_CREDENTIALS" |
Email or password is incorrect. |
"USER_NOT_FOUND" |
No user found for the given identifier. |
"BAD_REQUEST" |
Malformed request or missing required data. |
"UNAUTHORIZED" |
Request lacks valid authentication. |
"TOO_MANY_REQUESTS" |
Rate limit exceeded. |
"APP_ERROR" |
Generic application error with no specific type assigned. |
"VALIDATION_ERROR" |
Zod validation failed. See details array for field-level errors. |
Returned in login, signup, and user endpoints. The password field is never included.
{
"id": "uuid",
"name": "John Doe",
"email": "john@example.com",
"emailVerified": false,
"createdAt": "2026-03-29T10:00:00.000Z",
"updatedAt": "2026-03-29T10:00:00.000Z"
}Register a new account. Sets access_token and refresh_token cookies on success. Sends a verification email asynchronously.
Rate limit: 10 requests / 5 minutes
{
"name": "John Doe",
"email": "john@example.com",
"password": "SecurePass1!",
"device": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}| Field | Type | Rules |
|---|---|---|
name |
string | Required, non-empty |
email |
string | Valid email, trimmed, lowercased |
password |
string | 8–100 chars, must contain uppercase, lowercase, digit, special character |
device |
string | Valid UUID v4 |
{
"success": true,
"message": "Signup successful",
"data": {
"user": { ...User }
}
}| Status | Message | Type |
|---|---|---|
400 |
"A user with this email already exists." |
"APP_ERROR" |
400 |
Validation errors | "VALIDATION_ERROR" |
429 |
"Too many requests, please wait before retrying!" |
"TOO_MANY_REQUESTS" |
Authenticate with email and password. Sets access_token and refresh_token cookies on success. Sends a login alert email asynchronously.
Rate limit: 10 requests / 5 minutes
{
"email": "john@example.com",
"password": "SecurePass1!",
"device": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}| Field | Type | Rules |
|---|---|---|
email |
string | Valid email |
password |
string | 8–100 chars, complexity rules |
device |
string | Valid UUID v4 |
{
"success": true,
"message": "Login successful",
"data": {
"user": { ...User }
}
}| Status | Message | Type |
|---|---|---|
401 |
"Invalid Credentials" |
"INVALID_CREDENTIALS" |
400 |
Validation errors | "VALIDATION_ERROR" |
429 |
"Too many requests, please wait before retrying!" |
"TOO_MANY_REQUESTS" |
Sends a password reset email. Always returns success regardless of whether the email exists (prevents user enumeration). A Redis lock prevents duplicate emails within 60 seconds for the same account.
Rate limit: 3 requests / 5 minutes
{
"email": "john@example.com",
"device": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}| Field | Type | Rules |
|---|---|---|
email |
string | Valid email |
device |
string | Valid UUID v4 |
{
"success": true,
"message": "Password reset email sent!"
}| Status | Message | Type |
|---|---|---|
400 |
Validation errors | "VALIDATION_ERROR" |
429 |
"Too many requests, please wait before retrying!" |
"TOO_MANY_REQUESTS" |
The email contains a link to
{FRONTEND_URL}/reset-password/{token}. The token expires perRESET_TOKEN_EXPIRY.
Resets the user's password using the token from the reset email. On success, deletes the reset token and all active sessions (forces re-login on all devices).
Rate limit: 10 requests / 5 minutes
{
"token": "<token extracted from /reset-password/:token URL>",
"newPassword": "NewSecurePass1!"
}| Field | Type | Rules |
|---|---|---|
token |
string | Required — the path param from the frontend /reset-password/:token route |
newPassword |
string | 8–100 chars, complexity rules |
{
"success": true,
"message": "Password reset Successful, please login!"
}| Status | Message | Type |
|---|---|---|
400 |
"Invalid Reset Token" |
"APP_ERROR" |
400 |
Validation errors | "VALIDATION_ERROR" |
429 |
"Too many requests, please wait before retrying!" |
"TOO_MANY_REQUESTS" |
Verifies a user's email address using the token from the verification email. Sets emailVerified = true and deletes the verification token.
Rate limit: 10 requests / 5 minutes
{
"token": "<token extracted from /verify-email/:token URL>"
}| Field | Type | Rules |
|---|---|---|
token |
string | Required — the path param from the frontend /verify-email/:token route |
{
"success": true,
"message": "Email verification successful!"
}| Status | Message | Type |
|---|---|---|
400 |
"Invalid Verification Token" |
"APP_ERROR" |
400 |
Validation errors | "VALIDATION_ERROR" |
429 |
"Too many requests, please wait before retrying!" |
"TOO_MANY_REQUESTS" |
Resends the email verification link. Enforces a 5-minute cooldown between requests per user. Always returns success even if the email is not found (prevents enumeration).
Rate limit: 3 requests / 5 minutes
{
"email": "john@example.com",
"device": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}| Field | Type | Rules |
|---|---|---|
email |
string | Valid email |
device |
string | Valid UUID v4 |
{
"success": true,
"message": "Verification email sent successfully!"
}| Status | Message | Type |
|---|---|---|
429 |
"Please wait {n} minutes before requesting another email." |
"APP_ERROR" |
500 |
"We couldn't send the email. Please try again in a moment." |
"APP_ERROR" |
400 |
Validation errors | "VALIDATION_ERROR" |
429 |
"Too many requests, please wait before retrying!" |
"TOO_MANY_REQUESTS" |
Logs out the current device session. Deletes the refresh token from the database and sets a Redis revocation key to block any in-flight access tokens. Clears both cookies.
Rate limit: 10 requests / 5 minutes Auth required: Yes
{
"device": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}| Field | Type | Rules |
|---|---|---|
device |
string | Valid UUID v4 |
{
"success": true,
"message": "Logout successful!"
}| Status | Message | Type |
|---|---|---|
401 |
"Unauthorized" |
"UNAUTHORIZED" |
401 |
"Access token expired. Use refresh token to continue." |
"ACCESS_TOKEN_EXPIRED" |
401 |
"Session revoked or expired. Please login again." |
"REFRESH_TOKEN_EXPIRED" |
429 |
"Too many requests, please wait before retrying!" |
"TOO_MANY_REQUESTS" |
Logs out all active sessions for the authenticated user across all devices. Expires all refresh tokens in the DB and writes Redis revocation keys for each.
Rate limit: 10 requests / 5 minutes Auth required: Yes
{
"device": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}| Field | Type | Rules |
|---|---|---|
device |
string | Valid UUID v4 |
{
"success": true,
"message": "Logout successful!"
}| Status | Message | Type |
|---|---|---|
400 |
"User not found!" |
"USER_NOT_FOUND" |
401 |
"Unauthorized" |
"UNAUTHORIZED" |
401 |
"Access token expired. Use refresh token to continue." |
"ACCESS_TOKEN_EXPIRED" |
401 |
"Session revoked or expired. Please login again." |
"REFRESH_TOKEN_EXPIRED" |
429 |
"Too many requests, please wait before retrying!" |
"TOO_MANY_REQUESTS" |
Silently rotates both tokens using the refresh token cookie. Call this when a request fails with type: "ACCESS_TOKEN_EXPIRED". Sets new access_token and refresh_token cookies.
Theft detection: If the refresh token's
tokenIdis not found in the database (already consumed), all sessions for that user are immediately wiped.
Rate limit: 10 requests / 5 minutes
{
"device": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}| Field | Type | Rules |
|---|---|---|
device |
string | Valid UUID v4 |
{
"success": true,
"message": "Access token generated successfully"
}| Status | Message | Type |
|---|---|---|
401 |
"Refresh token invalid, please login again." |
"REFRESH_TOKEN_EXPIRED" |
401 |
"Session revoked or expired. Please login again." |
"REFRESH_TOKEN_EXPIRED" |
400 |
Validation errors | "VALIDATION_ERROR" |
429 |
"Too many requests, please wait before retrying!" |
"TOO_MANY_REQUESTS" |
Changes the password for the currently authenticated user. Hashes the new password, revokes all active sessions across all devices, and clears the current device's cookies. The user must log in again on all devices.
Rate limit: 10 requests / 5 minutes Auth required: Yes
{
"newPassword": "NewSecurePass1!",
"confirmPassword": "NewSecurePass1!"
}| Field | Type | Rules |
|---|---|---|
newPassword |
string | 8–100 chars, must contain uppercase, lowercase, digit, special character |
confirmPassword |
string | Must match newPassword |
{
"success": true,
"message": "Password changed successfully, please login again!"
}| Status | Message | Type |
|---|---|---|
400 |
"Passwords do not match" |
"BAD_REQUEST" |
400 |
"Please login" |
"BAD_REQUEST" |
400 |
Validation errors | "VALIDATION_ERROR" |
401 |
"Unauthorized" |
"UNAUTHORIZED" |
401 |
"Access token expired. Use refresh token to continue." |
"ACCESS_TOKEN_EXPIRED" |
401 |
"Session revoked or expired. Please login again." |
"REFRESH_TOKEN_EXPIRED" |
429 |
"Too many requests, please wait before retrying!" |
"TOO_MANY_REQUESTS" |
All user routes require authentication (access token cookie).
Common auth errors for all user routes:
| Status | Message | Type |
|---|---|---|
401 |
"Unauthorized" |
"UNAUTHORIZED" |
401 |
"Access token expired. Use refresh token to continue." |
"ACCESS_TOKEN_EXPIRED" |
401 |
"Session revoked or expired. Please login again." |
"REFRESH_TOKEN_EXPIRED" |
Returns the currently authenticated user. Checks Redis cache first; falls back to database and repopulates cache (TTL: 1 hour).
{
"success": true,
"message": "User retrieved successfully",
"data": {
"user": {
"id": "uuid",
"name": "John Doe",
"email": "john@example.com",
"emailVerified": true,
"createdAt": "2026-03-29T10:00:00.000Z",
"updatedAt": "2026-03-29T10:00:00.000Z"
}
}
}| Status | Message | Type |
|---|---|---|
400 |
"User not found!" |
"USER_NOT_FOUND" |
Updates the authenticated user's profile. Currently supports updating name only. Invalidates and refreshes Redis user cache on success.
{
"name": "Jane Doe"
}| Field | Type | Rules |
|---|---|---|
name |
string | Required, non-empty |
{
"success": true,
"message": "User updated successfully",
"data": {
"user": { ...User }
}
}| Status | Message | Type |
|---|---|---|
404 |
"User not found!" |
"USER_NOT_FOUND" |
400 |
Validation errors | "VALIDATION_ERROR" |
Permanently deletes the authenticated user's account and all associated tokens (cascade). Clears the Redis user cache entry.
{
"success": true,
"message": "User deleted successfully!"
}| Status | Message | Type |
|---|---|---|
404 |
"User not found!" |
"USER_NOT_FOUND" |
Returns all active sessions (refresh tokens) for the authenticated user. lastActive is populated from Redis. current is true for the session matching the provided device.
| Param | Type | Rules |
|---|---|---|
device |
string | Required, valid UUID v4 |
Example: GET /user/sessions?device=a1b2c3d4-e5f6-7890-abcd-ef1234567890
{
"success": true,
"message": "Sessions fetched successfully!",
"data": {
"sessions": [
{
"id": "uuid",
"device": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"deviceName": "Chrome on Windows",
"lastActive": "2026-03-29T14:30:00.000Z",
"current": true
},
{
"id": "uuid",
"device": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"deviceName": "Mobile Safari",
"lastActive": "2026-03-28T09:00:00.000Z",
"current": false
}
]
}
}| Field | Type | Description |
|---|---|---|
id |
string | Token ID — use this as tokenId to revoke the session |
device |
string | null | The device UUID associated with this session |
deviceName |
string | null | Parsed from User-Agent at login time |
lastActive |
ISO datetime | null | Last authenticated request timestamp (from Redis) |
current |
boolean | true if this session belongs to the requesting device |
| Status | Message | Type |
|---|---|---|
400 |
Validation errors | "VALIDATION_ERROR" |
Revokes a specific session by its token ID. Verifies the session belongs to the authenticated user before revoking (prevents IDOR). Sets a Redis revocation key to block any in-flight access token for that session.
{
"tokenId": "uuid-of-session-to-revoke"
}| Field | Type | Rules |
|---|---|---|
tokenId |
string | Required, non-empty. Obtain from GET /user/sessions |
{
"success": true,
"message": "Sessions revoked successfully!"
}| Status | Message | Type |
|---|---|---|
400 |
"Token not found." |
"APP_ERROR" |
400 |
"Invalid session, please login again!" |
"APP_ERROR" |
401 |
"Unable to revoke token." |
"APP_ERROR" |
400 |
Validation errors | "VALIDATION_ERROR" |
Every request
└─ If response type === "ACCESS_TOKEN_EXPIRED"
└─ Call POST /auth/get-access-token { device }
├─ Success → retry original request
└─ Failure (type === "REFRESH_TOKEN_EXPIRED") → redirect to login