Skip to main content

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:

SettingValue
Failed attempts threshold5
Lockout window15 minutes
Lockout duration15 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.