Skip to content

[PM-35395] MasterPasswordService Key Management Integration#7637

Open
enmande wants to merge 14 commits into
mainfrom
auth/pm-35395/master-password-service-km-integration
Open

[PM-35395] MasterPasswordService Key Management Integration#7637
enmande wants to merge 14 commits into
mainfrom
auth/pm-35395/master-password-service-km-integration

Conversation

@enmande
Copy link
Copy Markdown
Contributor

@enmande enmande commented May 14, 2026

🎟️ Tracking

PM-35395

📔 Objective

Refactors the Change-KDF flow to use the new MasterPasswordService, routing through SaveUpdateKdfConfigurationAsync (source).

📓 Note the name change from the base branch of invoked method to SaveUpdateKdfConfigurationAsync.
At implementation time, it was decided SaveUpdateMasterPasswordAndKdfAsync was not an ideal name for this method. It is used for KDF-affecting flows (only); the fact that the hash of a master password will change as a result of key derivation is a side effect, and naming it in the method directly invites misunderstanding. Tests and comments were updated throughout to reflect this.

Part of the PM-33011 story-of-stories to route all password set/change/rotate flows through MasterPasswordService. Depends on PM-35393.

📸 Screenshots

KDF Configuration change

zed is a user with a Master Password.

zed changes their KDF configuration: PBKDF2 -> Argon2id with default settings.

  • Stored hash-of-hash for Master Password is updated
  • LastKdfChangeDate is updated as expected
  • LastMasterPasswordChangeDate is not updated as expected
  • Appropriate KDF configurations are updated
PM-39395_kdf-change.mov

@enmande enmande added needs-qa ai-review Request a Claude code review labels May 14, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 14, 2026

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

This PR routes the Change-KDF flow through MasterPasswordService.SaveUpdateExistingKdfConfigurationAsync (renamed from SaveUpdateExistingMasterPasswordAndKdfAsync), drops the dual-shape legacy payload from ChangeKdfRequestModel in favor of the required AuthenticationData/UnlockData contract, and correctly stops updating LastPasswordChangeDate during a KDF rotation. Behavior is preserved: validation, security-stamp rotation, push-logout, feature-flag handling, and salt-unchanged enforcement all match the prior flow, with KdfSettingsValidator.Validate and the explicit salt check retained on the command as defense in depth above the service. Test coverage is comprehensive — unit tests assert that LastPasswordChangeDate is left untouched, that command-level errors surface from the service, and integration tests cover both missing-JSON-key (deserialization) and explicit-null ([Required]) failure modes.

No findings worth posting. The most material risk in the diff — semantic drift on LastPasswordChangeDate — was already caught and fixed in commit d6e86c6e2, and the unresolved review thread on MasterPasswordService.cs:204 is on the now-removed LEFT-side code.

Code Review Details

No findings.

// LastPasswordChangeDate is intentionally not set: KDF rotation re-derives the authentication
// hash from the same password using new KDF parameters — the user's password has not changed.
var now = _timeProvider.GetUtcNow().UtcDateTime;
user.LastPasswordChangeDate = now;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method incorrectly indexed on a change of hash to warrant an update to the LastPasswordChangeDate. KDF- and Master Password-change operations are separate, and LastPasswordChangeDate is intended to capture the user action/intent to change the Master Password.

LastKdfChangeDate is provided to separate these concerns.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent catch.

Comment thread test/Core.Test/KeyManagement/Kdf/ChangeKdfCommandTests.cs Outdated
// Prevent a de-synced salt value from creating an un-decryptable unlock method
// Prevent a de-synced salt value from creating an un-decryptable unlock method.
// Also checked in the MasterPasswordService via UpdateExistingKdfConfigurationData.ValidateDataForUser.
authenticationData.ValidateSaltUnchangedForUser(user);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MasterPasswordService will perform salt-unchanged validation for authentication and unlock data requests consistently. That makes this check (now) an additional check of the same.

I have elected not to remove it from the command at this time, considering that up to Key Management's discretion.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 14, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 60.42%. Comparing base (455a345) to head (c22cc13).
⚠️ Report is 10 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7637      +/-   ##
==========================================
- Coverage   64.61%   60.42%   -4.20%     
==========================================
  Files        2137     2140       +3     
  Lines       94388    94612     +224     
  Branches     8405     8441      +36     
==========================================
- Hits        60993    57168    -3825     
- Misses      31312    35437    +4125     
+ Partials     2083     2007      -76     

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

@enmande enmande marked this pull request as ready for review May 14, 2026 19:16
@enmande enmande requested review from a team as code owners May 14, 2026 19:16
@enmande enmande requested review from JaredSnider-Bitwarden, ike-kottlowski and mzieniukbw and removed request for a team and JaredSnider-Bitwarden May 14, 2026 19:16
Thomas-Avery
Thomas-Avery previously approved these changes May 18, 2026
Copy link
Copy Markdown
Contributor

@Thomas-Avery Thomas-Avery left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KM changes look good. My callout here is before this is merged into main we need good QA regression testing around feature flags pm-18021-force-update-kdf-settings and pm-23995-no-logout-on-kdf-change on to make sure we don't impact the KDF migrations currently on going.

@JaredSnider-Bitwarden
Copy link
Copy Markdown
Contributor

Merge plan: Auth integration PR (base) merges first then we will rebase this PR and re-request reviews then this can go to QA

Base automatically changed from auth/pm-35393/master-password-service-auth-integration to main May 20, 2026 16:28
@JaredSnider-Bitwarden JaredSnider-Bitwarden dismissed Thomas-Avery’s stale review May 20, 2026 16:28

The base branch was changed.

@JaredSnider-Bitwarden JaredSnider-Bitwarden force-pushed the auth/pm-35395/master-password-service-km-integration branch from 848f6d7 to d6e86c6 Compare May 20, 2026 20:50
@sonarqubecloud
Copy link
Copy Markdown

Thomas-Avery
Thomas-Avery previously approved these changes May 20, 2026
Copy link
Copy Markdown
Contributor

@Thomas-Avery Thomas-Avery left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KM changes LGTM

Copy link
Copy Markdown
Contributor

@JaredSnider-Bitwarden JaredSnider-Bitwarden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent work

…cy fields

Removes the deprecated NewMasterPasswordHash/Key fields and the
MasterPasswordPayloadVariantValidator path from ChangeKdfRequestModel.
AuthenticationData and UnlockData become C# required + [Required]; rejection
of null/missing values is delegated to System.Text.Json and the ASP.NET
ModelStateValidationFilter.

Validate() adds an early return when either cross-field input is null,
so it remains safe to invoke after [Required] has already populated
ModelState (the framework runs IValidatableObject.Validate regardless of
data-annotation outcome).

Tests for the dropped variant-validator path are removed; a new
Validate_EitherFieldNull_ReturnsNoErrors test pins the defensive guard.
…, align tests

Removes the manual null check from PostKdf — null/missing payload fields
are now rejected by [Required] + ModelStateValidationFilterAttribute
before the action runs.

Drops two unit tests that asserted the old custom error message
(framework-layer behavior, no longer reachable at the controller layer).
Updates the integration test PostKdf_AuthenticationDataOrUnlockDataNull
to assert the default [Required] messages, and adds
PostKdf_MissingRequiredJsonKey to lock in the `required` C# keyword
contract for the wire payload.
@JaredSnider-Bitwarden JaredSnider-Bitwarden dismissed stale reviews from Thomas-Avery and themself via f564c17 May 21, 2026 19:06
@JaredSnider-Bitwarden
Copy link
Copy Markdown
Contributor

When reviewing the QA testing notes, I realized that when we (BW) modified PostKdf 7 months ago with this PR, we made the authentication and unlock data required at that time but enforced it within the endpoint itself instead of on the request model. We also changed the clients at that time to always send unlock and authentication data with bitwarden/clients#15748.

So, I've updated the ChangeKdfRequest model to remove the legacy field properties that haven't been consumed in prod for ~6 months, and I've updated all the relevant tests to ensure no regressions.

Copy link
Copy Markdown
Contributor

@JaredSnider-Bitwarden JaredSnider-Bitwarden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Smoke tested latest changes - everything looks good.

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

Labels

ai-review Request a Claude code review needs-qa

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants