Skip to Content
ConceptsMulti-Tenancy

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

DataIsolation Level
User credentials (password hashes)Per-realm (separate database rows)
User sessionsPer-realm
OAuth clients (applications)Per-realm
Realm-level rolesPer-realm
Identity provider configurationsPer-realm
Authentication flowsPer-realm

Auris Prisma Layer

All Auris-specific data models include a tenantId field that scopes queries:

DataIsolation Mechanism
Roles and permissions (V2 RBAC)tenantId field on Role, Permission, RolePermission
FGA authorization modelstenantId on AuthorizationModel
FGA relationship tuplestenantId on RelationshipTuple
Actions (custom hooks)tenantId on Action
Custom claimsVia applicationId (which belongs to a tenant)
Webhooks and log streamstenantId on LogStream
Organizations (B2B)tenantId on Organization
SCIM connectionstenantId on ScimConnection
Billing subscriptionstenantId on TenantSubscription
Attack protection settingstenantId 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-corp

The 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 automatically

Console 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:

SettingEffect
Company NameShown in the login page title and email templates
LogoDisplayed in the login page header
Primary ColorApplied to buttons, links, and focus indicators
Background ColorLogin page background
FaviconBrowser tab icon

These settings are configured in the Console under SettingsBranding.

How Branding Is Resolved

When the hosted login page loads, Auris resolves branding in this order:

  1. Application-level overrides: If the client_id in the OAuth authorize request maps to an application with custom branding, those values are used.
  2. Tenant-level settings: For any branding field not overridden at the application level, the tenant’s default branding is used.
  3. 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

  1. Add the domain in ConsoleSettingsCustom Domains
  2. Configure a CNAME DNS record pointing to the Auris platform
  3. Auris verifies DNS ownership via a verification token (CNAME or TXT record)
  4. SSL/TLS is provisioned automatically via Let’s Encrypt
  5. 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:

MethodHow It Works
CNAMEAdd a CNAME record for the domain pointing to the Auris platform hostname
TXTAdd 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:

PlanPriceKey Features
Free$0/moBasic auth, up to 100 users, 1 application
Pro$29/moUnlimited users, social login, MFA, webhooks, organizations
Enterprise$99/moSSO (SAML/OIDC), FGA, SCIM provisioning, custom domains, SLA

How Billing Works

  • Each tenant has a TenantSubscription record linking to a Stripe subscription
  • The TenantStripeCustomer record 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

SettingDescription
MFA PolicyWhich MFA methods are enabled (TOTP, SMS, WebAuthn), whether MFA is optional or required
Session PoliciesSliding expiry, absolute expiry, refresh token lifetime
Rate LimitsConfigured at the infrastructure level but applied per-tenant per-IP
IP RulesAllow/block lists with tenant-wide or per-application scope
Brute-Force ProtectionLockout threshold, duration, escalation multiplier
Suspicious Login DetectionWhich detectors are enabled, what action each triggers
CAPTCHAProvider, trigger policy, which pages are protected
Adaptive MFARisk scoring thresholds, custom risk rules
Password PolicyMinimum length, complexity requirements (configured in Keycloak realm)

Tenant-Level vs Application-Level

Some settings can be further scoped to individual applications within a tenant:

ScopeSettings
Tenant-wide onlyMFA policy, session policies, brute-force protection, suspicious login
Tenant or per-applicationIP rules, CAPTCHA, custom claims, social login providers

Creating and Managing Tenants

Creating a Tenant

Tenant creation provisions the full infrastructure stack:

  1. A new Keycloak realm is created with the tenant’s slug as the realm name
  2. Default realm settings are applied (password policy, session timeouts, required actions)
  3. The Auris database records are created (tenant configuration, default roles, security defaults)
  4. A default admin user is created (or an invitation is sent)
  5. Default permission definitions are seeded

Managing Tenants in the Console

Tenant management is available under ConsoleSettingsTenant:

ActionDescription
Edit tenant nameChange the display name (the slug/realm name is immutable after creation)
Configure security defaultsSet default MFA policy, session settings, password requirements
View tenant metadataCreation date, user count, application count, current plan
Delete tenantPermanently 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-tenant header 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_id to 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

ScenarioRecommendation
Single product, single companySingle tenant
SaaS platform with customer organizationsMulti-tenant (one tenant per customer)
Development + staging + production environmentsMulti-tenant (one tenant per environment)
White-label authentication for agenciesMulti-tenant with custom domains
Internal application suiteSingle 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, }, }