@nestbolt/authentication
API Routes
Complete reference for all 19 authentication endpoints provided by @nestbolt/authentication, including request bodies, response formats, and error codes.
This page documents every HTTP endpoint registered by the authentication module. Routes are grouped by feature area. All request and response bodies use JSON.
Access tokens are sent as Bearer tokens in the Authorization header:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...Refresh tokens are sent in the request body.
Core Authentication
These routes are always available, regardless of which features are enabled.
POST /login
Authenticate a user with their credentials. If the user has two-factor authentication enabled, a challenge token is returned instead of access/refresh tokens.
Authentication: None
Rate limiting: LoginThrottleGuard (default: 5 attempts per 60 seconds, per username+IP)
Request body:
{
"email": "user@example.com",
"password": "secretpassword",
"remember": false
}| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | The user's email (or whatever field usernameField is set to) |
password | string | Yes | The user's password |
remember | boolean | No | Whether to remember the user (passed to the 2FA challenge token) |
Response (no 2FA):
{
"twoFactor": false,
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}Response (2FA required):
{
"twoFactor": true,
"challengeToken": "eyJhbGciOiJIUzI1NiIs..."
}The challengeToken is a short-lived JWT (5 minutes) that must be sent to POST /two-factor-challenge along with a TOTP code or recovery code.
Error responses:
| Status | Condition |
|---|---|
401 Unauthorized | Invalid credentials |
429 Too Many Requests | Rate limit exceeded |
POST /refresh
Exchange a valid refresh token for a new access/refresh token pair.
Authentication: Refresh token (sent in request body, validated by jwt-refresh strategy)
Request body:
{
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}Response:
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}Error responses:
| Status | Condition |
|---|---|
401 Unauthorized | Invalid or expired refresh token |
POST /logout
Log the user out. Emits the auth.logout event.
Authentication: JWT Bearer token
Request body: None
Response:
{
"message": "Logged out successfully."
}Error responses:
| Status | Condition |
|---|---|
401 Unauthorized | Missing or invalid access token |
Registration
Requires: Feature.REGISTRATION
POST /register
Create a new user account and return access/refresh tokens.
Authentication: None
Request body:
{
"name": "Jane Doe",
"email": "jane@example.com",
"password": "secretpassword",
"passwordConfirmation": "secretpassword"
}| Field | Type | Required | Validation |
|---|---|---|---|
name | string | Yes | Not empty, max 255 characters |
email | string | Yes | Valid email, max 255 characters |
password | string | Yes | Min 8 characters |
passwordConfirmation | string | Yes | Not empty |
Response (201 Created):
{
"twoFactor": false,
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}Error responses:
| Status | Condition |
|---|---|
400 Bad Request | Validation errors |
404 Not Found | Feature not enabled |
409 Conflict | User already exists (if your CreatesNewUsers implementation throws this) |
500 Internal Server Error | Missing CREATES_NEW_USERS provider |
Password Reset
Requires: Feature.RESET_PASSWORDS
POST /forgot-password
Request a password reset token. The raw token is returned in the response -- you are responsible for delivering it to the user (e.g., via email).
Authentication: None
Request body:
{
"email": "user@example.com"
}| Field | Type | Required | Validation |
|---|---|---|---|
email | string | Yes | Valid email |
Response:
{
"message": "If the email exists, a reset link has been sent."
}The response message is intentionally vague to prevent email enumeration. The raw token is also returned in the response object (access it via the token field in the PasswordResetService.sendResetLink() return value).
Error responses:
| Status | Condition |
|---|---|
400 Bad Request | Validation errors |
404 Not Found | Feature not enabled |
POST /reset-password
Reset the user's password using a valid reset token.
Authentication: None
Request body:
{
"email": "user@example.com",
"token": "a1b2c3d4e5f6...",
"password": "newsecretpassword",
"passwordConfirmation": "newsecretpassword"
}| Field | Type | Required | Validation |
|---|---|---|---|
email | string | Yes | Valid email |
token | string | Yes | Not empty |
password | string | Yes | Min 8 characters |
passwordConfirmation | string | Yes | Not empty |
Response:
{
"message": "Your password has been reset."
}Error responses:
| Status | Condition |
|---|---|
400 Bad Request | Validation errors |
404 Not Found | Feature not enabled |
422 Unprocessable Entity | Invalid token, expired token (older than 60 minutes), or user not found |
Email Verification
Requires: Feature.EMAIL_VERIFICATION
GET /email/verify/:id/:hash
Verify a user's email address using a signed URL.
Authentication: JWT Bearer token
Query parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
signature | string | Yes | HMAC-SHA256 signature of the verification payload |
expires | string | Yes | Unix timestamp when the link expires |
Response:
{
"message": "Email verified successfully."
}Error responses:
| Status | Condition |
|---|---|
400 Bad Request | Missing signature or expires parameter, or user not found |
401 Unauthorized | User ID in URL does not match authenticated user |
404 Not Found | Feature not enabled |
422 Unprocessable Entity | Link expired, invalid hash, or invalid signature |
POST /email/verification-notification
Request a new verification notification. Returns the verification data needed to construct a verification URL.
Authentication: JWT Bearer token
Rate limiting: VerificationThrottleGuard (default: 6 requests per 60 seconds)
Request body: None
Response (email not yet verified):
{
"message": "Verification link sent.",
"id": "user-uuid",
"hash": "abc123...",
"signature": "def456...",
"expires": "1712500000000"
}Response (email already verified):
{
"message": "Email already verified."
}Error responses:
| Status | Condition |
|---|---|
401 Unauthorized | Missing or invalid access token |
404 Not Found | Feature not enabled |
429 Too Many Requests | Rate limit exceeded |
Profile Management
Requires: Feature.UPDATE_PROFILE_INFORMATION
PUT /user/profile-information
Update the authenticated user's profile information.
Authentication: JWT Bearer token
Request body:
{
"name": "Jane Smith",
"email": "jane.smith@example.com"
}| Field | Type | Required | Validation |
|---|---|---|---|
name | string | No | Max 255 characters |
email | string | No | Valid email, max 255 characters |
Both fields are optional -- send only the fields you want to update.
Response:
{
"message": "Profile information updated."
}Error responses:
| Status | Condition |
|---|---|
400 Bad Request | Validation errors |
401 Unauthorized | Missing or invalid access token |
404 Not Found | Feature not enabled |
500 Internal Server Error | Missing UPDATES_USER_PROFILE provider |
Password Update
Requires: Feature.UPDATE_PASSWORDS
PUT /user/password
Change the authenticated user's password. Requires the current password for verification.
Authentication: JWT Bearer token
Request body:
{
"currentPassword": "oldsecretpassword",
"password": "newsecretpassword",
"passwordConfirmation": "newsecretpassword"
}| Field | Type | Required | Validation |
|---|---|---|---|
currentPassword | string | Yes | Not empty |
password | string | Yes | Min 8 characters |
passwordConfirmation | string | Yes | Not empty |
Response:
{
"message": "Password updated."
}Error responses:
| Status | Condition |
|---|---|
400 Bad Request | Validation errors |
401 Unauthorized | Missing or invalid access token |
404 Not Found | Feature not enabled |
422 Unprocessable Entity | Current password incorrect (if your action throws this) |
500 Internal Server Error | Missing UPDATES_USER_PASSWORDS provider |
Password Confirmation
These routes are always available (not feature-gated).
POST /user/confirm-password
Confirm the user's password. Stores a timestamp that the PasswordConfirmedGuard checks when guarding sensitive operations.
Authentication: JWT Bearer token
Request body:
{
"password": "secretpassword"
}| Field | Type | Required | Validation |
|---|---|---|---|
password | string | Yes | Not empty |
Response (password correct):
{
"confirmed": true
}Error responses:
| Status | Condition |
|---|---|
401 Unauthorized | Missing or invalid access token |
422 Unprocessable Entity | Incorrect password |
GET /user/confirmed-password-status
Check whether the user has recently confirmed their password.
Authentication: JWT Bearer token
Query parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
seconds | string | No | Override the default timeout (in seconds). Defaults to passwordTimeout option (900). |
Response:
{
"confirmed": true
}or
{
"confirmed": false
}Two-Factor Authentication
Requires: Feature.TWO_FACTOR_AUTHENTICATION
POST /user/two-factor-authentication
Enable two-factor authentication for the authenticated user. Generates a TOTP secret and 8 recovery codes, stored encrypted in the database.
Authentication: JWT Bearer token
Guard: PasswordConfirmedGuard (active when twoFactorOptions.confirmPassword is true)
Request body (optional):
{
"force": true
}| Field | Type | Required | Description |
|---|---|---|---|
force | boolean | No | If true, regenerates the secret even if 2FA is already enabled |
Response:
{
"message": "Two-factor authentication enabled."
}DELETE /user/two-factor-authentication
Disable two-factor authentication. Removes the TOTP secret, recovery codes, and confirmation timestamp.
Authentication: JWT Bearer token
Guard: PasswordConfirmedGuard
Request body: None
Response:
{
"message": "Two-factor authentication disabled."
}POST /user/confirmed-two-factor-authentication
Confirm the 2FA setup by providing a valid TOTP code from the user's authenticator app. This sets the twoFactorConfirmedAt timestamp.
Required when twoFactorOptions.confirm is true. Without confirmation, the 2FA setup is not enforced during login.
Authentication: JWT Bearer token
Guard: PasswordConfirmedGuard
Request body:
{
"code": "123456"
}| Field | Type | Required | Validation |
|---|---|---|---|
code | string | Yes | Not empty |
Response:
{
"message": "Two-factor authentication confirmed."
}Error responses:
| Status | Condition |
|---|---|
422 Unprocessable Entity | Invalid TOTP code or 2FA not enabled |
GET /user/two-factor-qr-code
Get a QR code for setting up 2FA in an authenticator app. Returns the QR code as an SVG string and the otpauth URL.
Authentication: JWT Bearer token
Guard: PasswordConfirmedGuard
Response:
{
"svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" ...",
"url": "otpauth://totp/MyApp:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=MyApp"
}Error responses:
| Status | Condition |
|---|---|
404 Not Found | 2FA not enabled for this user |
GET /user/two-factor-secret-key
Get the raw TOTP secret key (base32-encoded). Useful for manual entry in authenticator apps that do not support QR code scanning.
Authentication: JWT Bearer token
Guard: PasswordConfirmedGuard
Response:
{
"secretKey": "JBSWY3DPEHPK3PXP"
}Error responses:
| Status | Condition |
|---|---|
404 Not Found | 2FA not enabled for this user |
GET /user/two-factor-recovery-codes
Get the current list of recovery codes for the authenticated user.
Authentication: JWT Bearer token
Guard: PasswordConfirmedGuard
Response:
[
"aBcDeFgHiJ-kLmNoPqRsT",
"uVwXyZ0123-4567890abc",
"..."
]Returns an empty array if 2FA is not enabled.
POST /user/two-factor-recovery-codes
Regenerate all recovery codes. Replaces the existing codes with 8 new ones.
Authentication: JWT Bearer token
Guard: PasswordConfirmedGuard
Response:
{
"message": "Recovery codes regenerated."
}POST /two-factor-challenge
Complete a two-factor authentication challenge during login. This is the second step of the login flow when the user has 2FA enabled.
Authentication: None (uses the challenge token from the login response)
Rate limiting: TwoFactorThrottleGuard (default: 5 attempts per 60 seconds, per IP)
Request body (with TOTP code):
{
"challengeToken": "eyJhbGciOiJIUzI1NiIs...",
"code": "123456"
}Request body (with recovery code):
{
"challengeToken": "eyJhbGciOiJIUzI1NiIs...",
"recoveryCode": "aBcDeFgHiJ-kLmNoPqRsT"
}| Field | Type | Required | Description |
|---|---|---|---|
challengeToken | string | Yes | The challenge token from the login response |
code | string | No | A 6-digit TOTP code from the authenticator app |
recoveryCode | string | No | A recovery code |
One of code or recoveryCode must be provided.
Response:
{
"twoFactor": false,
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}Error responses:
| Status | Condition |
|---|---|
400 Bad Request | Neither code nor recoveryCode provided |
401 Unauthorized | Invalid or expired challenge token |
422 Unprocessable Entity | Invalid TOTP code or recovery code |
429 Too Many Requests | Rate limit exceeded |
Error Response Format
All error responses follow the standard NestJS exception format:
{
"statusCode": 422,
"message": "The provided two-factor code was invalid.",
"error": "Unprocessable Entity"
}For validation errors (400 Bad Request), the message field is an array of validation error strings:
{
"statusCode": 400,
"message": [
"email must be an email",
"password must be longer than or equal to 8 characters"
],
"error": "Bad Request"
}Next Steps
- Decorators and Guards -- use the package's decorators and guards in your own controllers.
- Events -- subscribe to authentication lifecycle events.
Features
Detailed explanation of each Feature enum value in @nestbolt/authentication, including what routes, services, and action providers each feature enables.
Decorators and Guards
Reference for all decorators and guards exported by @nestbolt/authentication, with usage examples for building custom protected controllers.