Multi-tenancy & Access Control
How tenant isolation, OAuth scopes, and RBAC compose on every request.
Rekamedika is one API serving many hospitals (tenants). Isolation is enforced in layers, and none of them can be chosen by the request:
Identity — a verified better-auth session cookie or an OAuth Bearer
token. Unauthenticated requests to tenant data fail with 401.
Tenant — read from the authenticated user's row on the server. Every
tRPC resolver runs inside a transaction that sets the Postgres RLS GUC
app.tenant_id and additionally filters where tenant_id = … (defense in
depth). One deliberate exception: a superadmin session may select a
tenant with the x-tenant-code header; OAuth Bearer callers are always
pinned to their user's tenant regardless of role.
Scope (Bearer callers only) — simrs:read for reads, simrs:write for
writes. Missing scope ⇒ 403.
RBAC — mutations require one of the roles the domain declares
(requireRoles), read from the server-side user_roles table. A broad OAuth
scope never widens roles.
OAuth scopes
| Scope | Meaning |
|---|---|
openid · profile · email | OIDC identity claims |
offline_access | Refresh tokens |
simrs:read | tRPC queries, SATUSEHAT GETs, read MCP tools |
simrs:write | tRPC mutations, SATUSEHAT POST/PATCH, write MCP tools |
What this means for examples
All examples in these docs use placeholders (YOUR_ACCESS_TOKEN,
you@your-hospital.example) — there is no demo tenant credential, and no
real tenant data ever appears in generated pages.
Is this page helpful?