Auth Endpoints
Detailed API reference for authentication endpoints — Setup, Register, Login, Refresh, Logout, and Change Password.
Auth Endpoints
Base path: /api/v1/auth
Rate Limiting
| Endpoint | Limiter | Limit |
|---|---|---|
POST /setup | IP-based | 20 req / 15 min |
POST /register | IP-based | 3 req / hour |
POST /login | IP + Email | 5 req / 15 min (email), 20 req / 15 min (IP) |
POST /refresh | Token-based | 30 req / min |
POST /logout | None | — |
POST /change-password | Authenticated | Global API limiter |
POST /auth/setup
Creates the initial Super Admin account. Can only be called once — returns 403 if an admin already exists.
Request
POST /api/v1/auth/setup
Content-Type: application/json{
"firstName": "System",
"lastName": "Admin",
"email": "admin@university.edu",
"contactNumber": "+1234567890",
"password": "SecureP@ssw0rd123"
}Response — 201 Created
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"_id": "64b8f3a2e7d1c...",
"firstName": "System",
"lastName": "Admin",
"email": "admin@university.edu",
"roleId": "64b8f...",
"status": "ACTIVE"
}
}A refreshToken is set as an HttpOnly cookie.
Error Responses
| Status | Code | Condition |
|---|---|---|
403 | — | Admin already exists |
400 | — | Admin role not found (roles not seeded) |
POST /auth/register
Creates a new Student account.
Request
{
"firstName": "Aarav",
"lastName": "Sharma",
"email": "aarav@university.edu",
"contactNumber": "+919876543210",
"rollNumber": "CSE-2024-001",
"password": "MyP@ssw0rd123"
}Response — 201 Created
{
"message": "Registration successful"
}A welcome email is sent asynchronously.
Error Responses
| Status | Condition |
|---|---|
400 | Roll number already exists |
400 | Email already exists |
500 | Student role not found in database |
429 | Rate limited (3/hour per IP) |
POST /auth/login
Authenticates a user by email or roll number.
Request
{
"identifier": "aarav@university.edu",
"password": "MyP@ssw0rd123"
}The identifier field accepts either an email address or a roll number.
Response — 200 OK
{
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"_id": "64b8f3a2e7d1c...",
"firstName": "Aarav",
"lastName": "Sharma",
"email": "aarav@university.edu",
"rollNumber": "CSE-2024-001",
"status": "ACTIVE",
"roleId": "64b8f..."
}
}A refreshToken is set as an HttpOnly cookie.
Error Responses
| Status | Condition |
|---|---|
401 | Invalid credentials (email not found OR wrong password) |
403 | Account is SUSPENDED, PENDING, or DISABLED |
403 | Account is temporarily locked (too many failed attempts) |
429 | Rate limited |
Brute Force Protection
- After 5 failed login attempts, the account is locked for 15 minutes
- Failed attempts are reset on successful login
- All attempts are logged to the audit trail
POST /auth/refresh
Rotates the refresh token and issues a new access token.
Request
No body required. The refreshToken is read from the HttpOnly cookie.
Response — 200 OK
{
"accessToken": "eyJhbGciOiJIUzI1NiIs..."
}A new refreshToken cookie is set (old one is deleted from the database).
Error Responses
| Status | Condition |
|---|---|
400 | No refresh token in cookies |
401 | Invalid token format |
401 | Token not found in database |
401 | Token expired or revoked |
401 | User inactive or deleted |
POST /auth/logout
Revokes the current refresh token and clears the cookie.
Request
No body required.
Response — 200 OK
{
"message": "Successfully logged out"
}POST /auth/change-password
Changes the authenticated user's password. Requires authenticate middleware.
Request
POST /api/v1/auth/change-password
Authorization: Bearer <accessToken>
Content-Type: application/json{
"currentPassword": "OldP@ssw0rd123",
"newPassword": "NewP@ssw0rd456"
}Response — 200 OK
{
"message": "Password updated successfully. All devices have been logged out."
}Side Effects
- All refresh tokens for the user are deleted (logs out all devices)
tokenVersionis incremented (invalidates all outstanding access tokens)
Error Responses
| Status | Condition |
|---|---|
401 | Not authenticated |
401 | Current password is incorrect |
404 | User not found |
Security Notes
- Passwords are hashed with Argon2id (not bcrypt)
- Refresh tokens are stored as Argon2id hashes — raw tokens never touch the database
- The
passwordHashfield is always excluded from API responses - Error messages never reveal whether an email exists in the system ("Invalid credentials" for both cases)