-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[PM-27281] Support v2 account encryption on JIT master password signups #6777
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
mzieniukbw
merged 20 commits into
main
from
km/pm-27281-v2-encryption-on-jit-password-signups
Jan 9, 2026
Merged
Changes from 6 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
c326c11
V2 prep, rename existing SSO JIT MP command to V1
mzieniukbw 91afded
set initial master password for account registraton V2
mzieniukbw fe1c6ce
later removel docs
mzieniukbw be70162
TDE MP onboarding split
mzieniukbw 3be17ce
revert separate TDE onboarding controller api
mzieniukbw 6ffc418
Server side hash of the user master password hash
mzieniukbw 8cfc753
use `ValidationResult` instead for validation errors
mzieniukbw 7aa6729
unit test coverage
mzieniukbw 3363b12
integration test coverage
mzieniukbw ed5c2d9
update sql migration script date
mzieniukbw 67443a6
Merge branch 'main' into km/pm-27281-v2-encryption-on-jit-password-si…
mzieniukbw b56fbfb
revert validate password change
mzieniukbw a2dd100
better requests validation
mzieniukbw d9bd084
explicit error message when org sso identifier invalid
mzieniukbw f58f500
more unit test coverage
mzieniukbw 47c77d5
renamed onboarding to set, hash naming clarifications
mzieniukbw 435ffd4
update db sql script, formatting
mzieniukbw 51a351b
use raw json as request instead of request models for integration test
mzieniukbw beb4129
v1 integration test coverage
mzieniukbw 38c17f9
change of name
mzieniukbw File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
157 changes: 157 additions & 0 deletions
157
src/Api/Auth/Models/Request/Accounts/SetInitialPasswordRequestModel.cs
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| using System.ComponentModel.DataAnnotations; | ||
| using Bit.Api.KeyManagement.Models.Requests; | ||
| using Bit.Core.Auth.Models.Api.Request.Accounts; | ||
| using Bit.Core.Auth.Models.Data; | ||
| using Bit.Core.Entities; | ||
| using Bit.Core.Enums; | ||
| using Bit.Core.Exceptions; | ||
| using Bit.Core.KeyManagement.Models.Api.Request; | ||
| using Bit.Core.Utilities; | ||
|
|
||
| namespace Bit.Api.Auth.Models.Request.Accounts; | ||
|
|
||
| public class SetInitialPasswordRequestModel : IValidatableObject | ||
| { | ||
| // TODO will be removed with https://bitwarden.atlassian.net/browse/PM-27327 | ||
| [Obsolete("Use MasterPasswordAuthentication instead")] | ||
| [StringLength(300)] | ||
| public string? MasterPasswordHash { get; set; } | ||
|
|
||
| [Obsolete("Use MasterPasswordUnlock instead")] | ||
| public string? Key { get; set; } | ||
|
|
||
| [Obsolete("Use AccountKeys instead")] | ||
| public KeysRequestModel? Keys { get; set; } | ||
|
|
||
| [Obsolete("Use MasterPasswordAuthentication instead")] | ||
| public KdfType? Kdf { get; set; } | ||
|
|
||
| [Obsolete("Use MasterPasswordAuthentication instead")] | ||
| public int? KdfIterations { get; set; } | ||
|
|
||
| [Obsolete("Use MasterPasswordAuthentication instead")] | ||
| public int? KdfMemory { get; set; } | ||
|
|
||
| [Obsolete("Use MasterPasswordAuthentication instead")] | ||
| public int? KdfParallelism { get; set; } | ||
|
|
||
| public MasterPasswordAuthenticationDataRequestModel? MasterPasswordAuthentication { get; set; } | ||
| public MasterPasswordUnlockDataRequestModel? MasterPasswordUnlock { get; set; } | ||
| public AccountKeysRequestModel? AccountKeys { get; set; } | ||
|
|
||
| [StringLength(50)] | ||
| public string? MasterPasswordHint { get; set; } | ||
|
|
||
| [Required] | ||
| public required string OrgIdentifier { get; set; } | ||
|
|
||
| // TODO removed with https://bitwarden.atlassian.net/browse/PM-27327 | ||
| public User ToUser(User existingUser) | ||
| { | ||
| existingUser.MasterPasswordHint = MasterPasswordHint; | ||
| existingUser.Kdf = Kdf!.Value; | ||
| existingUser.KdfIterations = KdfIterations!.Value; | ||
| existingUser.KdfMemory = KdfMemory; | ||
| existingUser.KdfParallelism = KdfParallelism; | ||
| existingUser.Key = Key; | ||
| Keys?.ToUser(existingUser); | ||
| return existingUser; | ||
| } | ||
|
|
||
| public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) | ||
| { | ||
| if (IsV2Request()) | ||
| { | ||
| // V2 registration | ||
|
|
||
| // Validate Kdf | ||
| var authenticationKdf = MasterPasswordAuthentication!.Kdf.ToData(); | ||
| var unlockKdf = MasterPasswordUnlock!.Kdf.ToData(); | ||
|
|
||
| // Currently, KDF settings are not saved separately for authentication and unlock and must therefore be equal | ||
| if (!authenticationKdf.Equals(unlockKdf)) | ||
| { | ||
| throw new BadRequestException("KDF settings must be equal for authentication and unlock."); | ||
| } | ||
|
|
||
| var authenticationValidationErrors = KdfSettingsValidator.Validate(authenticationKdf).ToList(); | ||
| if (authenticationValidationErrors.Count != 0) | ||
| { | ||
| yield return authenticationValidationErrors.First(); | ||
| } | ||
|
|
||
| var unlockValidationErrors = KdfSettingsValidator.Validate(unlockKdf).ToList(); | ||
| if (unlockValidationErrors.Count != 0) | ||
| { | ||
| yield return unlockValidationErrors.First(); | ||
| } | ||
|
|
||
| yield break; | ||
| } | ||
|
|
||
| // V1 registration | ||
| // TODO removed with https://bitwarden.atlassian.net/browse/PM-27327 | ||
| if (string.IsNullOrEmpty(MasterPasswordHash)) | ||
| { | ||
| yield return new ValidationResult("MasterPasswordHash must be supplied."); | ||
| } | ||
|
|
||
| if (string.IsNullOrEmpty(Key)) | ||
| { | ||
| yield return new ValidationResult("Key must be supplied."); | ||
| } | ||
|
|
||
| if (Kdf == null) | ||
| { | ||
| yield return new ValidationResult("Kdf must be supplied."); | ||
| } | ||
|
|
||
| if (KdfIterations == null) | ||
| { | ||
| yield return new ValidationResult("KdfIterations must be supplied."); | ||
| } | ||
|
|
||
| if (Kdf == KdfType.Argon2id) | ||
| { | ||
| if (KdfMemory == null) | ||
| { | ||
| yield return new ValidationResult("KdfMemory must be supplied when Kdf is Argon2id."); | ||
| } | ||
|
|
||
| if (KdfParallelism == null) | ||
| { | ||
| yield return new ValidationResult("KdfParallelism must be supplied when Kdf is Argon2id."); | ||
| } | ||
| } | ||
|
|
||
| var validationErrors = KdfSettingsValidator | ||
| .Validate(Kdf!.Value, KdfIterations!.Value, KdfMemory, KdfParallelism).ToList(); | ||
| if (validationErrors.Count != 0) | ||
| { | ||
| yield return validationErrors.First(); | ||
| } | ||
| } | ||
|
|
||
| public bool IsV2Request() | ||
| { | ||
| // AccountKeys can be null for TDE users, so we don't check that here | ||
| return MasterPasswordAuthentication != null && MasterPasswordUnlock != null; | ||
| } | ||
|
|
||
| public bool IsTdeOnboardingPassword() | ||
| { | ||
| return AccountKeys == null; | ||
| } | ||
|
|
||
| public SetInitialMasterPasswordDataModel ToData() | ||
| { | ||
| return new SetInitialMasterPasswordDataModel | ||
| { | ||
| MasterPasswordAuthentication = MasterPasswordAuthentication!.ToData(), | ||
| MasterPasswordUnlock = MasterPasswordUnlock!.ToData(), | ||
| OrgSsoIdentifier = OrgIdentifier, | ||
| AccountKeys = AccountKeys?.ToAccountKeysData(), | ||
| MasterPasswordHint = MasterPasswordHint | ||
| }; | ||
| } | ||
| } |
40 changes: 0 additions & 40 deletions
40
src/Api/Auth/Models/Request/Accounts/SetPasswordRequestModel.cs
This file was deleted.
Oops, something went wrong.
23 changes: 23 additions & 0 deletions
23
src/Core/Auth/Models/Data/SetInitialMasterPasswordDataModel.cs
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| using Bit.Core.KeyManagement.Models.Data; | ||
|
|
||
| namespace Bit.Core.Auth.Models.Data; | ||
|
|
||
| /// <summary> | ||
| /// Data model for setting an initial master password for a user. | ||
| /// </summary> | ||
| public class SetInitialMasterPasswordDataModel | ||
| { | ||
| public required MasterPasswordAuthenticationData MasterPasswordAuthentication { get; set; } | ||
| public required MasterPasswordUnlockData MasterPasswordUnlock { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Organization SSO identifier. | ||
| /// </summary> | ||
| public required string OrgSsoIdentifier { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// User account keys. Required for Master Password decryption user. | ||
| /// </summary> | ||
| public required UserAccountKeysData? AccountKeys { get; set; } | ||
| public string? MasterPasswordHint { get; set; } | ||
| } |
27 changes: 27 additions & 0 deletions
27
src/Core/Auth/UserFeatures/TdeOnboardingPassword/Interfaces/ITdeOnboardingPasswordCommand.cs
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| using Bit.Core.Auth.Models.Data; | ||
| using Bit.Core.Entities; | ||
| using Bit.Core.Exceptions; | ||
|
|
||
| namespace Bit.Core.Auth.UserFeatures.TdeOnboardingPassword.Interfaces; | ||
|
|
||
| /// <summary> | ||
| /// <para>Manages the setting of the master password onboarding for a TDE <see cref="User"/> in an organization.</para> | ||
| /// <para>In organizations configured with SSO and trusted devices decryption: | ||
| /// Users who are upgraded to have admin account recovery permissions must set a master password | ||
| /// to ensure their ability to reset other users' accounts.</para> | ||
| /// </summary> | ||
| public interface ITdeOnboardingPasswordCommand | ||
| { | ||
| /// <summary> | ||
| /// Onboard the master password for the specified TDE user. | ||
| /// </summary> | ||
| /// <param name="user">User to set the master password for</param> | ||
| /// <param name="masterPasswordDataModel">Master password setup data</param> | ||
| /// <returns>A task that completes when the operation succeeds</returns> | ||
| /// <exception cref="BadRequestException"> | ||
| /// Thrown if the user's master password is already set, the organization is not found, | ||
| /// the user is not a member of the organization, the master password does not meet requirements, | ||
| /// or the user is a TDE user without account keys set. | ||
| /// </exception> | ||
| Task OnboardMasterPasswordAsync(User user, SetInitialMasterPasswordDataModel masterPasswordDataModel); | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.