Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[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

Open
enmande wants to merge6 commits intomain
base:main
Choose a base branch
Loading
fromauth/pm-23572/support-persistent-grants-for-sso

Conversation

@enmande
Copy link
Contributor

@enmandeenmande commentedNov 24, 2025
edited by atlassianbot
Loading

🎟️ Tracking

PM-23572

📔 Objective

Allow SSO to be configured such that grants (currently onlyauthorization_code grants 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 theExtendedCache introduced by#6591. The following occurs:

  • Akeyed service is established, backed by theExtendedCache, scoped to SSO grants.
  • ExtendedCache will negotiate the storage mechanism behind the keyed service:
    • If aRedis connection string is present, Redis will be negotiated as the backing store with scoped backplane to ensure any relevant nodes have the value propagated.
    • In-memory caching will be the default (current state).

After 5 minutes, all storage eventualities had their TTLs verified; inspection of Redis viaKEYS 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

  • Contributor guidelines followed
  • All formatters and local linters executed and passed
  • Written new unit and / or integration tests where applicable
  • Protected functional changes with optionality (feature flags)
  • Used internationalization (i18n) for all UI strings
  • CI builds passed
  • Communicated to DevOps any deployment requirements
  • Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

🦮 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

@github-actions
Copy link
Contributor

Logo
Checkmarx One – Scan Summary & Details6b270b9f-6d5b-4707-88af-28b092a7ba5f

Great job! No new security vulnerabilities introduced in this pull request

@codecov
Copy link

codecovbot commentedNov 24, 2025

Codecov Report

❌ Patch coverage is95.55556% with2 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.08%. Comparing base (0422796) to head (04d1a16).
⚠️ Report is 3 commits behind head on main.

Files with missing linesPatch %Lines
...e/src/Sso/Utilities/ServiceCollectionExtensions.cs0.00%2 Missing⚠️
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.
📢 Have feedback on the report?Share it here.

🚀 New features to boost your workflow:
  • ❄️Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@enmandeenmande marked this pull request as ready for reviewNovember 25, 2025 14:35
@enmandeenmande requested a review froma team as acode ownerNovember 25, 2025 14:35
@claude
Copy link
Contributor

claudebot commentedNov 25, 2025
edited
Loading

Claude finished@enmande's task ——View job


Code Review Complete

Summary

This 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 Issues

None found.

Suggested Improvements

Finding 1: 💭 Consider the security implications of grant data serialization and encryption

Details

ThePersistedGrant.Data field contains sensitive authorization code information that gets serialized to Redis. While the PR description mentions "all information encrypted in protected payload," I don't see explicit encryption handling in the code:

  • DistributedCachePersistedGrantStore.cs:96-98 stores the grant directly via_cache.SetAsync(grant.Key, grant, ...)
  • FusionCache usesFusionCacheSystemTextJsonSerializer by default

Questions:

  1. Does Duende IdentityServer pre-encrypt theData field before passing it toIPersistedGrantStore.StoreAsync()?
  2. Is Redis configured with TLS/encryption in transit?
  3. Should we add an additional encryption layer at the cache boundary?

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:⚠️ Missing test coverage for the service registration code

Details

Codecov reports 2 lines missing coverage inServiceCollectionExtensions.cs:86 andServiceCollectionExtensions.cs:90:

services.AddExtendedCache("sso-grants",globalSettings);// ...services.AddSingleton<IPersistedGrantStore,DistributedCachePersistedGrantStore>();

While these are simple registration calls, integration tests verifying:

  1. The keyed service resolves correctly with[FromKeyedServices("sso-grants")]
  2. The cache key prefix is applied properly (sso-grants:)
  3. Graceful fallback to in-memory when Redis is unavailable

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

Details

InDistributedCachePersistedGrantStore.cs:43-46:

if(grant.Expiration.HasValue&&grant.Expiration.Value<DateTime.UtcNow){awaitRemoveAsync(key);returnnull;}

The condition uses< (strictly less than), meaning a grant expiring at the exact current UTC moment would be considered valid. While this is likely acceptable given the sub-second execution time, using<= would be more conservative and align with typical expiration semantics where "expired at time T" means "invalid at time T."

Similarly inStoreAsync (line 89), the check is<= TimeSpan.Zero. Consider whether grants expiring exactly now should be stored.

Finding 4: 🎨 Nullable reference type compliance in ServiceCollectionExtensions.cs

Details

ServiceCollectionExtensions.cs:1-2 has nullable reference types disabled:

// FIXME: Update this file to be null safe and then delete the line below#nullable disable

This 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 enhancement

The PR description includes excellent video demonstrations but could strengthen the "Test plan" section in the checklist. Consider adding:

Test Plan:

  • ✅ Unit tests cover all store operations (257 lines of tests)
  • ✅ Manual testing: In-memory cache verified (video)
  • ✅ Manual testing: Redis cache verified with TTL expiration (video)
  • ✅ Verified cache key scoping prevents conflicts (sso-grant* namespace)
  • ⏱️ Load testing: Horizontal scaling validation (if applicable)
  • 🔒 Security: Verified grant data encryption in Redis (if applicable)

Good Practices Observed

  • Comprehensive unit tests with 95.55% coverage
  • Clear XML documentation explaining design decisions
  • Proper use of keyed services pattern
  • Graceful degradation to in-memory cache
  • Thorough inline comments explaining architectural choices

Action Items

  1. ⚠️Required: Verify/document the encryption strategy for grant data at rest in Redis
  2. ⚠️Recommended: Add integration tests for service registration and keyed service resolution
  3. 💭Optional: Consider using<= for expiration checks for more conservative semantics
  4. 💭Optional: Address nullable reference types in ServiceCollectionExtensions.cs

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.

Copy link
Contributor

@brant-livefrontbrant-livefront left a 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)
Copy link
Contributor

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)

Comment on lines +102 to +104
// Keep distributed cache enabled for multi-instance scenarios
// When Redis isn't configured, FusionCache gracefully uses only L1 (in-memory)
}.SetSkipDistributedCache(false,false));
Copy link
Contributor

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);
Copy link
Contributor

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

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@brant-livefrontbrant-livefrontbrant-livefront approved these changes

@ike-kottlowskiike-kottlowskiAwaiting requested review from ike-kottlowskiike-kottlowski is a code owner automatically assigned from bitwarden/team-auth-dev

At least 1 approving review is required to merge this pull request.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

3 participants

@enmande@brant-livefront

[8]ページ先頭

©2009-2025 Movatter.jp