Authentication
Atlas delegates authentication to Keycloak using OpenID Connect. Each tenant has its own Keycloak realm; a request carries a JWT from which the backend derives the tenant, user, and roles. Authorization is role-based, and the tenant claim drives the database Row-Level Security that isolates data.
Token flow
The frontend uses keycloak-js with PKCE and refreshes tokens ahead of expiry. In development the
Vite proxy keeps /realms and /resources same-origin; in production nginx does the same so
cookies and redirects behave.
What the backend does with a token
PlatformAuthMiddleware (src/api/auth.py) and KeycloakClient
(src/integrations/keycloak_client.py) together:
- Extract the
Bearertoken. - Validate it against the tenant's Keycloak realm.
- Read
tenant_id,sub(user id), androles. - Build an
AuthContextonrequest.stateand bind the tenant to the DB session for RLS.
A request without a valid token, or without a resolvable tenant, is rejected — require_tenant
returns 401. This is the fail-closed default described in
Security & multi-tenancy.
Roles
| Role | Capability |
|---|---|
admin | Full access including Settings and Studio configuration |
analyst | Run and review investigations, resolve conflicts |
viewer | Read-only access |
workflow_editor | Author and run low-code workflows |
Endpoint guards enforce these; Studio and Settings are admin-only. Workflow-specific authorization
lives in src/workflows/auth.py. Role assignment is managed via auth_router (admin).
Tenant resolution (frontend)
Before login, the Console resolves which realm to use from the URL slug:
See Frontend architecture for the provider tree that implements this.
Service-to-service & workers
Temporal workers operate within a tenant context propagated from the originating request, so activities persist data under the correct tenant and RLS applies to worker database access just as it does to the API.