Skip to content

Commit 22731cb

Browse files
committed
docs(mp-service): Address review comment feedback.
1 parent ba43405 commit 22731cb

2 files changed

Lines changed: 26 additions & 10 deletions

File tree

src/Core/Auth/UserFeatures/UserMasterPassword/Interfaces/IMasterPasswordService.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
namespace Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces;
88

99
/// <summary>
10-
/// Centralized mutation point for all master password set, change, and rotate operations.
10+
/// Centralized mutation point for all master password set and update operations.
1111
/// Provides consistent validation, password hashing, and timestamp management across every
1212
/// flow that establishes or updates a user's master password.
1313
///
@@ -170,8 +170,8 @@ public interface IMasterPasswordService
170170

171171
/// <summary>
172172
/// Applies a new master password and updated KDF parameters over the user's existing ones
173-
/// and persists the updated user to the database. Salt must remain unchanged; KDF is
174-
/// intentionally allowed to change. Use for KDF rotation flows.
173+
/// and persists the updated user to the database. Salt must remain unchanged; KDF
174+
/// validation is intentionally skipped. Use for KDF rotation flows.
175175
/// </summary>
176176
/// <param name="user">
177177
/// The user object to mutate and persist. Must already have a master password;

src/Core/Auth/UserFeatures/UserMasterPassword/MasterPasswordService.cs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ public async Task<OneOf<User, IdentityError[]>> PrepareSetInitialMasterPasswordA
5151
EnsureUserIsHydrated(user);
5252
setInitialData.ValidateDataForUser(user);
5353

54+
// Note: Keep "unlock then authenticate" pattern in mind.
55+
// This is a purposeful inversion of that principle:
56+
// Authentication is derivative of unlock, and is the mechanism for validation;
57+
// eager validation keeps the logic easier to reason about, so it is performed first.
5458
var result = await UpdateExistingPasswordHashAsync(
5559
user,
5660
setInitialData.MasterPasswordAuthentication.MasterPasswordAuthenticationHash,
@@ -67,7 +71,7 @@ public async Task<OneOf<User, IdentityError[]>> PrepareSetInitialMasterPasswordA
6771
// Set salt on the user
6872
user.MasterPasswordSalt = setInitialData.MasterPasswordUnlock.Salt;
6973

70-
// Always override the master password hint, even if it's null.
74+
// Always override the master password hint, even if it's null
7175
user.MasterPasswordHint = setInitialData.MasterPasswordHint;
7276

7377
// Update time markers on the user
@@ -103,6 +107,10 @@ public UpdateUserData BuildUpdateUserDelegateSetInitialMasterPassword(
103107

104108
return async (connection, transaction) =>
105109
{
110+
// Note: Keep "unlock then authenticate" pattern in mind.
111+
// This is a purposeful inversion of that principle:
112+
// Authentication is derivative of unlock, and is the mechanism for validation;
113+
// eager validation keeps the logic easier to reason about, so it is performed first.
106114
if (setInitialData.ValidatePassword)
107115
{
108116
var validate = await ValidatePasswordInternalAsync(user,
@@ -139,6 +147,10 @@ public async Task<OneOf<User, IdentityError[]>> PrepareUpdateExistingMasterPassw
139147
EnsureUserIsHydrated(user);
140148
updateExistingData.ValidateDataForUser(user);
141149

150+
// Note: Keep "unlock then authenticate" pattern in mind.
151+
// This is a purposeful inversion of that principle:
152+
// Authentication is derivative of unlock, and is the mechanism for validation;
153+
// eager validation keeps the logic easier to reason about, so it is performed first.
142154
var result = await UpdateExistingPasswordHashAsync(
143155
user,
144156
updateExistingData.MasterPasswordAuthentication.MasterPasswordAuthenticationHash,
@@ -150,13 +162,13 @@ public async Task<OneOf<User, IdentityError[]>> PrepareUpdateExistingMasterPassw
150162
return result.Errors.ToArray();
151163
}
152164

153-
var now = _timeProvider.GetUtcNow().UtcDateTime;
154-
155165
user.Key = updateExistingData.MasterPasswordUnlock.MasterKeyWrappedUserKey;
156166

157-
// Always override the master password hint, even if it's null.
167+
// Always override the master password hint, even if it's null
158168
user.MasterPasswordHint = updateExistingData.MasterPasswordHint;
159169

170+
// Update time markers on the user
171+
var now = _timeProvider.GetUtcNow().UtcDateTime;
160172
user.LastPasswordChangeDate = now;
161173
user.RevisionDate = user.AccountRevisionDate = now;
162174

@@ -170,6 +182,10 @@ public async Task<OneOf<User, IdentityError[]>> SaveUpdateExistingMasterPassword
170182
EnsureUserIsHydrated(user);
171183
updateExistingExistingData.ValidateDataForUser(user);
172184

185+
// Note: Keep "unlock then authenticate" pattern in mind.
186+
// This is a purposeful inversion of that principle:
187+
// Authentication is derivative of unlock, and is the mechanism for validation;
188+
// eager validation keeps the logic easier to reason about, so it is performed first.
173189
var result = await UpdateExistingPasswordHashAsync(
174190
user,
175191
updateExistingExistingData.MasterPasswordAuthentication.MasterPasswordAuthenticationHash,
@@ -181,14 +197,14 @@ public async Task<OneOf<User, IdentityError[]>> SaveUpdateExistingMasterPassword
181197
return result.Errors.ToArray();
182198
}
183199

184-
var now = _timeProvider.GetUtcNow().UtcDateTime;
185-
186200
user.Key = updateExistingExistingData.MasterPasswordUnlock.MasterKeyWrappedUserKey;
187201
SetKdfStateOnUser(user, updateExistingExistingData.MasterPasswordUnlock.Kdf);
188202

189-
// Always override the master password hint, even if it's null.
203+
// Always override the master password hint, even if it's null
190204
user.MasterPasswordHint = updateExistingExistingData.MasterPasswordHint;
191205

206+
// Update time markers on the user
207+
var now = _timeProvider.GetUtcNow().UtcDateTime;
192208
user.LastPasswordChangeDate = now;
193209
user.LastKdfChangeDate = now;
194210
user.RevisionDate = user.AccountRevisionDate = now;

0 commit comments

Comments
 (0)