Back to skills
SKILL.md
Rfc Multitenancy Architecture
ASecurityComprehensive multi-tenancy architecture guide using RFC standards for identity, authorization, and provisioning. Use when implementing or validating rfc multitenancy architecture protocol compliance.
- 9 stars
- 0 votes
- 0 copies
- 4 views
- Added September 8, 2026
Works with
Security analysis
100/100npx -y skills add anubhavg-icpl/vibe --skill rfc-multitenancy-architecture --agent claude-codeAre you the author of Rfc Multitenancy Architecture?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-rfc-multitenancy-architecture)---
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
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.