OS Trading Engine
API Reference
Authentication
Login & Authentication

Login & Authentication

Endpoints for user authentication.


Admin Account

The admin account is created from environment variables when the backend starts:

VariableRequiredDescription
ADMIN_EMAILYesEmail address used to log in
ADMIN_PASSWORDYesPassword for the admin account

Behavior:

  • First boot (no users): The admin account is created with the email and password from the env vars.
  • Subsequent boots: If ADMIN_EMAIL or ADMIN_PASSWORD change, the existing admin account is updated to match.
  • No password strength rules are enforced for these env vars, so deployment platforms can accept any value you set.

Set ADMIN_EMAIL and ADMIN_PASSWORD in your backend .env (see Quickstart). After starting the backend, use those credentials to log in via the dashboard or the Login endpoint below.


Login

Authenticate an existing user.

POST /api/v1/auth/login

Request

curl -X POST https://your-instance.com/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "SecurePassword123!",
    "rememberMe": false
  }'

Request Body

FieldTypeRequiredDescription
emailstringYesUser's email address
passwordstringYesUser's password
rememberMebooleanNoExtended refresh token (30 days vs 24h)

Response

Success (200):

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "user@example.com",
    "createdAt": "2024-01-15T10:30:00.000Z"
  }
}

Errors

StatusErrorCause
400Email and password are requiredMissing fields
401Invalid email or passwordWrong credentials
423Account is temporarily locked...Too many failed attempts

Account Lockout

After 5 failed login attempts, the account is locked for 15 minutes.

Lockout Response (423):

{
  "error": "Account is temporarily locked due to too many failed login attempts",
  "lockedUntil": "2024-01-15T10:45:00.000Z"
}

The lockout:

  • Resets automatically after 15 minutes
  • Resets on successful login
  • Counter resets after successful login

Get Current User

Get information about the authenticated user.

GET /api/v1/auth/me

Request

curl -X GET https://your-instance.com/api/v1/auth/me \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Response

Success (200):

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "email": "user@example.com",
  "createdAt": "2024-01-15T10:30:00.000Z"
}

Errors

StatusErrorCause
401Authorization header requiredNo token
401Invalid tokenToken invalid or expired

Logout

Logout the current user and invalidate tokens.

POST /api/v1/auth/logout

Request

curl -X POST https://your-instance.com/api/v1/auth/logout \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{
    "refreshToken": "eyJhbGciOiJIUzI1NiIs..."
  }'

Request Body (Optional)

FieldTypeRequiredDescription
refreshTokenstringNoRefresh token to explicitly revoke

Response

Success (200):

{
  "success": true,
  "message": "Logged out successfully"
}

What Happens on Logout

  1. Access token blacklisted - Immediately invalidated via Redis
  2. Refresh token revoked - If provided, removed from Redis
  3. Client should - Clear stored tokens

The access token is blacklisted for its remaining lifetime (up to 15 minutes), ensuring it cannot be reused.


Authentication Flow Example

1. Login

Use the credentials from your backend ADMIN_EMAIL and ADMIN_PASSWORD.

const response = await fetch('/api/v1/auth/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: 'user@example.com',
    password: 'SecurePassword123!',
    rememberMe: true
  })
});
 
const { accessToken, refreshToken, user } = await response.json();
 
// Store tokens securely
storeAccessToken(accessToken);
storeRefreshToken(refreshToken);

2. Make Authenticated Requests

const response = await fetch('/api/v1/agents', {
  headers: {
    'Authorization': `Bearer ${getAccessToken()}`
  }
});

3. Handle Token Expiration

// When access token expires (401 response)
const refreshResponse = await fetch('/api/v1/auth/refresh', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    refreshToken: getRefreshToken()
  })
});
 
if (refreshResponse.ok) {
  const { accessToken, refreshToken } = await refreshResponse.json();
  storeAccessToken(accessToken);
  storeRefreshToken(refreshToken);
  // Retry original request
} else {
  // Redirect to login
  redirectToLogin();
}

4. Logout

await fetch('/api/v1/auth/logout', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${getAccessToken()}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    refreshToken: getRefreshToken()
  })
});
 
// Clear stored tokens
clearTokens();