- Notifications
You must be signed in to change notification settings - Fork1.5k
[PM-23572] Add Persistent Grants to SSO#6636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Conversation
…CachePersistedGrantStore.
… comments on ExtendedCache vs InMemoryCaching for Duende.
Great job! No new security vulnerabilities introduced in this pull request |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@## main #6636 +/- ##==========================================+ Coverage 52.99% 53.08% +0.08%========================================== Files 1910 1900 -10 Lines 84843 84687 -156 Branches 7636 7614 -22 ==========================================- Hits 44966 44959 -7+ Misses 38125 37979 -146+ Partials 1752 1749 -3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
claudebot commentedNov 25, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Claude finished@enmande's task ——View job Code Review CompleteSummaryThis PR adds distributed caching support for SSO authorization code grants, enabling horizontal scaling. The implementation uses FusionCache with Redis (or in-memory fallback) and is well-architected with strong test coverage. Critical IssuesNone found. Suggested ImprovementsFinding 1: 💭 Consider the security implications of grant data serialization and encryption DetailsThe
Questions:
Given Bitwarden's zero-knowledge principles and the security-critical nature of authorization codes, explicit documentation or verification of the encryption strategy would strengthen confidence. Finding 2: DetailsCodecov reports 2 lines missing coverage in services.AddExtendedCache("sso-grants",globalSettings);// ...services.AddSingleton<IPersistedGrantStore,DistributedCachePersistedGrantStore>(); While these are simple registration calls, integration tests verifying:
would provide confidence in the DI wiring, especially given the keyed services pattern. Finding 3: 💭 Consider edge case: grants with expiration exactly at DateTime.UtcNow DetailsIn if(grant.Expiration.HasValue&&grant.Expiration.Value<DateTime.UtcNow){awaitRemoveAsync(key);returnnull;} The condition uses Similarly in Finding 4: 🎨 Nullable reference type compliance in ServiceCollectionExtensions.cs Details
// FIXME: Update this file to be null safe and then delete the line below#nullable disableThis conflicts with ADR-0024 which mandates nullable reference types. While this PR doesn't introduce the issue, adding new code to a file marked for nullable compliance would be a good opportunity to address the FIXME, or at minimum, add a TODO comment acknowledging this tech debt. Finding 5: 💭 PR Description: Test plan could be more detailed Suggested enhancementThe PR description includes excellent video demonstrations but could strengthen the "Test plan" section in the checklist. Consider adding: Test Plan:
Good Practices Observed
Action Items
Overall Assessment: This is a well-designed implementation with strong test coverage. The primary concern is ensuring grant data encryption aligns with Bitwarden's zero-knowledge security model. Once encryption strategy is verified/documented, this is ready to merge. |
brant-livefront left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
This looks great and I'm excited to see someone else using extended cache. 🎉
A few mild suggestions, but this looks good to me.
| privatereadonlyIFusionCache_cache; | ||
| publicDistributedCachePersistedGrantStore( | ||
| [FromKeyedServices("sso-grants")]IFusionCachecache) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
One of the patterns I was using (you can see an example in#6650) was to have a cache constants class. The virtue being that the cache name and keys are not strings but are typed to constants. It looks like you already have a consistent way of building keys, so you probably don't need that part. But it still might be worthwhile to make absolutely sure the cache name is consistent.
i.e.
// In constants classpublicconststringCacheName="sso-grants";// In this code[FromKeyedServices(SsoGrantsCacheConstants.CacheName)]IFusionCache cache)
| // Keep distributed cache enabled for multi-instance scenarios | ||
| // When Redis isn't configured, FusionCache gracefully uses only L1 (in-memory) | ||
| }.SetSkipDistributedCache(false,false)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I think these are the default values, so you shouldn't have to overwrite on every cache entry?
| // Provides separation of concerns and automatic Redis/in-memory negotiation | ||
| // .AddInMemoryCaching should still persist above; this handles configuration caching, etc., | ||
| // and is separate from this keyed service, which only serves grant negotiation. | ||
| services.AddExtendedCache("sso-grants",globalSettings); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
If you adopt my comment above, here's another place to use theCacheName constant

Uh oh!
There was an error while loading.Please reload this page.
🎟️ Tracking
PM-23572
📔 Objective
Allow SSO to be configured such that grants (currently only
authorization_codegrants are used) can be distributed beyond the server's in-memory caching to enable horizontal scaling of the application.Clarifying details around this particular approach can be found in the ticket, the linked Tech Breakdown, and in the proposed code.
This approach will use the
ExtendedCacheintroduced by#6591. The following occurs:ExtendedCache, scoped to SSO grants.ExtendedCachewill negotiate the storage mechanism behind the keyed service:After 5 minutes, all storage eventualities had their TTLs verified; inspection of Redis via
KEYS sso-grant*showed an empty array.📸 Screenshots
In-Memory Caching
No redis configuration present. Does not support horizontal scaling of SSO. Represents continued viability of current state.
PM-23572__in-memory.mov
Redis caching
Redis configuration present. Shows startup of the SSO application and connection to Redis with configuration of the keyed backplane. Shows a full SSO login flow proving scoped storage of sso grants to the cache (key conflict possibilities mitigated), with all information encrypted in protected payload.
PM-23572__redis.mov
⏰ Reminders before review
🦮 Reviewer guidelines
:+1:) or similar for great changes:memo:) or ℹ️ (:information_source:) for notes or general info:question:) for questions:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion:art:) for suggestions / improvements:x:) or:warning:) for more significant problems or concerns needing attention:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt:pick:) for minor or nitpick changes