Skip to content

Authentication

Model

The backend uses short-lived bearer access tokens plus an opaque refresh token in an HttpOnly cookie.

Send the access token in the Authorization header.

Authorization: Bearer <token>

POST /api/v1/auth/register

Field Value
Auth No
Success 201 auth result { access_token, user } plus Set-Cookie: refresh_token=...
Errors 400 INVALID_INPUT, 400 INVALID_EMAIL, 400 INVALID_PASSWORD, 400 EMAIL_ALREADY_EXISTS, 500 INTERNAL_SERVER_ERROR
Notes Password must be at least 8 characters

Request:

{
  "email": "demo@example.com",
  "password": "password123"
}

Response:

{
  "access_token": "eyJhbGciOi...",
  "user": {
    "id": "7d7efb4d-0f1c-4f69-9b74-5e2a0d5d5a50",
    "email": "demo@example.com",
    "created_at": "2026-05-05T12:00:00Z"
  }
}

Validation errors use the shared envelope, for example:

{
  "error": {
    "code": "INVALID_EMAIL",
    "message": "Invalid email",
    "details": {
      "field": "email"
    }
  }
}

POST /api/v1/auth/login

Field Value
Auth No
Success 200 auth result { access_token, user } plus Set-Cookie: refresh_token=...
Errors 400 INVALID_INPUT, 401 INVALID_CREDENTIALS, 500 INTERNAL_SERVER_ERROR
Notes Missing users and bad passwords both map to INVALID_CREDENTIALS

Request:

{
  "email": "demo@example.com",
  "password": "password123"
}

Response:

{
  "access_token": "eyJhbGciOi...",
  "user": {
    "id": "7d7efb4d-0f1c-4f69-9b74-5e2a0d5d5a50",
    "email": "demo@example.com",
    "created_at": "2026-05-05T12:00:00Z"
  }
}

POST /api/v1/auth/refresh

Field Value
Auth Refresh cookie
Success 200 auth result { access_token, user } plus rotated Set-Cookie: refresh_token=...
Errors 401 UNAUTHORIZED
Notes Refresh tokens are single-use; a successful refresh revokes the previous refresh token

Request body is empty. The browser/client must send the refresh_token cookie scoped to /api/v1/auth.

POST /api/v1/auth/logout

Field Value
Auth Optional bearer token and optional refresh cookie
Success 200 { "status": "ok" } and cleared refresh cookie
Errors None expected for invalid or missing tokens
Notes If a valid access token is provided, its JWT ID is blacklisted until expiry; the refresh token is revoked when present

GET /api/v1/auth/me

Field Value
Auth Yes
Success 200 user object { id, email, created_at }
Errors 401 UNAUTHORIZED, 500 INTERNAL_SERVER_ERROR
Notes Returns the current token subject

Response:

{
  "id": "7d7efb4d-0f1c-4f69-9b74-5e2a0d5d5a50",
  "email": "demo@example.com",
  "created_at": "2026-05-05T12:00:00Z"
}

Missing or invalid bearer tokens return 401 with UNAUTHORIZED.