feat(MCD): implement multiple custom domains support#368
Open
developerkunal wants to merge 8 commits intomasterfrom
Open
feat(MCD): implement multiple custom domains support#368developerkunal wants to merge 8 commits intomasterfrom
developerkunal wants to merge 8 commits intomasterfrom
Conversation
Implement complete multi-issuer/multi-tenant support for applications with multiple custom domains: MultiIssuerProvider: - Automatic JWKS routing per issuer from request context - Lazy provider creation with double-checked locking pattern - Thread-safe concurrent access across multiple issuers - LRU eviction with WithMaxProviders() for memory control - Custom cache support with WithMultiIssuerCache() (Redis, etc.) Validator Enhancements: - WithIssuers() for static multiple issuer lists - WithIssuersResolver() for dynamic issuer resolution - Issuer context management for provider routing JWKS Package: - Consolidated option.go for all provider options - MultiIssuerProvider with configurable TTL and HTTP client - Per-issuer provider caching with LRU eviction - Redis cache support for 100+ tenant scenarios Examples: - http-multi-issuer-example: Static issuer configuration - http-dynamic-issuer-example: Dynamic issuer resolution - http-multi-issuer-redis-example: Large-scale Redis + LRU Testing & CI: - 96.8% code coverage maintained - Integration tests for all examples - CI workflow automation for example validation Documentation: - Complete multi-tenant guide in README - Scaling recommendations for different tenant counts - Performance benchmarks and best practices - Enhanced package documentation
There was a problem hiding this comment.
Pull request overview
This PR implements multi-issuer/multi-tenant support for applications that need to validate JWTs from multiple OIDC issuers (Multiple Custom Domains).
Changes:
- Adds
MultiIssuerProviderfor automatic per-issuer JWKS routing with lazy provider creation, LRU eviction, and custom cache support - Migrates from go-jose/go-jose.v2 to lestrrat-go/jwx/v3 for JWKS handling
- Introduces DPoP (Demonstrating Proof-of-Possession) support with trusted proxy configuration and multiple operation modes
Reviewed changes
Copilot reviewed 80 out of 112 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| jwks/provider.go | Refactored JWKS provider to use jwx library, added caching layer with background refresh |
| jwks/option.go | New options for provider configuration supporting multi-issuer and caching customization |
| jwks/multi_issuer_provider_test.go | Comprehensive tests for multi-issuer provider including concurrency and LRU eviction |
| jwks/multi_issuer_provider.go | Core implementation of multi-issuer provider with double-checked locking and LRU cache |
| jwks/doc.go | Package documentation explaining provider types, usage patterns, and performance guidance |
| internal/oidc/oidc.go | Minor change to defer function for response body close |
| internal/oidc/doc.go | New documentation for OIDC discovery functionality |
| go.mod | Updated to v3 module path and migrated to jwx library |
| extractor_test.go | Enhanced tests for token extraction including DPoP scheme support |
| extractor.go | Extended token extractor to support DPoP scheme and return scheme information |
| examples/* | Multiple new examples demonstrating multi-issuer, dynamic issuer resolution, Redis caching, DPoP modes, and trusted proxy configuration |
| Makefile | Added test-examples target and updated golangci-lint version |
Comments suppressed due to low confidence (1)
jwks/provider.go:1
- The comment mentions 'single-flight fetching' behavior, but this is implemented through the
fetchMumutex which blocks all concurrent fetches rather than allowing one to proceed while others wait for the result. This is not true single-flight behavior (like sync.Singleflight). Consider clarifying the comment to say 'serialized fetching' or 'mutex-protected fetching' to accurately describe the implementation.
package jwks
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #368 +/- ##
==========================================
- Coverage 96.55% 96.35% -0.20%
==========================================
Files 18 20 +2
Lines 1508 1727 +219
==========================================
+ Hits 1456 1664 +208
- Misses 34 41 +7
- Partials 18 22 +4 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
0d24351 to
2092669
Compare
3e3e3c3 to
a8a4c83
Compare
- Adjust timing assertion to accept 3-4 requests (timing variance) - Increase TTL from 2s to 5s to prevent cache expiry during test - Fixes CI failures in background refresh scenarios
a8a4c83 to
e5877dd
Compare
…idation - Merged GetWellKnownEndpointsWithIssuerValidation into GetWellKnownEndpointsFromIssuerURL - Added expectedIssuer parameter to perform double-validation by default - Issuer validation now mandatory (defense-in-depth for MCD security) - Updated all callers in Provider and CachingProvider - Updated all test mocks to include issuer field in OIDC metadata - Simplified internal API from 2 functions to 1 function - All tests passing (internal/oidc + jwks)
- Bump from v0.10.3 to v0.10.5 in http-dpop-trusted-proxy, http-jwks-example, and iris-example. - Update from v3.0.1 to v3.0.3 in http-dpop-trusted-proxy, http-jwks-example, iris-example, and dynamic-issuer-example. - Upgrade from v1.6.4 to v1.6.7 in http-dpop-trusted-proxy, http-jwks-example, and iris-example. - Upgrade from v0.45.0 to v0.46.0 in http-dpop-trusted-proxy, http-jwks-example, and iris-example. - Upgrade from v0.38.0 to v0.40.0 in http-dpop-trusted-proxy, http-jwks-example, and iris-example. - Update from v1.6.1 to v1.7.1 in iris-example. - Add issuer field to the OpenID configuration response in http-jwks-example.
… documentation - Added support for Cache-Control headers in JWKS caching provider to extend cache time when allowed. - Updated documentation to clarify security and performance features, including automatic validation of exp and nbf claims. - Improved examples and README files to reflect new caching strategies and recommendations for multi-tenant scenarios.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📝 Checklist
🔧 Changes
This PR implements multi-issuer/multi-tenant support for applications that need to validate JWTs from multiple OIDC issuers (Multiple Custom Domains).
New Components
MultiIssuerProvider:
NewMultiIssuerProvider(opts ...MultiIssuerProviderOption)- Creates multi-issuer JWKS providerKeyFunc(ctx context.Context) (any, error)- Automatically routes JWKS requests per issuerProviderCount() int- Returns number of cached providersNew JWKS Options:
WithMultiIssuerCacheTTL(ttl time.Duration)- Configure cache refresh interval (default: 15 min)WithMultiIssuerHTTPClient(client *http.Client)- Custom HTTP client (default: 30s timeout)WithMultiIssuerCache(cache Cache)- Custom cache implementation for RedisWithMaxProviders(maxProviders int)- LRU eviction limit for memory control (default: unlimited)Validator Options:
WithIssuers(issuers []string)- Accept tokens from multiple static issuersWithIssuersResolver(resolver func(context.Context) ([]string, error))- Dynamic issuer resolutionContext Helpers:
IssuerFromContext(ctx context.Context) (string, bool)- Extract validated issuerSetIssuerInContext(ctx context.Context, issuer string)- Store issuer in contextFeatures
Usage
Static Multiple Issuers:
Large-Scale with Redis + LRU:
Dynamic Issuer Resolution:
📚 References
Use Cases:
Scaling Recommendations:
🔬 Testing
Test Coverage: 96.8%
Unit Tests:
Integration Tests:
examples/http-multi-issuer-example- Static issuer list with 3 tenantsexamples/http-dynamic-issuer-example- Dynamic issuer resolutionexamples/http-multi-issuer-redis-example- Redis cache with LRU evictionCI/CD:
make test-examplestargetManual Testing:
Documentation: