Multi-Tenancy
Auris is a multi-tenant identity and access management platform. Every deployment serves one or more tenants, each representing an isolated organizational unit with its own users, applications, roles, permissions, security policies, and branding. This page explains how tenancy works at every layer of the system.
The Auris Tenant Model
A tenant in Auris represents a single organization, company, or environment that uses Auris for authentication and authorization. Each tenant gets:
- Its own set of users (no user is shared across tenants by default)
- Its own applications (each with its own OAuth client credentials)
- Its own roles and permissions (RBAC, FGA models, custom claims)
- Its own security policies (MFA enforcement, session policies, rate limits, IP rules, CAPTCHA)
- Its own branding (logo, colors, company name, custom domain)
- Its own billing subscription (plan, payment method, invoices)
- Its own audit logs (authentication events, admin actions, policy changes)
Tenants are the top-level isolation boundary in Auris. Nothing leaks between tenants.
One Tenant = One Keycloak Realm
Under the hood, each Auris tenant maps to exactly one Keycloak realm. Keycloak is the identity provider that Auris wraps with its higher-level API.
Auris Tenant "acme-corp" ←→ Keycloak Realm "acme-corp"
Auris Tenant "beta-inc" ←→ Keycloak Realm "beta-inc"
Auris Tenant "staging" ←→ Keycloak Realm "staging"This mapping provides several guarantees:
Complete Data Isolation
Keycloak realms are fully isolated at the database level. Users, clients, roles, sessions, and credentials in one realm cannot be accessed from another realm. This is not application-level filtering — it is enforced by Keycloak’s data model.
Separate Authentication Infrastructure
Each realm has its own:
- Login endpoints and session management
- SSO cookies (a user logged into realm A is not logged into realm B)
- Identity provider configurations (social login, SAML, OIDC federations)
- Authentication flows and required actions
- Password policies and credential management
Independent Key Material
Each realm can have its own signing keys for JWTs. This means tokens issued for one tenant cannot be verified against another tenant’s JWKS endpoint, even if the token was somehow intercepted.
Data Isolation in Detail
Data isolation operates at multiple layers:
Keycloak Layer
| Data | Isolation Level |
|---|---|
| User credentials (password hashes) | Per-realm (separate database rows) |
| User sessions | Per-realm |
| OAuth clients (applications) | Per-realm |
| Realm-level roles | Per-realm |
| Identity provider configurations | Per-realm |
| Authentication flows | Per-realm |
Auris Prisma Layer
All Auris-specific data models include a tenantId field that scopes queries:
| Data | Isolation Mechanism |
|---|---|
| Roles and permissions (V2 RBAC) | tenantId field on Role, Permission, RolePermission |
| FGA authorization models | tenantId on AuthorizationModel |
| FGA relationship tuples | tenantId on RelationshipTuple |
| Actions (custom hooks) | tenantId on Action |
| Custom claims | Via applicationId (which belongs to a tenant) |
| Webhooks and log streams | tenantId on LogStream |
| Organizations (B2B) | tenantId on Organization |
| SCIM connections | tenantId on ScimConnection |
| Billing subscriptions | tenantId on TenantSubscription |
| Attack protection settings | tenantId on AttackProtectionSetting, IpRule, etc. |
Every server-side service method in Auris accepts or derives tenantId from the authenticated request context and uses it to scope database queries. There are no cross-tenant queries in the application code.
API Layer
The API enforces tenant isolation at the request boundary. Every authenticated request must include a valid tenant identifier (via the x-tenant header or derived from the access token). If the authenticated user does not belong to the requested tenant, the API returns 403 Forbidden.
Tenant Identification
Applications and API clients identify which tenant they are operating against using the x-tenant HTTP header.
The x-tenant Header
GET /api/users
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
x-tenant: acme-corpThe x-tenant header value is the tenant’s slug — a URL-safe identifier set during tenant creation. It maps directly to the Keycloak realm name.
How the SDKs Handle It
The Auris SDKs automatically include the x-tenant header on every request based on the configured domain or tenant ID:
// @auris/js
const auris = new AurisClient({
domain: 'acme-corp.auris.example.com',
clientId: 'your-client-id',
})
// SDK extracts tenant from domain and sends x-tenant header automaticallyConsole API Client
The Auris Console’s internal API client (api-client.ts) sends the x-tenant header on every request, derived from the logged-in admin’s tenant context:
// Simplified from apps/console/lib/api-client.ts
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
'x-tenant': currentTenant,
}Hosted Login and Tenant-Specific Branding
When a user is redirected to the Auris Hosted Login page, the page is branded according to the tenant’s configuration. Each tenant can customize:
| Setting | Effect |
|---|---|
| Company Name | Shown in the login page title and email templates |
| Logo | Displayed in the login page header |
| Primary Color | Applied to buttons, links, and focus indicators |
| Background Color | Login page background |
| Favicon | Browser tab icon |
These settings are configured in the Console under Settings → Branding.
How Branding Is Resolved
When the hosted login page loads, Auris resolves branding in this order:
- Application-level overrides: If the
client_idin the OAuth authorize request maps to an application with custom branding, those values are used. - Tenant-level settings: For any branding field not overridden at the application level, the tenant’s default branding is used.
- Platform defaults: If neither application nor tenant has configured a setting, the Auris platform defaults are used.
This means a single tenant can have different-looking login pages for different applications while sharing a consistent base brand.
Custom Domains Per Tenant
Each tenant can configure one or more custom domains to white-label the authentication experience. With a custom domain, the hosted login page, OIDC Discovery document, JWKS endpoint, and all email links use the tenant’s domain instead of the Auris platform domain.
Without custom domain: https://auris.example.com/hosted/login?client_id=...
With custom domain: https://auth.acme-corp.com/hosted/login?client_id=...Setting Up Custom Domains
- Add the domain in Console → Settings → Custom Domains
- Configure a CNAME DNS record pointing to the Auris platform
- Auris verifies DNS ownership via a verification token (CNAME or TXT record)
- SSL/TLS is provisioned automatically via Let’s Encrypt
- Set the domain as primary to use it for all hosted pages and email links
Multiple domains can be registered per tenant (for example, auth.acme-corp.com for production and auth-staging.acme-corp.com for staging), but only one is primary at a time.
Domain Verification
Auris supports two verification methods:
| Method | How It Works |
|---|---|
| CNAME | Add a CNAME record for the domain pointing to the Auris platform hostname |
| TXT | Add a TXT record with a verification token at the domain |
CNAME is recommended because it also handles routing. TXT is available for cases where CNAME records are not possible (for example, apex domains).
Billing Per Tenant
Each tenant has its own billing subscription managed through Stripe Billing integration.
Subscription Plans
Auris offers tiered plans with different feature sets:
| Plan | Price | Key Features |
|---|---|---|
| Free | $0/mo | Basic auth, up to 100 users, 1 application |
| Pro | $29/mo | Unlimited users, social login, MFA, webhooks, organizations |
| Enterprise | $99/mo | SSO (SAML/OIDC), FGA, SCIM provisioning, custom domains, SLA |
How Billing Works
- Each tenant has a
TenantSubscriptionrecord linking to a Stripe subscription - The
TenantStripeCustomerrecord maps the tenant to a Stripe customer ID - Plan changes (upgrade/downgrade) are handled via Stripe Checkout sessions
- Cancellations take effect at the end of the current billing period (
cancelAtPeriodEnd) - Stripe webhooks update the subscription status in real time
Feature Gating
Features are gated based on the tenant’s active plan. The feature-gate.ts module provides:
// Server-side feature check
const allowed = await isFeatureAllowed(tenantId, 'fga')
if (!allowed) {
return Response.json(
{ ok: false, error: { code: 'PLAN_LIMIT', message: 'FGA requires Enterprise plan' } },
{ status: 403 }
)
}Feature gate checks use an in-memory cache with a 5-minute TTL to avoid hitting the database on every request.
Security Settings Per Tenant
Every security feature in Auris is scoped to the tenant level. Different tenants can have completely different security postures:
Per-Tenant Security Controls
| Setting | Description |
|---|---|
| MFA Policy | Which MFA methods are enabled (TOTP, SMS, WebAuthn), whether MFA is optional or required |
| Session Policies | Sliding expiry, absolute expiry, refresh token lifetime |
| Rate Limits | Configured at the infrastructure level but applied per-tenant per-IP |
| IP Rules | Allow/block lists with tenant-wide or per-application scope |
| Brute-Force Protection | Lockout threshold, duration, escalation multiplier |
| Suspicious Login Detection | Which detectors are enabled, what action each triggers |
| CAPTCHA | Provider, trigger policy, which pages are protected |
| Adaptive MFA | Risk scoring thresholds, custom risk rules |
| Password Policy | Minimum length, complexity requirements (configured in Keycloak realm) |
Tenant-Level vs Application-Level
Some settings can be further scoped to individual applications within a tenant:
| Scope | Settings |
|---|---|
| Tenant-wide only | MFA policy, session policies, brute-force protection, suspicious login |
| Tenant or per-application | IP rules, CAPTCHA, custom claims, social login providers |
Creating and Managing Tenants
Creating a Tenant
Tenant creation provisions the full infrastructure stack:
- A new Keycloak realm is created with the tenant’s slug as the realm name
- Default realm settings are applied (password policy, session timeouts, required actions)
- The Auris database records are created (tenant configuration, default roles, security defaults)
- A default admin user is created (or an invitation is sent)
- Default permission definitions are seeded
Managing Tenants in the Console
Tenant management is available under Console → Settings → Tenant:
| Action | Description |
|---|---|
| Edit tenant name | Change the display name (the slug/realm name is immutable after creation) |
| Configure security defaults | Set default MFA policy, session settings, password requirements |
| View tenant metadata | Creation date, user count, application count, current plan |
| Delete tenant | Permanently removes the tenant, all users, all data (requires confirmation) |
Deleting a tenant is irreversible. All users, applications, sessions, roles, permissions, FGA models, tuples, audit logs, and billing history for the tenant are permanently removed. The associated Keycloak realm is also deleted.
Default Tenant vs Multi-Tenant Deployments
Auris supports two deployment modes:
Single-Tenant (Default)
For many deployments, a single tenant is sufficient. The dashboard and website use Auris with a single 'altovar' tenant. In this mode:
- All users belong to the same tenant
- The
x-tenantheader is always the same value - Tenant selection UI is unnecessary
- The deployment is simpler to operate and reason about
Multi-Tenant
Multi-tenant deployments serve multiple organizations from a single Auris instance. In this mode:
- Each organization gets its own tenant with isolated data
- The hosted login page uses the tenant context from the
client_idto resolve branding - Admin console users can manage their own tenant only
- Platform-level administration (creating new tenants, viewing across tenants) requires platform admin access
- Billing, security, and branding are fully independent per tenant
Choosing the Right Mode
| Scenario | Recommendation |
|---|---|
| Single product, single company | Single tenant |
| SaaS platform with customer organizations | Multi-tenant (one tenant per customer) |
| Development + staging + production environments | Multi-tenant (one tenant per environment) |
| White-label authentication for agencies | Multi-tenant with custom domains |
| Internal application suite | Single tenant with multiple applications |
Tenant Data in JWTs
The access token issued by Auris does not contain a tenantId claim by default. Instead, the iss (issuer) claim encodes the tenant context:
{
"iss": "https://auth.acme-corp.com",
"sub": "usr_abc123",
"aud": "your-client-id"
}If you need the tenant ID explicitly in the token (for multi-tenant resource servers), add it as a Custom Claim or use an Action to inject it:
// Post-Login Action: add tenant to token claims
return {
allow: true,
claims: {
tenant_id: context.tenant.id,
tenant_name: context.tenant.name,
},
}Related Concepts
- OAuth 2.0 & OIDC — The protocol layer that tenant-specific tokens operate within
- Branding & Customization — Configure tenant-specific branding
- Custom Domains — White-label authentication under your domain
- Billing & Plans — Manage tenant subscriptions
- Organizations (B2B) — Multi-org within a single tenant
- Enterprise SSO — Per-organization SAML/OIDC federations