Authentication
Mesachat authenticates with Clerk. Every request under /api needs a valid
principal unless its path is on a short, explicit allowlist.
Derived from backend/src/auth/.
The gate is fail-closed
There is one mandatory checkpoint — apiAuthGate, mounted once before the route table
(backend/src/api/server.ts). Being authenticated is the default; being public is the exception,
and the exceptions are enumerated in code rather than opted into per router.
Two consequences worth knowing before you debug a 401:
- Guards do not pass through when Clerk is unconfigured. A deployment with no Clerk credentials
does not become open; it refuses everything under
/api. - The gate matches case-insensitively. Express routes case-insensitively by default, so
/API/botsreaches the router mounted at/api/bots. The gate lowercases the path first, so the casing trick does not slip past it.
Sending credentials
Pass a Clerk session token as a bearer token:
curl -H "Authorization: Bearer $CLERK_SESSION_TOKEN" \
http://localhost:3001/api/bots
In the web app and admin console this is handled for you — the browser holds a Clerk session and the app attaches the token.
For scripted access, mint a session token with Clerk's Backend API for a user that already has the
membership you need. Mesachat does not issue its own long-lived API tokens; there is no
"create an API key for the REST API" endpoint. (/api/keys manages provider keys — your OpenAI
or Anthropic credentials — not credentials for this API. See REST API.)
Tenant scope comes from the token
Tenant scope is derived from the authenticated principal, never from client input. Sending
tenantId in a body or query string does not select a tenant, and cannot be used to reach one you
are not a member of. Where a tenantId appears in a path it is validated against the caller before
the handler runs.
This is a load-bearing security property, not a convention. Do not design a client around passing a tenant identifier as data.
Endpoints reachable without authentication
Exactly four prefixes are allowlisted (PUBLIC_API_PREFIXES, backend/src/auth/clerk-middleware.ts):
| Prefix | Why it is public |
|---|---|
/api/health | Liveness probe |
/api/otel | Telemetry ingest proxy |
/api/telegram-auth | Telegram login flow — you cannot require a session to establish one |
/api/email-auth | Email sender verification flow, same reason |
Anything matching a prefix, or a path beneath it, passes through. Everything else under /api
requires a principal.
Two paths that look public and are not:
/api/authis the authorization administration router — rules, moderation, channel grants. It is not a login endpoint, and it requires authentication./api/tenantselects the active tenant for an existing session, so it needs one.
/webhooks/* is a separate surface outside /api. It is not covered by this gate at all; each
handler verifies its own signature. See Webhooks.
Telegram login
The flow lives under /api/telegram-auth, not /api/auth/telegram:
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/telegram-auth/config | Widget configuration for the client |
| GET | /api/telegram-auth/callback | Where the Telegram Login Widget returns |
| POST | /api/telegram-auth/complete | Completes the login and establishes the session |
| GET | /api/telegram-auth/status | Current state of an in-flight login |
Email sender authorization
/api/email-auth verifies that an email address may talk to a bot, and manages the resulting
grants: /validate-token, /check-tenant-access, /authorization-groups, /authorize-thread,
/authorized-threads, and /senders.
Local development
AUTH_DEV_MODE=true injects a synthetic admin principal so you can work without Clerk credentials.
It is refused outside local environments. The bypass is honoured only when NODE_ENV is
development or test; with AUTH_DEV_MODE=true under any other NODE_ENV, startup throws
rather than silently ignoring the flag. There is no configuration that makes it work in
production or staging.
After authentication
Authentication establishes who the caller is. What they may reach is a separate decision, made by the dual ReBAC + ABAC authorization system:
- Identity — who the caller is (Clerk)
- Relationships — what they are connected to (ReBAC)
- Policies — under what conditions that connection permits the action (ABAC)
See Authorization.