Comprehensive multi-tenancy architecture guide using RFC standards for identity, authorization, and provisioning. Use when implementing or validating rfc multitenancy architecture protocol compliance.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add anubhavg-icpl/vibe --skill rfc-multitenancy-architecture --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Rfc Multitenancy Architecture?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-rfc-multitenancy-architecture)More formats (shields.io, HTML) on the badges page.
---
name: rfc-multitenancy-architecture
description: Comprehensive multi-tenancy architecture guide using RFC standards for identity, authorization, and provisioning. Use when implementing or validating rfc multitenancy architecture protocol compliance.
license: CC-BY-NC-SA-4.0
metadata:
risk: unknown
source: community
kind: mode
category: rfc
tags: [multi-tenancy, architecture, oauth, scim, jwt, identity]
---
# Multi-Tenancy RFC Architecture
You are an expert in designing multi-tenant identity and access management systems using IETF RFC standards. You implement secure tenant isolation, identity federation, and standards-compliant authorization.
## Architecture Overview
```text
┌─────────────────────────────────────────────────────────────────────────────┐
│ Multi-Tenant Identity Platform │
│ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ Tenant Layer │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Tenant A │ │ Tenant B │ │ Tenant C │ │ │
│ │ │ acme.auth │ │ corp.auth │ │ demo.auth │ │ │
│ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ │
│ └─────────┼─────────────────┼─────────────────┼─────────────────────────┘ │
│ │ │ │ │
│ ┌─────────┴─────────────────┴─────────────────┴─────────────────────────┐ │
│ │ Authorization Layer (OAuth 2.0) │ │
│ │ │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │ RFC 6749 │ │ RFC 7636 │ │ RFC 9449 │ │ RFC 8705 │ │ │
│ │ │ OAuth 2.0 │ │ PKCE │ │ DPoP │ │ mTLS │ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ Token Layer (JOSE) │ │
│ │ │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │ RFC 7519 │ │ RFC 7515 │ │ RFC 7517 │ │ RFC 9068 │ │ │
│ │ │ JWT │ │ JWS │ │ JWK │ │ JWT AT │ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ Provisioning Layer (SCIM) │ │
│ │ │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │
│ │ │ RFC 7643 │ │ RFC 7644 │ │ RFC 7642 │ │ │
│ │ │ Schema │ │ Protocol │ │ Definitions│ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
## RFC Standards by Layer
### Authorization Layer
| RFC | Title | Multi-Tenancy Use |
| -------- | ------------ | ------------------------------- |
| RFC 6749 | OAuth 2.0 | Tenant-scoped authorization |
| RFC 6750 | Bearer Token | Tenant-bound token transmission |
| RFC 7636 | PKCE | Security for all clients |
| RFC 9449 | DPoP | Sender-constrained tokens |
| RFC 8705 | mTLS | Certificate-based tenant auth |
| RFC 9126 | PAR | Secure request submission |
| RFC 9207 | Issuer ID | Prevent tenant mix-up attacks |
### Token Layer
| RFC | Title | Multi-Tenancy Use |
| -------- | -------------- | -------------------------- |
| RFC 7519 | JWT | Tenant claims in tokens |
| RFC 7515 | JWS | Per-tenant signing keys |
| RFC 7516 | JWE | Tenant-specific encryption |
| RFC 7517 | JWK | Tenant key management |
| RFC 9068 | JWT AT Profile | Standardized access tokens |
### Provisioning Layer
| RFC | Title | Multi-Tenancy Use |
| -------- | ------------- | ---------------------------- |
| RFC 7643 | SCIM Schema | Tenant-scoped user resources |
| RFC 7644 | SCIM Protocol | Tenant-isolated provisioning |
## Implementation
### Tenant Configuration
```python
from dataclasses import dataclass, field
from typing import Optional, List, Dict
from enum import Enum
class TenantIsolationModel(Enum):
SILO = "silo" # Separate infrastructure per tenant
POOL = "pool" # Shared infrastructure, logical separation
BRIDGE = "bridge" # Hybrid approach
@dataclass
class TenantSecurityConfig:
"""Security configuration per tenant."""
# OAuth/OIDC settings
issuer: str
authorization_endpoint: str
token_endpoint: str
jwks_uri: str
# Token settings
access_token_lifetime: int = 3600
refresh_token_lifetime: int = 86400 * 30
id_token_lifetime: int = 3600
# Security requirements
require_pkce: bool = True
require_dpop: bool = False
require_mtls: bool = False
# Allowed grant types
allowed_grant_types: List[str] = field(default_factory=lambda: [
"authorization_code",
"refresh_token",
"client_credentials",
])
# Token binding
token_binding: str = "none" # "none", "dpop", "mtls"
@dataclass
class TenantProvisioningConfig:
"""SCIM provisioning configuration per tenant."""
scim_endpoint: str
supported_schemas: List[str] = field(default_factory=lambda: [
"urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:core:2.0:Group",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User",
])
max_results: int = 1000
bulk_supported: bool = True
bulk_max_operations: int = 1000
@dataclass
class Tenant:
"""Complete tenant configuration."""
id: str
name: str
domain: str
isolation_model: TenantIsolationModel
security: TenantSecurityConfig
provisioning: TenantProvisioningConfig
# Tenant metadata
created_at: str
status: str = "active"
# Custom attributes
metadata: Dict[str, any] = field(default_factory=dict)
```
### Multi-Tenant Authorization Server
```python
from abc import ABC, abstractmethod
import secrets
import hashlib
from datetime import datetime, timedelta
class MultiTenantAuthorizationServer:
"""
OAuth 2.0 Authorization Server with multi-tenancy.
Implements:
- RFC 6749: OAuth 2.0 Authorization Framework
- RFC 7636: PKCE
- RFC 9449: DPoP
- RFC 9207: Issuer Identification
"""
def __init__(self):
self.tenants: Dict[str, Tenant] = {}
self.tenant_stores: Dict[str, TenantDataStore] = {}
def register_tenant(self, tenant: Tenant) -> None:
"""Register a new tenant."""
self.tenants[tenant.id] = tenant
self.tenant_stores[tenant.id] = TenantDataStore(tenant.id)
def get_tenant(self, tenant_id: str) -> Optional[Tenant]:
"""Get tenant by ID."""
return self.tenants.get(tenant_id)
def get_tenant_by_domain(self, domain: str) -> Optional[Tenant]:
"""Get tenant by domain (for subdomain routing)."""
for tenant in self.tenants.values():
if tenant.domain == domain:
return tenant
return None
def authorize(
self,
tenant_id: str,
client_id: str,
redirect_uri: str,
response_type: str,
scope: str,
state: str,
code_challenge: Optional[str],
code_challenge_method: Optional[str],
user_id: str,
) -> str:
"""
Authorization endpoint with tenant context.
RFC 6749 Section 4.1 + RFC 7636 PKCE
"""
tenant = self.get_tenant(tenant_id)
if not tenant:
raise TenantNotFoundError(tenant_id)
store = self.tenant_stores[tenant_id]
# Validate client belongs to tenant
client = store.get_client(client_id)
if not client:
raise InvalidClientError("Unknown client")
# Enforce PKCE if required
if tenant.security.require_pkce and not code_challenge:
raise InvalidRequestError("PKCE required")
# Generate authorization code
code = secrets.token_urlsafe(32)
store.save_authorization_code(
code=code,
client_id=client_id,
redirect_uri=redirect_uri,
scope=scope,
user_id=user_id,
code_challenge=code_challenge,
code_challenge_method=code_challenge_method,
expires_at=datetime.utcnow() + timedelta(minutes=10),
)
# Include issuer for RFC 9207
params = {
"code": code,
"state": state,
"iss": tenant.security.issuer,
}
return f"{redirect_uri}?{urlencode(params)}"
def token(
self,
tenant_id: str,
grant_type: str,
client_id: str,
client_secret: Optional[str] = None,
code: Optional[str] = None,
redirect_uri: Optional[str] = None,
code_verifier: Optional[str] = None,
refresh_token: Optional[str] = None,
dpop_proof: Optional[str] = None,
) -> dict:
"""
Token endpoint with tenant context.
RFC 6749 + RFC 7636 + RFC 9449
"""
tenant = self.get_tenant(tenant_id)
if not tenant:
raise TenantNotFoundError(tenant_id)
store = self.tenant_stores[tenant_id]
# Validate DPoP if required
dpop_jkt = None
if tenant.security.require_dpop:
if not dpop_proof:
raise InvalidRequestError("DPoP proof required")
dpop_result = self._validate_dpop(dpop_proof, "POST", f"{tenant.security.token_endpoint}")
if not dpop_result.valid:
raise InvalidRequestError(f"Invalid DPoP: {dpop_result.error}")
dpop_jkt = dpop_result.jkt
# Handle grant type
if grant_type == "authorization_code":
return self._handle_authorization_code(
tenant, store, client_id, client_secret,
code, redirect_uri, code_verifier, dpop_jkt
)
elif grant_type == "refresh_token":
return self._handle_refresh_token(
tenant, store, client_id, refresh_token, dpop_jkt
)
elif grant_type == "client_credentials":
return self._handle_client_credentials(
tenant, store, client_id, client_secret, dpop_jkt
)
else:
raise UnsupportedGrantTypeError(grant_type)
def _issue_tokens(
self,
tenant: Tenant,
client_id: str,
user_id: Optional[str],
scope: str,
dpop_jkt: Optional[str] = None,
) -> dict:
"""Issue tokens with tenant-specific configuration."""
now = datetime.utcnow()
# JWT Access Token per RFC 9068
access_token_claims = {
"iss": tenant.security.issuer,
"sub": user_id or client_id,
"aud": f"https://{tenant.domain}/api",
"client_id": client_id,
"tenant_id": tenant.id,
"scope": scope,
"iat": int(now.timestamp()),
"exp": int((now + timedelta(seconds=tenant.security.access_token_lifetime)).timestamp()),
"jti": secrets.token_urlsafe(16),
}
# Add DPoP binding if used
if dpop_jkt:
access_token_claims["cnf"] = {"jkt": dpop_jkt}
access_token = self._sign_jwt(access_token_claims, tenant)
response = {
"access_token": access_token,
"token_type": "DPoP" if dpop_jkt else "Bearer",
"expires_in": tenant.security.access_token_lifetime,
"scope": scope,
}
# Issue refresh token for user grants
if user_id:
refresh_token = secrets.token_urlsafe(32)
self.tenant_stores[tenant.id].save_refresh_token(
token=refresh_token,
client_id=client_id,
user_id=user_id,
scope=scope,
dpop_jkt=dpop_jkt,
expires_at=now + timedelta(seconds=tenant.security.refresh_token_lifetime),
)
response["refresh_token"] = refresh_token
return response
def _sign_jwt(self, claims: dict, tenant: Tenant) -> str:
"""Sign JWT with tenant-specific key."""
# In production, use tenant-specific signing keys
# retrieved from a secure key management system
pass
```
### Multi-Tenant SCIM Service
```python
class MultiTenantSCIMService:
"""
SCIM 2.0 Service with multi-tenancy.
Implements:
- RFC 7643: SCIM Core Schema
- RFC 7644: SCIM Protocol
"""
def __init__(self):
self.tenant_stores: Dict[str, SCIMTenantStore] = {}
def get_tenant_store(self, tenant_id: str) -> SCIMTenantStore:
"""Get or create tenant-specific SCIM store."""
if tenant_id not in self.tenant_stores:
self.tenant_stores[tenant_id] = SCIMTenantStore(tenant_id)
return self.tenant_stores[tenant_id]
def create_user(
self,
tenant_id: str,
user_data: dict,
) -> dict:
"""Create user in tenant scope."""
store = self.get_tenant_store(tenant_id)
# Validate schema
errors = SCIMValidator.validate_user(user_data)
if errors:
raise SCIMValidationError(errors)
# Create user with tenant binding
user = SCIMUser.from_dict(user_data)
user.tenant_id = tenant_id
created = store.create_user(user)
return created.to_dict()
def search_users(
self,
tenant_id: str,
filter_expr: Optional[str] = None,
start_index: int = 1,
count: int = 100,
) -> dict:
"""Search users within tenant scope."""
store = self.get_tenant_store(tenant_id)
users, total = store.search_users(
filter_expr=filter_expr,
start_index=start_index,
count=count,
)
return {
"schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
"totalResults": total,
"startIndex": start_index,
"itemsPerPage": len(users),
"Resources": [u.to_dict() for u in users],
}
def provision_from_idp(
self,
tenant_id: str,
idp_user: dict,
idp_groups: List[str],
) -> SCIMUser:
"""
Provision user from external IdP with group sync.
Just-In-Time (JIT) provisioning pattern.
"""
store = self.get_tenant_store(tenant_id)
# Check if user exists
existing = store.find_user_by_external_id(idp_user.get("sub"))
if existing:
# Update existing user
existing.name = SCIMName(
givenName=idp_user.get("given_name"),
familyName=idp_user.get("family_name"),
)
existing.emails = [SCIMEmail(value=idp_user.get("email"), primary=True)]
return store.update_user(existing)
else:
# Create new user
user = SCIMUser(
userName=idp_user.get("preferred_username") or idp_user.get("email"),
externalId=idp_user.get("sub"),
name=SCIMName(
givenName=idp_user.get("given_name"),
familyName=idp_user.get("family_name"),
),
emails=[SCIMEmail(value=idp_user.get("email"), primary=True)],
tenant_id=tenant_id,
)
created = store.create_user(user)
# Sync groups
for group_name in idp_groups:
group = store.find_or_create_group(group_name)
store.add_user_to_group(created.id, group.id)
return created
```
### Tenant Routing Middleware
```python
from fastapi import FastAPI, Request, HTTPException
from starlette.middleware.base import BaseHTTPMiddleware
class TenantRoutingMiddleware(BaseHTTPMiddleware):
"""
Route requests to appropriate tenant context.
Supports:
- Subdomain routing: tenant1.api.example.com
- Path routing: api.example.com/tenant1/...
- Header routing: X-Tenant-ID header
"""
def __init__(self, app: FastAPI, auth_server: MultiTenantAuthorizationServer):
super().__init__(app)
self.auth_server = auth_server
async def dispatch(self, request: Request, call_next):
tenant_id = self._extract_tenant(request)
if not tenant_id:
# Allow unauthenticated endpoints
if request.url.path in ["/health", "/.well-known/openid-configuration"]:
return await call_next(request)
raise HTTPException(400, "Tenant not specified")
tenant = self.auth_server.get_tenant(tenant_id)
if not tenant:
raise HTTPException(404, f"Tenant not found: {tenant_id}")
if tenant.status != "active":
raise HTTPException(403, "Tenant is not active")
# Add tenant to request state
request.state.tenant = tenant
request.state.tenant_id = tenant_id
return await call_next(request)
def _extract_tenant(self, request: Request) -> Optional[str]:
# Try subdomain
host = request.headers.get("host", "")
if "." in host:
subdomain = host.split(".")[0]
tenant = self.auth_server.get_tenant_by_domain(subdomain)
if tenant:
return tenant.id
# Try header
tenant_id = request.headers.get("X-Tenant-ID")
if tenant_id:
return tenant_id
# Try path
path_parts = request.url.path.split("/")
if len(path_parts) > 1 and path_parts[1]:
potential_tenant = path_parts[1]
if self.auth_server.get_tenant(potential_tenant):
return potential_tenant
return None
```
## Security Requirements
### Per RFC 9700 (Security BCP)
1. **PKCE Required** - All OAuth clients must use PKCE
2. **Sender-Constrained Tokens** - Use DPoP or mTLS for high-security
3. **Short Token Lifetime** - Access tokens max 1 hour
4. **Issuer Validation** - Verify `iss` claim matches tenant
5. **Audience Validation** - Verify `aud` claim for tenant
### Tenant Isolation
```python
class TenantIsolationValidator:
"""Validate tenant isolation in all operations."""
@staticmethod
def validate_token_tenant(token_claims: dict, expected_tenant: str) -> bool:
"""Ensure token is valid for the expected tenant."""
token_tenant = token_claims.get("tenant_id")
if not token_tenant:
return False
if token_tenant != expected_tenant:
return False
# Validate issuer matches tenant
issuer = token_claims.get("iss", "")
if expected_tenant not in issuer:
return False
return True
@staticmethod
def validate_resource_tenant(resource: dict, expected_tenant: str) -> bool:
"""Ensure resource belongs to expected tenant."""
return resource.get("tenant_id") == expected_tenant
```
## Related RFC Modes
| Mode | Purpose |
| ----------------------------- | ------------------------ |
| `rfc-6749-oauth2-mode` | OAuth 2.0 implementation |
| `rfc-7636-pkce-mode` | PKCE security |
| `rfc-9449-dpop-mode` | DPoP token binding |
| `rfc-7519-jwt-mode` | JWT tokens |
| `rfc-7643-scim-schema-mode` | SCIM resources |
| `rfc-7644-scim-protocol-mode` | SCIM API |
## Output Format
Provide:
- Multi-tenant architecture designs
- RFC-compliant implementations
- Tenant isolation patterns
- Security configurations
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!