Authentication
On this page
dembrane’s data, authentication, and file storage all live in Directus. The FastAPI backend doesn’t run its own user database; it trusts tokens issued by Directus and reads the claims inside them. So “authenticating against dembrane” almost always means “presenting a valid Directus token”. This page explains the three ways to do that and how the backend interprets what it receives.
Note
This page is about the authenticated API (the dashboard and integrations). The participant API is deliberately unauthenticated - that’s how someone can record via a link with no account.
How the backend reads a request#
The backend’s auth dependency (dependency_auth.py) looks for a Directus session token in one
of two places, in this order:
- A cookie named
directus_session_token(this is how the browser-based dashboard and portal authenticate - Directus sets the cookie on login). - An
Authorization: Bearer <token>header (this is how server-to-server integrations authenticate).
Either way, the token is validated as a Directus JWT and its claims are used to identify the user and their permissions. There’s no separate dembrane login: a valid Directus token is a valid dembrane request.
Obtaining a token via Directus#
For an integration, log in against your Directus instance and use the access token it returns:
curl -X POST https://YOUR-DIRECTUS-HOST:8055/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"you@example.org","password":"••••••••"}'
Directus replies with an access_token (a short-lived JWT) and a refresh_token. Send the
access token as a bearer token on subsequent calls to the dembrane backend:
curl https://YOUR-API-HOST:8000/api/v2/... \
-H "Authorization: Bearer $ACCESS_TOKEN"
When the access token expires, exchange the refresh token at Directus’s /auth/refresh
endpoint for a fresh pair. (Directus is the authority here; its
authentication documentation covers refresh, logout, and 2FA in
full. dembrane’s dashboard supports two-factor authentication on login.)
Tip
For local development with self-hosting, your Directus admin
credentials come from directus/.env. Logging in with them gives you a token whose claims
include admin_access (see below).
Static tokens for integrations#
For a long-lived, non-interactive integration - a backend job, a sync script - a per-session
login is awkward. Directus supports static access tokens: assign one to a dedicated
service user in Directus, and send it as the bearer token. dembrane reads it from the
environment as DIRECTUS_TOKEN for its own server-to-server calls, and you can mint
equivalent tokens for your integrations.
Important
A static token carries the full permissions of the user it's attached to and doesn't expire on its own. Treat it like a password: scope its user to exactly what the integration needs, store it as a secret, and rotate it if it leaks. Prefer a narrowly-permissioned service user over reusing an admin token.
The admin_access claim (staff)#
dembrane has a notion of staff - dembrane employees who operate the
admin panel. Staff status is not a dembrane
role in the roles & permissions sense; it’s a JWT
claim, admin_access, that Directus sets when the user has the Directus admin role.
The backend gates the staff-only endpoints (everything under /api/v2/admin/*, plus actions
like setting a tier or transferring a workspace) on admin_access == true. If you self-host,
your own Directus admins will carry this claim - which means they can reach the admin/billing
surface. Grant the Directus admin role deliberately.
The everyday org and workspace roles (owner, admin,
member, billing, external, observer) are evaluated separately, per organisation and per
workspace, against the policies in policies.py. admin_access sits above all of that and is
for dembrane-operations use.
Which token for which job#
| You’re building… | Use |
|---|---|
| A browser app on top of the dashboard | The directus_session_token cookie (set by Directus login). |
| A server-to-server integration | A bearer token - a static DIRECTUS_TOKEN-style token on a scoped service user. |
| A short-lived script | POST /auth/login → use the access_token as a bearer token; refresh as needed. |
| Recording from a public link | Nothing - use the unauthenticated participant API. |
Related#
- The participant API - the unauthenticated endpoints.
- Webhooks - outbound events (signed, not bearer-authenticated).
- Export & integrations - authenticated export endpoints.
- Roles & permissions - the org/workspace role model.
- Self-hosting - where Directus fits in the stack.
Comments