4.4 — Multi-Tenant Identity Architecture: B2B vs B2C Patterns
Goal: Architect multi-tenant identity systems that can scale from individual consumer accounts to complex enterprise customers who demand their own SAML/OIDC federation, custom password policies, and strict tenant isolation.
Pros: Total cryptographic and database isolation. Tenant A’s signing keys and configuration never touch Tenant B.
Cons: Operational nightmare at scale. Deploying, migrating, and monitoring 10,000 distinct user pools hits cloud API limits.
2. Shared Pool with Tenant Claims (Discriminator Model)
Pros: Highly scalable, zero per-tenant operational overhead. Simple user lookups.
Cons: Weak isolation. A bug in your API gateway or SQL query (WHERE tenant_id = ?) can leak data across organizations.
3. Identity-of-Identities Broker (The Modern Enterprise Choice)
A single master IdP handles application authentication.
For each enterprise customer, the master IdP registers an external Identity Provider federation mapping to the customer’s corporate IdP.
3. Domain-Based IdP Discovery & Home Realm Discovery (HRD)
In a multi-tenant enterprise app, how do you know which corporate IdP a user should be redirected to?
sequenceDiagram
autonumber
actor User as User
participant App as Web Frontend
participant AuthAPI as Auth Router
participant OktaAcme as Acme Corp Okta
User->>App: Enters email: alice@acme.com
App->>AuthAPI: POST /auth/discover-realm { "email": "alice@acme.com" }
Note over AuthAPI: Extracts domain: "acme.com"<br/>Looks up tenant configuration in DB
AuthAPI-->>App: { "type": "sso", "idp_url": "https://auth.company.com/realms/acme" }
App->>OktaAcme: Redirects user directly to Acme's Okta Login!
Fallback Strategies:
Subdomain Routing:https://acme.cloudnative.wiki routes directly to Acme’s realm.
Organization Slug: Prompts user: “Enter your organization name” before redirecting.
4. Bring Your Own Identity Provider (BYO-IdP) Architecture
Enterprise customers expect to “Bring Your Own IdP” (BYO-IdP). This requires your auth system to support:
Self-Service Federation Portal: Enterprise IT admins upload their SAML metadata.xml or enter OIDC Discovery URLs without opening a support ticket.
Domain Verification: Prove the customer owns acme.com (via a DNS TXT record check) before allowing them to route @acme.com logins.
Attribute Mapping Rules: Allow the customer to map custom corporate claims (memberOf, title) to application roles.
5. Tenant Isolation in Tokens & API Gateways
In a multi-tenant system, every JWT issued to the client must bind the user to their active tenant context:
For complex authorization (e.g. Google Drive folder inheritance), modern multi-tenant architectures use ReBAC models (Zanzibar / OpenFGA / Oso) rather than packing deeply nested permissions into JWT claims.
7. Enterprise Self-Service Federation Onboarding
flowchart TD
Admin[Enterprise Customer Admin] --> Portal[SaaS Admin Portal]
Portal --> Step1[1 · Input Company Email Domain: acme.com]
Step1 --> Step2[2 · Verify Domain via DNS TXT Record]
Step2 --> Step3[3 · Upload SAML Metadata / OIDC Discovery URL]
Step3 --> Step4[4 · Configure Group-to-Role Mappings]
Step4 --> Step5[5 · Test Connection with Test Account]
Step5 --> Step6[6 · Enable SSO Enforcement for all @acme.com Users]
from fastapi import Request, HTTPException, statusasync def tenant_isolation_middleware(request: Request, call_next): # Skip unauthenticated / public routes if request.url.path.startswith("/public") or request.url.path.startswith("/auth"): return await call_next(request) claims = getattr(request.state, "jwt_claims", None) if not claims: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Missing auth context") tenant_context = claims.get("https://cloudnative.wiki/tenant") if not tenant_context or not tenant_context.get("id"): raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="No active tenant context in token") tenant_id = tenant_context["id"] # Prevent URL parameter spoofing: # If the route contains /tenants/{tenant_id}/..., verify it matches the token claim! path_tenant_id = request.path_params.get("tenant_id") if path_tenant_id and path_tenant_id != tenant_id: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Cross-tenant access violation: URL tenant does not match token claim" ) # Attach tenant to request state for downstream DB queries request.state.tenant_id = tenant_id response = await call_next(request) return response
9. Exercises & Verification
Verify Cross-Tenant Isolation: Send an API request with a valid JWT for Tenant A targeting /api/tenants/tenant_b/invoices. Confirm that the gateway returns HTTP 403 Forbidden.
Domain Discovery Test: Write a function that tests domain extraction from email addresses, handling subdomains (user@us.acme.com) and edge cases (user@gmail.com→ redirect to B2C login).
Simulate Domain Takeover: Explain what would happen if a tenant could register gmail.com as their corporate SSO domain. How does DNS verification prevent this?
10. Next Step
Now that multi-tenant architecture patterns are established, which Identity Provider platform should you deploy? We evaluate AWS Cognito, Microsoft Entra ID, Auth0, Okta, Keycloak, and WorkOS next.