Skip to content

Multi-tenancy ​

Authara isolates many companies/products in a single deployment. This is the backbone of using one install across all your products.

The model ​

A tenant is a fully isolated identity space. Each tenant has:

  • Its own Postgres role and row-level security (RLS) — a tenant's queries physically cannot read another tenant's rows. Isolation is enforced at the database.
  • Its own OIDC issuer and signing keys — a token minted for tenant A is rejected by tenant B.
  • Its own Management API (scoped resource indicator).
  • Its own sign-in experience, branding, connectors, users, roles, organizations, webhooks, SSO, and audit logs.

There's a special admin tenant (the control plane) plus a default tenant; real customer tenants sit alongside them, each isolated.

Tenants vs. organizations

Tenants are hard isolation (own DB role + RLS + issuer + console) — use them to separate companies/products. Organizations are lightweight groups inside a single tenant (members/roles/ branding, no separate console) — use them for B2B sub-groups within one tenant, not to separate customers.

Why it's safe to host many products ​

At request time Authara resolves the tenant from the host/path, connects to Postgres as that tenant's role, and every query is confined by RLS to that tenant. Caches and connection pools are per-tenant. Hosting many products on one install does not risk cross-tenant data leakage — the isolation is structural.

Operator rule

Never run tenant business queries on the shared/owner DB pool (it bypasses RLS). The shared pool is only for control-plane operations (creating roles/tenants).

Creating a tenant ​

Today, tenant provisioning is done through the CLI seed path (createTenant()), which creates the tenant's Postgres role, the tenants row, and seeds it.

Runtime provisioning API is planned

A runtime POST/GET/DELETE /api/tenants control plane (create tenant → invite tenant-admin → scoped console) is on the roadmap, along with per-tenant console serving and cross-tenant Management-API authorization. Until then, a super-admin provisions tenants through the seed/CLI path.

Tenant administration ​

  • Each tenant is administered through the admin console, scoped to that tenant.
  • Who can administer a tenant is modeled by an organization in the admin tenant named t-{tenantId} with roles Admin and Collaborator. A user is authorized into a tenant by their membership + role.
  • Keep super-admin (provisions tenants, runs the control plane) and tenant-admin (manages only their own tenant) as distinct scopes.

Custom domains (planned) ​

The engine already maps host → tenant. To give each tenant its own host (login.customer.com), use subdomain-based multi-tenancy (ENDPOINT=https://*.host) and provision TLS/DNS at your proxy. Reserve admin/default and the admin host so a customer can't shadow the control plane.

Best practices ​

  • One product = one tenant so each product gets a full, isolated console.
  • Verify isolation after provisioning: two tenants, each admin sees only their own data; confirm cross-tenant reads are blocked at the DB (they are, via RLS).
  • Least privilege at the DB: the runtime app should not own the tables; consider FORCE ROW LEVEL SECURITY.
  • Audit-log control-plane actions (tenant create/suspend/delete, admin invites).

Built on Logto (MPL-2.0). Released under the Mozilla Public License 2.0.