Skip to content

feat: add Multiple Custom Domains (MCD) support and fix JWT verification#71

Open
kishore7snehil wants to merge 8 commits intomainfrom
feat/mcd-support
Open

feat: add Multiple Custom Domains (MCD) support and fix JWT verification#71
kishore7snehil wants to merge 8 commits intomainfrom
feat/mcd-support

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
from unittest.mock import ANY, AsyncMock, MagicMock, patch
from urllib.parse import parse_qs, urlparse

import jwt

Check notice

Code scanning / CodeQL

Unused import Note test

Import of 'jwt' is not used.

Copilot Autofix

AI about 2 hours ago

To fix an unused import, you remove the corresponding import statement so that the test module no longer declares a dependency it does not use. This improves readability and satisfies the static analysis rule without changing runtime behavior.

Concretely, in src/auth0_server_python/tests/test_server_client.py, delete the line import jwt (line 6). No other changes are required, and no additional methods, imports, or definitions are needed. All remaining imports are left untouched to avoid impacting existing functionality.

Suggested changeset 1
src/auth0_server_python/tests/test_server_client.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/auth0_server_python/tests/test_server_client.py b/src/auth0_server_python/tests/test_server_client.py
--- a/src/auth0_server_python/tests/test_server_client.py
+++ b/src/auth0_server_python/tests/test_server_client.py
@@ -3,7 +3,6 @@
 from unittest.mock import ANY, AsyncMock, MagicMock, patch
 from urllib.parse import parse_qs, urlparse
 
-import jwt
 import pytest
 from auth0_server_python.auth_server.my_account_client import MyAccountClient
 from auth0_server_python.auth_server.server_client import ServerClient
EOF
@@ -3,7 +3,6 @@
from unittest.mock import ANY, AsyncMock, MagicMock, patch
from urllib.parse import parse_qs, urlparse

import jwt
import pytest
from auth0_server_python.auth_server.my_account_client import MyAccountClient
from auth0_server_python.auth_server.server_client import ServerClient
Copilot is powered by AI and may make mistakes. Always verify output.
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.

1 participant