Login
Authenticate a user with email and password to receive JWT tokens.
Endpoint
POST /auth/login
Request
{
"email": "john.doe@acme.com",
"password": "SecureP@ss123"
}
Response (200 OK)
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "john.doe@acme.com",
"firstName": "John",
"lastName": "Doe",
"tenantId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}
}
Error Responses
401 Unauthorized — Invalid Credentials
{
"type": "https://api.cortex.purplelab.ai/errors/invalid-credentials",
"title": "Invalid Credentials",
"status": 401,
"detail": "The email or password provided is incorrect",
"instance": "/auth/login"
}
Security Note
The error message is intentionally vague — it does not reveal whether the email exists or the password was wrong. This prevents user enumeration attacks.
403 Forbidden — Account Locked
{
"type": "https://api.cortex.purplelab.ai/errors/account-locked",
"title": "Account Locked",
"status": 403,
"detail": "Account is temporarily locked due to too many failed login attempts. Try again in 15 minutes.",
"instance": "/auth/login"
}
403 Forbidden — Account Suspended
{
"type": "https://api.cortex.purplelab.ai/errors/account-suspended",
"title": "Account Suspended",
"status": 403,
"detail": "This account has been suspended. Please contact your administrator.",
"instance": "/auth/login"
}
Account Lockout
CORTEX implements automatic account lockout to prevent brute-force attacks:
| Setting | Value |
|---|---|
| Failed attempts threshold | 5 |
| Lockout window | 15 minutes |
| Lockout duration | 15 minutes |
After 5 failed login attempts within 15 minutes, the account is locked for 15 minutes. The counter resets after a successful login.
Code Examples
TypeScript / Fetch
interface LoginResponse {
accessToken: string;
refreshToken: string;
user: {
id: string;
email: string;
firstName: string;
lastName: string;
tenantId: string;
};
}
async function login(email: string, password: string): Promise<LoginResponse> {
const response = await fetch('http://localhost:8091/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail);
}
return response.json();
}
// Usage
try {
const { accessToken, user } = await login(
'john.doe@acme.com',
'SecureP@ss123'
);
console.log('Logged in as:', user.email);
// Store accessToken for subsequent requests
} catch (error) {
console.error('Login failed:', error.message);
}
cURL
curl -X POST http://localhost:8091/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "john.doe@acme.com", "password": "SecureP@ss123"}'
Token Usage
After login, include the access token in the Authorization header for all protected endpoints:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
When the access token expires (after 15 minutes), use the refresh token to obtain a new pair.
Session Creation
Each successful login creates a new session that tracks:
- Login timestamp
- IP address
- User agent
- Last activity
Users can view their active sessions via the /auth/sessions endpoint.