Skip to main content
Version: Latest (v0.0.1)

Multi-Tenancy

O2ID is multi-tenant: one instance hosts many isolated tenants, each its own identity universe — its own users, applications, roles, resource servers, and OIDC issuer. Tenants share nothing; a user in one tenant is invisible to every other.

Tenant-scoped URLs

Every endpoint is served under a tenant path prefix: /t/{tenant}/…, where {tenant} is the tenant's slug. For example, the OpenID discovery document for the acme tenant is at:

GET /t/acme/.well-known/openid-configuration

and it reports a per-tenant issuer:

{
"issuer": "https://id.example.com/t/acme",
"authorization_endpoint": "https://id.example.com/t/acme/oauth2/authorize",
"token_endpoint": "https://id.example.com/t/acme/oauth2/token"
}

The health check (/health) and the portal's static assets (/portal/static/…) are the only global, un-prefixed paths.

The system tenant

On first run, o2id serve seeds a tenant named system and creates the platform admin inside it. The system tenant holds platform-level identities — the operators who manage the deployment itself. o2idctl targets system by default.

Tenant-scoped identities

A user belongs to exactly one tenant. There is no cross-tenant membership: an operator who administers several tenants has a separate account in each. Email uniqueness is per-tenant, so alice@example.com can exist independently in two different tenants.

Platform vs tenant administration

Two distinct levels of authority:

  • Platform admin — holds the platform scope namespace (tenants:*) and can create and manage tenants across the deployment. Seeded into the system tenant on first run.
  • Tenant admin — holds * (every scope) within a single tenant. Can fully administer that tenant's users, apps, and roles, but has no cross-tenant authority.

Crucially, a tenant's * wildcard does not include the platform tenants:* scopes — so being a tenant admin never confers the ability to touch other tenants. Platform authority is granted explicitly, only by an existing platform operator.

Tenant settings

Each tenant has its own settings — the pushed-authorization-request request_uri TTL, the webhook SSRF allowlist for flow webhook nodes, and which log levels get captured — read and updated with:

o2idctl tenant-settings get
o2idctl tenant-settings update --par-request-uri-lifetime 90s

A tenant's settings are its own defaults only — they have no effect on any other tenant. Token, session, and transaction lifetimes are configured per application instead — see Application settings.

Creating tenants

o2idctl tenants create --slug acme --name "Acme, Inc."
o2idctl tenants get <id>
o2idctl tenants list

Requires the tenants:create (or tenants:read for get/list) platform scope. A newly created tenant gets its own OIDC signing key immediately — every tenant, at any depth, has its own effective key, either its own or an inherited one (see Organizations). Pass --admin-email/--admin-password to seed a first admin (every scope, within the new tenant only) in the same step:

o2idctl tenants create --slug acme --name "Acme, Inc." \
--admin-email admin@acme.example --admin-password Str0ngPassw0rd9

Organizations

A tenant can host its own customer accounts — Organizations — nested underneath it. See Organizations for creating, listing, and reasoning about them; from the outside an Organization behaves like a lightweight tenant of its own (own users, own roles, own optional signing key and settings), scoped entirely within its parent tenant.

Current status

Pass --admin-email/--admin-password to o2idctl tenants create to seed a first admin alongside a new tenant; without them, a freshly created tenant starts with no users, and its first user has to be created separately via o2idctl users create once a role exists to assign them. A freshly created Organization never seeds its own admin this way — see Organizations — Delegated administration for how it gets its first user instead. Delegated Organization self-service (an Organization's own admin managing its own members/roles without touching its parent tenant's credentials) is not yet built — see Organizations' current status for what's next.