Skip to content

Conversation

@kishore7snehil
Copy link
Contributor

@kishore7snehil kishore7snehil commented Feb 2, 2026

Multiple Custom Domains (MCD) Support with JWT Verification

🎯 Overview

This PR adds Multiple Custom Domains (MCD) support to auth0-server-python, enabling applications to serve multiple tenants from different hostnames, each mapping to a separate Auth0 tenant. Additionally, this PR includes critical security fixes for JWT verification and token refresh in MCD scenarios.

✨ Features

1. Multiple Custom Domains Support

  • Dynamic Domain Resolution: Accept Callable as domain parameter for runtime resolution
  • Type-Safe Context: New DomainResolverContext with request_url and request_headers
  • Backward Compatible: Static string domains continue to work unchanged
  • Framework-Agnostic: Works with FastAPI, Flask, Django, or any Python framework

Example:

async def domain_resolver(context: DomainResolverContext) -> str:
    host = context.request_headers.get('host', '').split(':')[0]
    return DOMAIN_MAP.get(host, DEFAULT_DOMAIN)

client = ServerClient(domain=domain_resolver, ...)  # MCD enabled

2. OIDC Metadata & JWKS Caching

  • Per-Domain Caching: Separate cache entries for each Auth0 domain
  • 1-Hour TTL: Configurable cache lifetime with automatic expiration
  • Smart Eviction: FIFO eviction when max 100 domains reached
  • Performance: Reduces API calls by ~99% for frequently used domains

3. JWT Signature Verification with Issuer Validation

  • JWKS-Based Verification: Proper signature validation using PyJWT
  • Issuer Matching: Validates token iss claim matches origin domain
  • Key Extraction: Correctly extracts signing key from JWKS using kid
  • Security: Prevents token substitution and cross-tenant replay attacks

4. Domain-Specific Session Management

  • Automatic Isolation: Sessions bound to their origin domain
  • Cross-Domain Protection: Sessions from domain A rejected on domain B
  • Token Refresh Fix: Uses session's stored domain (not current request domain)
  • Migration Support: Sessions coexist during domain migrations

🔄 Compatibility

✅ 100% Backward Compatible - No breaking changes for existing users.

Existing Usage (Unchanged)

# Static domain - works exactly as before
client = ServerClient(domain="tenant.auth0.com", ...)

New Usage (Optional)

# Dynamic MCD - new opt-in feature
async def domain_resolver(context: DomainResolverContext) -> str:
    return "tenant.auth0.com"

client = ServerClient(domain=domain_resolver, ...)

What Changed Internally

  • JWT verification now correctly extracts signing key from JWKS (bug fix)
  • Token refresh uses session domain instead of request domain (bug fix)
  • Domain parameter now accepts callables for dynamic resolution (new feature)

Impact: Existing applications require zero code changes to upgrade.

🔒 Security Enhancements

  1. Domain Isolation - Prevents session hijacking across tenants
  2. Issuer Validation - Validates all ID tokens against origin issuer
  3. JWKS Per Domain - Each tenant uses correct signing keys
  4. Session Domain Binding - Token refresh uses correct Auth0 tenant

📊 Testing

  • Test Coverage: 88 passing tests
  • New Tests: 30+ tests for MCD scenarios in test_server_client.py
  • Integration Test: Live MCD test verified with real Auth0 tenants
  • Coverage: 81% overall, 100% for critical paths

Test Scenarios Covered:

  • ✅ Domain resolution and validation
  • ✅ Session isolation across domains
  • ✅ Token refresh with session domain
  • ✅ Domain-specific logout
  • ✅ Issuer validation (success and failure)
  • ✅ OIDC/JWKS caching
  • ✅ Migration scenarios

📚 Documentation

@kishore7snehil kishore7snehil requested a review from a team as a code owner February 2, 2026 18:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants