dotnet / dotnet/AspNetCore.Docs
Add guidance for safely implementing a custom IssuerSigningKeyResolver (multi-tenant / JWKS scenarios)
- Dominant language
- C#
- Stars
- 13.1k
- Forks
- 24.6k
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 97
Description
## Help us make content visible
**Search terms used:** `IssuerSigningKeyResolver aspnetcore`, `JwtBearer custom signing key`, `multi-tenant JWT validation aspnetcore`.
**Docs found that didn't address the concern:**
- https://learn.microsoft.com/aspnet/core/security/authentication/configure-jwt-bearer-authentication — covers `Authority`/`IssuerSigningKey` but not custom resolvers.
- An audit of `dotnet/AspNetCore.Docs` returns **0 hits** for `IssuerSigningKeyResolver`. The same query on `dotnet/aspnetcore` also returns 0. The only hits across the `dotnet` org are inside the IdentityModel implementation itself — there is no narrative guidance on how to implement this extension point safely.
## Describe the new topic
**Why it's needed**
`TokenValidationParameters.IssuerSigningKeyResolver` is the standard extension point for dynamic signing-key lookup (multi-tenant apps, JWKS caching, custom key stores, key-rotation rollouts). It's widely used in real-world apps but has no official sample. Developers searching for examples land on third-party blogs and Stack Overflow, where anti-patterns are common.
The risk is concrete: ASP.NET Core's default JwtBearer config defends against HS/RS algorithm-confusion attacks via *typed-key resolution* — `JsonWebTokenHandler` only verifies HS256 signatures with `SymmetricSecurityKey` candidates. A custom resolver that returns a `SymmetricSecurityKey` derived from public-key bytes (a plausible mistake when caching key material in a multi-tenant scenario) silently disables that defense. The same forged HS256 token that the default config rejects with 401 then validates with 200 OK and full claim assumption.
I have a self-contained reproduction (Minimal API + xUnit, runs `dotnet test` in under 2 seconds) demonstrating both the safe default and the broken-resolver attack:
**https://github.com/loganbryanx/aspnetcore-auth-lab**
**Suggested ToC location**
`fundamentals/security/authentication/configure-jwt-bearer-authentication` — as a new sibling page or expanded section titled "Custom signing-key resolution".
**Abstract**
A how-to for implementing `IssuerSigningKeyResolver` in scenarios that aren't covered by `Authority`-based discovery (multi-tenant apps, custom key stores, advanced JWKS caching). Side-by-side examples show a safe resolver that asserts key/algorithm compatibility before yielding keys, and an anti-pattern resolver that silently disables the framework's typed-key defense against HS/RS algorithm confusion. Includes the recommendation to set `ValidAlgorithms` as defense-in-depth.
**Outline**
1. When you need a custom resolver (and when you don't — prefer `Authority` if possible)
2. The safe pattern — minimal multi-tenant resolver, with `kid`-keyed lookup
3. JWKS caching with `IssuerSigningKeyResolverUsingConfiguration`
4. The anti-pattern — wrapping public-key bytes in `SymmetricSecurityKey`, with the algorithm-confusion risk it introduces
5. Defense in depth — setting `ValidAlgorithms`, asserting key types, logging on validation failure
6. Testing your resolver — a forged-token regression test pattern
Happy to draft the PR if the team agrees the gap warrants it.
Contributor guide
Assessment
This issue has not been assessed yet.