Add PartitionAppTokenCacheByAudience to partition the app token cache by resource - #3979
Merged
Merged
Conversation
… by resource
Apps that acquire app tokens (client credentials) for many resources using the
same client and tenant accumulate all tokens in a single {clientId}_{tenantId}
cache partition, making AcquireTokenForClient cache lookups O(n) in the number of
resources.
Add a MicrosoftIdentityOptions.PartitionAppTokenCacheByAudience opt-in (config
bindable, flows into MergedOptions like UseFastUnboundedCache). When enabled,
TokenAcquisition derives the resource from the requested <resource>/.default scope
and calls MSAL's WithCachePartitionKey("resource", resource), which partitions both
MSAL's internal cache and the serialized in-memory cache blob per resource, keeping
reads O(1).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d02b5ca-22b5-4b48-840a-de79786b3f9d
gladjohn
marked this pull request as ready for review
July 27, 2026 17:10
neha-bhargava
approved these changes
Jul 27, 2026
bgavrilMS
approved these changes
Jul 27, 2026
gladjohn
enabled auto-merge (squash)
July 27, 2026 18:20
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.
Problem
Apps that acquire app tokens (client credentials /
AcquireTokenForClient) for many resources using the same client and tenant accumulate all of those tokens in a single{clientId}_{tenantId}app-token cache partition. MSAL's cache lookup is O(n) in the number of tokens in that partition, so a token-broker-style workload (one broker client acquiring tokens for many downstream audiences) can grow one partition very large and make everyAcquireTokenForClientslow. Related: #3961.What we've done so far
IMemoryCacheserialization provider (with per-entry expiry and the cache-key partitioning hook) actually runs instead of MSAL's opaque static cache. That was the prerequisite for per-audience partitioning.What this PR does
Adds an opt-in that puts the audience (resource) into the app-token cache key, so each resource gets its own partition and cache reads stay O(1).
MicrosoftIdentityOptions.PartitionAppTokenCacheByAudience(bool, defaultfalse), config-bindable and flowed intoMergedOptions(same pattern asEnableCacheSynchronization/UseFastUnboundedCache):TokenAcquisition.GetAuthenticationResultForAppInternalAsyncderives the resource from the requested<resource>/.defaultscope and calls MSAL's public API:GetKeyFromRequest) and write (GetExternalCacheKeyFromResponse) paths, the single call partitions both MSAL's internal cache and the serializedIMemoryCacheblob per resource, symmetrically.Files
MicrosoftIdentityOptions.cs— newPartitionAppTokenCacheByAudienceMergedOptions.cs— flow it intoMergedOptionsTokenAcquisition.cs— derive resource from scope +WithCachePartitionKeyPublicAPI/{NetCore,NetFramework}/PublicAPI.Unshipped.txtTesting
Two behavior tests:
AppToken_PartitionByAudience_UsesSeparateCacheEntryPerResource— flag on, two resources → 2IMemoryCacheentries (one partition per resource).AppToken_WithoutPartitionByAudience_SharesSingleCacheEntry— flag off, two resources → 1 shared entry (today's behavior).Local unit suite green across TFMs (net462, net472, net8.0, net9.0, net10.0). Integration/E2E not run locally (require lab credentials).
Performance (measured locally through Microsoft.Identity.Web)
Measured
IAuthorizationHeaderProvider.CreateAuthorizationHeaderForAppAsync(in-memory cache) for one client/tenant acquiring app tokens for N distinct resources, with the flag off (current behavior after #3970) vs ON (this PR). Cache-hit = repeated acquisition of an already-cached resource; warm-up = time to acquire all N.(Measured with a throwaway local harness; not included in this PR.)
Notes
WithCachePartitionKey; master is on 4.87.0.🤖 Prototyped during an investigation with GitHub Copilot.