API Authentication
The Atrium API uses JWT bearer tokens for authentication. Include the token in the Authorization header:
Authorization: Bearer <access_token>
Obtaining a token
Login (built-in IdP)
POST /api/v1/auth/login
Content-Type: application/json
{
"tenant": "your-tenant-slug",
"email": "user@example.com",
"password": "your-password"
}
Response:
{
"accessToken": "eyJ...",
"refreshToken": "opaque-refresh-token",
"expiresIn": 900,
"user": {
"id": "...",
"email": "user@example.com",
"displayName": "User Name",
"role": "tenant_admin",
"siteIds": ["..."]
}
}
Login (OIDC)
For external IdP login, use the browser-based OIDC flow. The API provides the redirect URL:
GET /api/v1/auth/oidc/authorize?tenant=your-tenant-slug
This redirects to your IdP. After authentication, the callback issues tokens.
Token lifecycle
| Token | Type | Lifetime | Purpose |
|---|---|---|---|
| Access token | JWT | 15 minutes | Authenticates API requests. |
| Refresh token | Opaque | 7 days | Obtains new access tokens without re-login. |
Refreshing tokens
When the access token expires, use the refresh token to get a new one:
POST /api/v1/auth/refresh
Content-Type: application/json
{
"refreshToken": "opaque-refresh-token"
}
Response: Same format as login (new access token and refresh token).
The old refresh token is invalidated — each refresh token can only be used once (rotation).
Token claims
The access token JWT contains:
| Claim | Description |
|---|---|
sub | User ID |
tenant_id | Tenant UUID |
site_ids | Array of accessible site UUIDs |
role | tenant_admin, site_admin, or member |
token_type | user, kiosk, or agent |
exp | Expiry timestamp |
Token type enforcement
Every API endpoint declares which token types it accepts. Most endpoints only accept user tokens. Kiosk endpoints accept kiosk tokens. Agent endpoints use mTLS (no JWT).
Sending a kiosk token to an admin endpoint returns 403 Forbidden.
Logout
POST /api/v1/auth/logout
Authorization: Bearer <access_token>
Invalidates the current refresh token. The access token remains valid until it expires (up to 15 minutes), but the refresh token can no longer be used.