Skip to content

Add guidance for safely implementing a custom IssuerSigningKeyResolver (multi-tenant / JWKS scenarios) #37076

@loganbryanx

Description

@loganbryanx

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:

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 resolutionJsonWebTokenHandler 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.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions