Skip to main content
Version: Next

SSO multi-tenancy

The SSO Bridge uses one issuer and signing-key set. Accounts, passwords, MFA and login sessions are global. Tenant Management owns tenants, memberships and service grants. SSO stores authorization snapshots and uses them to issue tenant- and audience-bound tokens.

Managed setup​

SSO always uses managed tenant authorization. It requires provisioned tenant configuration, an active tenant lifecycle and fresh authorization snapshots. There is no legacy tenancy mode.

tenancy:
serviceInstanceId: sso-1
platformUserIds:
- 11111111-1111-4111-8111-111111111111
trustedBrowserClients: [tenant-management-browser, wallet-browser, control-plane-browser]
provisioningIdentities:
- principal:
issuer: https://sso.example.org
subject: tenant-manager
audience: https://sso.example.org
serviceInstanceId: sso-1
operations: [configuration, status, authorization, lifecycle, account]

Use stable SESSION_SECRET and TENANCY_SECRET values across replicas and a shared database for sessions, OAuth state and signing keys. For HTTPS cookies, the trusted ingress must overwrite X-Forwarded-Proto. See configuration for all settings.

Clients and administration​

Each client explicitly declares its kind. Omitting grants uses the defaults below; an explicit list overrides them.

Client kindAuthorityDefault grants
sharedPlatform-controlled browser applicationauthorization_code, refresh_token
workloadOne canonical tenantclient_credentials
platformOperator-controlled serviceclient_credentials

Missing or legacy client kinds are rejected. Register exact redirect URIs; regular-expression redirects are no longer supported.

In the SSO UI, account/platform context contains global settings and tenant membership inspection. Selecting a tenant scopes /api/clients operations to that tenant. Owners and admins need the corresponding client permissions; delegated permissions must stay within their receiving-service grants. Tenant administrators cannot change global accounts, shared/platform clients, client IDs or operator-owned Kubernetes secrets. Tenant-created client IDs must begin with tenant:<canonical-tenant-id>:; the UI generates one automatically. Tenant Management cannot use that reserved namespace, and tenant administrators cannot modify or delete its provisioned clients. Client lists omit raw secrets, and policy or credential changes revoke existing client tokens.

Global account administration at /api/platform/users requires platform authority. Human administrators are registered in tenancy.platformUserIds; service administrators need a matching operator-controlled platform client registration. Tenant roles do not grant platform authority. Account self-service remains global, and removing a membership preserves the account.

Tenant Management can provision human accounts through the separately registered account provisioning operation. A new account has no platform permissions and must complete first-login setup before it can authenticate. Setup links expire, work once and are replaced when a new link is issued. Organization administrators can issue setup links only for pending accounts originally provisioned through their organization. They cannot reset an established shared login account. Password resets, authentication policy changes and email changes invalidate outstanding setup links.

Browser login​

tenancy.trustedBrowserClients explicitly allowlists first-party shared clients that may reuse an authenticated SSO session without a continuation screen. The client still needs its exact registered callback, PKCE and receiving audience. Other registered clients retain interactive authorization. prompt=none returns an OAuth error when a session or a required choice is missing; it does not bypass MFA, account setup or tenant authorization.

prompt=login requires fresh authentication for that authorization request. Password login records completion only after any required second factor; an existing session alone cannot complete the request. A combined login consent request waits for confirmation after authentication.

The application owns organization selection. SSO validates an explicit tenant. If the tenant is omitted, SSO uses a valid remembered selection for the current account, client and audience, or the first eligible organization. An invalid explicit tenant never falls back to a different tenant. Service callbacks retain a validated same-origin return path, and each service keeps its active organization per tab. Switching organizations reuses a valid retained authorization or completes OAuth automatically when the SSO session permits it.

Provisioning​

Bootstrap the manager as a confidential platform client with the SSO audience. Every provisioning request needs a verified bearer token matching the exact principal, audience, service instance and allowed operation in tenancy.provisioningIdentities.

EndpointPurpose
PUT /api/internal/tenants/:tenantId/configurationApply tenant configuration and workload credentials
GET /api/internal/tenants/:tenantId/statusRead the applied revision and public outputs
PUT /api/internal/tenants/:tenantId/authorizationApply a receiving-service authorization snapshot
PUT /api/internal/tenants/:tenantId/lifecycleActivate, suspend or delete a tenant

Configuration registers SSO's audience and any target-service audiences. Provisioned workload clients require the canonical tenant, registered audiences and permission ceilings. Configuration and lifecycle have separate revisions. Replaying identical revision/content is idempotent; stale revisions or changed content at the same revision conflict. Slugs are immutable and never recycled. Status includes appliedLifecycleRevision; deleting and deleted tenants are not ready. Provisioning and suspended tenants can report ready once configuration is applied. Lifecycle checks continue to deny access until the tenant is active.

Send one complete authorization snapshot per registered target service. It contains memberships, service grants and principal mappings. Identity is the exact (issuer, subject) pair, never an email or username. Local subjects belong to the target service; an SSO-targeted mapping for an SSO account must use that account's login ID.

Tokens and expiry​

Human authorization requests select tenant_id and one resource audience. Workloads use their fixed tenant. Browser flows require S256 PKCE. All browser clients use exact registered redirects. Access tokens carry the canonical tenant, receiving audience, actor_type of human or service, authorization revision and permissions bounded by both the service grant and client policy. ID tokens target the browser client and carry no API permissions.

offline_access requests refresh when the account and client allow it. Codes and refresh tokens are single-use. Refresh rechecks current authorization, can narrow permissions, and cannot change the client, tenant or audience. Refresh replay revokes the token family.

Snapshots expire at their manager-supplied validUntil, capped at 60 seconds. The manager must renew them before expiry. Tenant operations fail closed for stale snapshots, revoked memberships, inactive tenants or mismatched token revisions. A global login session does not bypass these checks.

Upgrading​

The migration preserves account/client IDs and credentials, adds unique client IDs, and revokes pre-upgrade tokens. Resolve duplicate client IDs before upgrading; users must authenticate again. Existing clients require an explicit reviewed kind and grant policy before they can issue tokens. Update existing registrations explicitly; startup defaults do not infer authority from old permissions.

Setup links issued before authentication-version binding are invalid after this upgrade. Issue a replacement from the pending account in Tenant Management. No database migration or account reset is needed for this link change.