From 6c7daa63432ad50f814b0c1d646916d85fe8fb31 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Tue, 9 Dec 2025 16:13:46 -0500 Subject: [PATCH 01/44] feat(register): [PM-27084] Account Register Uses New Data Types - Initial changes --- .../Accounts/RegisterFinishRequestModel.cs | 39 ++++++++++++++----- src/Core/Entities/User.cs | 6 +-- src/Core/Utilities/KdfSettingsValidator.cs | 27 +++++++++++++ 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 0ac7dbbcb4bd..92cafbfe7b7d 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -1,6 +1,7 @@ #nullable enable using Bit.Core.Entities; using Bit.Core.Enums; +using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Utilities; namespace Bit.Core.Auth.Models.Api.Request.Accounts; @@ -21,18 +22,25 @@ public class RegisterFinishRequestModel : IValidatableObject public required string Email { get; set; } public string? EmailVerificationToken { get; set; } + public MasterPasswordAuthenticationData? MasterPasswordAuthenticationData { get; set; } + public MasterPasswordUnlockData? MasterPasswordUnlockData { get; set; } + + // PM-28143 - Made to be optional as migrating to MasterPasswordUnlockData [StringLength(1000)] - public required string MasterPasswordHash { get; set; } + public required string? MasterPasswordHash { get; set; } [StringLength(50)] public string? MasterPasswordHint { get; set; } - public required string UserSymmetricKey { get; set; } + // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData + public string? UserSymmetricKey { get; set; } public required KeysRequestModel UserAsymmetricKeys { get; set; } - public required KdfType Kdf { get; set; } - public required int KdfIterations { get; set; } + // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData + public KdfType? Kdf { get; set; } + // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData + public int? KdfIterations { get; set; } public int? KdfMemory { get; set; } public int? KdfParallelism { get; set; } @@ -54,11 +62,13 @@ public User ToUser() { Email = Email, MasterPasswordHint = MasterPasswordHint, - Kdf = Kdf, - KdfIterations = KdfIterations, - KdfMemory = KdfMemory, - KdfParallelism = KdfParallelism, - Key = UserSymmetricKey, + Kdf = MasterPasswordUnlockData?.Kdf.KdfType ?? Kdf ?? throw new Exception($"{nameof(Kdf)} is required"), + KdfIterations = MasterPasswordUnlockData?.Kdf.Iterations ?? KdfIterations ?? throw new Exception($"{nameof(KdfIterations)} is required"), + KdfMemory = MasterPasswordUnlockData?.Kdf.Memory ?? KdfMemory, + KdfParallelism = MasterPasswordUnlockData?.Kdf.Parallelism ?? KdfParallelism, + // PM-28827 To be added when MasterPasswordSalt is added to the user column + // MasterPasswordSalt = MasterPasswordUnlockData?.Salt ?? Email.ToLower().Trim(), + Key = MasterPasswordUnlockData?.MasterKeyWrappedUserKey ?? UserSymmetricKey, }; UserAsymmetricKeys.ToUser(user); @@ -95,6 +105,15 @@ public RegisterFinishTokenType GetTokenType() public IEnumerable Validate(ValidationContext validationContext) { - return KdfSettingsValidator.Validate(Kdf, KdfIterations, KdfMemory, KdfParallelism); + var kdf = MasterPasswordUnlockData?.Kdf.KdfType ?? Kdf ?? throw new Exception($"{nameof(Kdf)} not found on RequestModel"); + var kdfIterations = MasterPasswordUnlockData?.Kdf.Iterations ?? KdfIterations ?? throw new Exception($"{nameof(KdfIterations)} not found on RequestModel"); + var kdfMemory = MasterPasswordUnlockData?.Kdf.Memory ?? KdfMemory; + var kdfParallelism = MasterPasswordUnlockData?.Kdf.Parallelism ?? KdfParallelism; + + // PM-28143 - Remove line below in favor of using the unlock data. + return KdfSettingsValidator.Validate(kdf, kdfIterations, kdfMemory, kdfParallelism); + + // PM-28143 - Uncomment + // return KdfSettingsValidator.Validate(MasterPasswordUnlockData); } } diff --git a/src/Core/Entities/User.cs b/src/Core/Entities/User.cs index 1ca660677914..75f35b7b32bf 100644 --- a/src/Core/Entities/User.cs +++ b/src/Core/Entities/User.cs @@ -7,8 +7,6 @@ using Bit.Core.Utilities; using Microsoft.AspNetCore.Identity; -#nullable enable - namespace Bit.Core.Entities; public class User : ITableObject, IStorableSubscriber, IRevisable, ITwoFactorProvidersUser @@ -51,7 +49,7 @@ public class User : ITableObject, IStorableSubscriber, IRevisable, ITwoFac public string? Key { get; set; } /// /// The raw public key, without a signature from the user's signature key. - /// + /// public string? PublicKey { get; set; } /// /// User key wrapped private key. @@ -102,6 +100,8 @@ public class User : ITableObject, IStorableSubscriber, IRevisable, ITwoFac public DateTime? LastKeyRotationDate { get; set; } public DateTime? LastEmailChangeDate { get; set; } public bool VerifyDevices { get; set; } = true; + // PM-28827 Uncomment below line. + // public string? MasterPasswordSalt { get; set; } public string GetMasterPasswordSalt() { diff --git a/src/Core/Utilities/KdfSettingsValidator.cs b/src/Core/Utilities/KdfSettingsValidator.cs index f89e8ddb66e4..34241eda9f94 100644 --- a/src/Core/Utilities/KdfSettingsValidator.cs +++ b/src/Core/Utilities/KdfSettingsValidator.cs @@ -36,6 +36,33 @@ public static IEnumerable Validate(KdfType kdfType, int kdfIte } } + public static IEnumerable Validate(MasterPasswordUnlockData masterPasswordUnlockData) + { + switch (masterPasswordUnlockData.Kdf.KdfType) + { + case KdfType.PBKDF2_SHA256: + if (!AuthConstants.PBKDF2_ITERATIONS.InsideRange(masterPasswordUnlockData.Kdf.Iterations)) + { + yield return new ValidationResult($"KDF iterations must be between {AuthConstants.PBKDF2_ITERATIONS.Min} and {AuthConstants.PBKDF2_ITERATIONS.Max}."); + } + break; + case KdfType.Argon2id: + if (!AuthConstants.ARGON2_ITERATIONS.InsideRange(masterPasswordUnlockData.Kdf.Iterations)) + { + yield return new ValidationResult($"Argon2 iterations must be between {AuthConstants.ARGON2_ITERATIONS.Min} and {AuthConstants.ARGON2_ITERATIONS.Max}."); + } + else if (!masterPasswordUnlockData.Kdf.Memory.HasValue || !AuthConstants.ARGON2_MEMORY.InsideRange(masterPasswordUnlockData.Kdf.Memory.Value)) + { + yield return new ValidationResult($"Argon2 memory must be between {AuthConstants.ARGON2_MEMORY.Min}mb and {AuthConstants.ARGON2_MEMORY.Max}mb."); + } + else if (!masterPasswordUnlockData.Kdf.Parallelism.HasValue || !AuthConstants.ARGON2_PARALLELISM.InsideRange(masterPasswordUnlockData.Kdf.Parallelism.Value)) + { + yield return new ValidationResult($"Argon2 parallelism must be between {AuthConstants.ARGON2_PARALLELISM.Min} and {AuthConstants.ARGON2_PARALLELISM.Max}."); + } + break; + } + } + public static IEnumerable Validate(KdfSettings settings) { return Validate(settings.KdfType, settings.Iterations, settings.Memory, settings.Parallelism); From 1535fa35d33afcd7320050dc6904f899f10fa73a Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Tue, 9 Dec 2025 17:08:54 -0500 Subject: [PATCH 02/44] feat(register): [PM-27084] Account Register Uses New Data Types - Fixed up reference to master password hash --- .../Accounts/RegisterFinishRequestModel.cs | 8 ++-- .../Controllers/AccountsController.cs | 40 ++++++++++++++----- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 92cafbfe7b7d..3ef3e8fb68e2 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -25,9 +25,9 @@ public class RegisterFinishRequestModel : IValidatableObject public MasterPasswordAuthenticationData? MasterPasswordAuthenticationData { get; set; } public MasterPasswordUnlockData? MasterPasswordUnlockData { get; set; } - // PM-28143 - Made to be optional as migrating to MasterPasswordUnlockData + // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData [StringLength(1000)] - public required string? MasterPasswordHash { get; set; } + public string? MasterPasswordHash { get; set; } [StringLength(50)] public string? MasterPasswordHint { get; set; } @@ -62,8 +62,8 @@ public User ToUser() { Email = Email, MasterPasswordHint = MasterPasswordHint, - Kdf = MasterPasswordUnlockData?.Kdf.KdfType ?? Kdf ?? throw new Exception($"{nameof(Kdf)} is required"), - KdfIterations = MasterPasswordUnlockData?.Kdf.Iterations ?? KdfIterations ?? throw new Exception($"{nameof(KdfIterations)} is required"), + Kdf = MasterPasswordUnlockData?.Kdf.KdfType ?? Kdf ?? throw new Exception("KdfType couldn't be found on either the MasterPasswordUnlockData or the Kdf property passed in."), + KdfIterations = MasterPasswordUnlockData?.Kdf.Iterations ?? KdfIterations ?? throw new Exception("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in."), KdfMemory = MasterPasswordUnlockData?.Kdf.Memory ?? KdfMemory, KdfParallelism = MasterPasswordUnlockData?.Kdf.Parallelism ?? KdfParallelism, // PM-28827 To be added when MasterPasswordSalt is added to the user column diff --git a/src/Identity/Controllers/AccountsController.cs b/src/Identity/Controllers/AccountsController.cs index b7d4342c1b8c..57155b640af0 100644 --- a/src/Identity/Controllers/AccountsController.cs +++ b/src/Identity/Controllers/AccountsController.cs @@ -145,37 +145,59 @@ public async Task PostRegisterVerificationEmailClicked([FromBody] [HttpPost("register/finish")] public async Task PostRegisterFinish([FromBody] RegisterFinishRequestModel model) { - var user = model.ToUser(); + User user; + + try + { + user = model.ToUser(); + } + catch (Exception e) + { + throw new BadRequestException(e.Message); + } // Users will either have an emailed token or an email verification token - not both. IdentityResult identityResult = null; + // PM-28143 - Just use the MasterPasswordAuthenticationData.MasterPasswordAuthenticationHash + string masterPasswordHash = model.MasterPasswordAuthenticationData?.MasterPasswordAuthenticationHash + ?? model.MasterPasswordHash ?? throw new BadRequestException("MasterPasswordHash couldn't be found on either the MasterPasswordAuthenticationData or the MasterPasswordHash property passed in."); + switch (model.GetTokenType()) { case RegisterFinishTokenType.EmailVerification: - identityResult = - await _registerUserCommand.RegisterUserViaEmailVerificationToken(user, model.MasterPasswordHash, - model.EmailVerificationToken); - + identityResult = await _registerUserCommand.RegisterUserViaEmailVerificationToken( + user, + masterPasswordHash, + model.EmailVerificationToken); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.OrganizationInvite: - identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken(user, model.MasterPasswordHash, + identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken( + user, + masterPasswordHash, model.OrgInviteToken, model.OrganizationUserId); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.OrgSponsoredFreeFamilyPlan: - identityResult = await _registerUserCommand.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(user, model.MasterPasswordHash, model.OrgSponsoredFreeFamilyPlanToken); + identityResult = await _registerUserCommand.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken( + user, + masterPasswordHash, + model.OrgSponsoredFreeFamilyPlanToken); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.EmergencyAccessInvite: Debug.Assert(model.AcceptEmergencyAccessId.HasValue); - identityResult = await _registerUserCommand.RegisterUserViaAcceptEmergencyAccessInviteToken(user, model.MasterPasswordHash, + identityResult = await _registerUserCommand.RegisterUserViaAcceptEmergencyAccessInviteToken( + user, + masterPasswordHash, model.AcceptEmergencyAccessInviteToken, model.AcceptEmergencyAccessId.Value); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.ProviderInvite: Debug.Assert(model.ProviderUserId.HasValue); - identityResult = await _registerUserCommand.RegisterUserViaProviderInviteToken(user, model.MasterPasswordHash, + identityResult = await _registerUserCommand.RegisterUserViaProviderInviteToken( + user, + masterPasswordHash, model.ProviderInviteToken, model.ProviderUserId.Value); return ProcessRegistrationResult(identityResult, user); From 9000474b5a2934aa4fb76d95de24c693a4686e2a Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Wed, 10 Dec 2025 17:47:12 -0500 Subject: [PATCH 03/44] fix(register): [PM-27084] Account Register Uses New Data Types - Added more comments and fixed up some long lines. --- .../Accounts/RegisterFinishRequestModel.cs | 24 ++++++++++++------- src/Core/Utilities/KdfSettingsValidator.cs | 2 ++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 3ef3e8fb68e2..7cc217cf8472 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -25,23 +25,25 @@ public class RegisterFinishRequestModel : IValidatableObject public MasterPasswordAuthenticationData? MasterPasswordAuthenticationData { get; set; } public MasterPasswordUnlockData? MasterPasswordUnlockData { get; set; } - // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData + // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData) [StringLength(1000)] public string? MasterPasswordHash { get; set; } [StringLength(50)] public string? MasterPasswordHint { get; set; } - // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData + // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData) public string? UserSymmetricKey { get; set; } public required KeysRequestModel UserAsymmetricKeys { get; set; } - // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData + // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData) public KdfType? Kdf { get; set; } - // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData + // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData) public int? KdfIterations { get; set; } + // PM-28143 - Remove line below public int? KdfMemory { get; set; } + // PM-28143 - Remove line below public int? KdfParallelism { get; set; } public Guid? OrganizationUserId { get; set; } @@ -105,10 +107,16 @@ public RegisterFinishTokenType GetTokenType() public IEnumerable Validate(ValidationContext validationContext) { - var kdf = MasterPasswordUnlockData?.Kdf.KdfType ?? Kdf ?? throw new Exception($"{nameof(Kdf)} not found on RequestModel"); - var kdfIterations = MasterPasswordUnlockData?.Kdf.Iterations ?? KdfIterations ?? throw new Exception($"{nameof(KdfIterations)} not found on RequestModel"); - var kdfMemory = MasterPasswordUnlockData?.Kdf.Memory ?? KdfMemory; - var kdfParallelism = MasterPasswordUnlockData?.Kdf.Parallelism ?? KdfParallelism; + var kdf = MasterPasswordUnlockData?.Kdf.KdfType + ?? Kdf + ?? throw new Exception($"{nameof(Kdf)} not found on RequestModel"); + var kdfIterations = MasterPasswordUnlockData?.Kdf.Iterations + ?? KdfIterations + ?? throw new Exception($"{nameof(KdfIterations)} not found on RequestModel"); + var kdfMemory = MasterPasswordUnlockData?.Kdf.Memory + ?? KdfMemory; + var kdfParallelism = MasterPasswordUnlockData?.Kdf.Parallelism + ?? KdfParallelism; // PM-28143 - Remove line below in favor of using the unlock data. return KdfSettingsValidator.Validate(kdf, kdfIterations, kdfMemory, kdfParallelism); diff --git a/src/Core/Utilities/KdfSettingsValidator.cs b/src/Core/Utilities/KdfSettingsValidator.cs index 34241eda9f94..be444d826da3 100644 --- a/src/Core/Utilities/KdfSettingsValidator.cs +++ b/src/Core/Utilities/KdfSettingsValidator.cs @@ -6,6 +6,7 @@ namespace Bit.Core.Utilities; public static class KdfSettingsValidator { + // PM-28143 - Remove below when fixing ticket public static IEnumerable Validate(KdfType kdfType, int kdfIterations, int? kdfMemory, int? kdfParallelism) { switch (kdfType) @@ -36,6 +37,7 @@ public static IEnumerable Validate(KdfType kdfType, int kdfIte } } + // PM-28143 - Will be used in the referenced ticket. public static IEnumerable Validate(MasterPasswordUnlockData masterPasswordUnlockData) { switch (masterPasswordUnlockData.Kdf.KdfType) From d7ddf2ecaf62b13f3b845a03ed6cd4aa0a87cfaf Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Thu, 11 Dec 2025 12:22:28 -0500 Subject: [PATCH 04/44] fix(register): [PM-27084] Account Register Uses New Data Types - Accounts controller no longer nullish allowed. --- .../Accounts/RegisterFinishRequestModel.cs | 10 +++- .../Controllers/AccountsController.cs | 51 +++++++++++++------ 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 7cc217cf8472..304a90f50b16 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -1,5 +1,4 @@ -#nullable enable -using Bit.Core.Entities; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Utilities; @@ -107,14 +106,21 @@ public RegisterFinishTokenType GetTokenType() public IEnumerable Validate(ValidationContext validationContext) { + // PM-28143 - Remove line below var kdf = MasterPasswordUnlockData?.Kdf.KdfType ?? Kdf ?? throw new Exception($"{nameof(Kdf)} not found on RequestModel"); + + // PM-28143 - Remove line below var kdfIterations = MasterPasswordUnlockData?.Kdf.Iterations ?? KdfIterations ?? throw new Exception($"{nameof(KdfIterations)} not found on RequestModel"); + + // PM-28143 - Remove line below var kdfMemory = MasterPasswordUnlockData?.Kdf.Memory ?? KdfMemory; + + // PM-28143 - Remove line below var kdfParallelism = MasterPasswordUnlockData?.Kdf.Parallelism ?? KdfParallelism; diff --git a/src/Identity/Controllers/AccountsController.cs b/src/Identity/Controllers/AccountsController.cs index 57155b640af0..805a98c543ca 100644 --- a/src/Identity/Controllers/AccountsController.cs +++ b/src/Identity/Controllers/AccountsController.cs @@ -1,8 +1,4 @@ -// FIXME: Update this file to be null safe and then delete the line below -#nullable disable - -using System.Diagnostics; -using System.Text; +using System.Text; using Bit.Core; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models.Api.Request.Accounts; @@ -42,7 +38,7 @@ public class AccountsController : Controller private readonly IFeatureService _featureService; private readonly IDataProtectorTokenFactory _registrationEmailVerificationTokenDataFactory; - private readonly byte[] _defaultKdfHmacKey = null; + private readonly byte[]? _defaultKdfHmacKey = null; private static readonly List _defaultKdfResults = [ // The first result (index 0) should always return the "normal" default. @@ -157,7 +153,7 @@ public async Task PostRegisterFinish([FromBody] Reg } // Users will either have an emailed token or an email verification token - not both. - IdentityResult identityResult = null; + IdentityResult? identityResult = null; // PM-28143 - Just use the MasterPasswordAuthenticationData.MasterPasswordAuthenticationHash string masterPasswordHash = model.MasterPasswordAuthenticationData?.MasterPasswordAuthenticationHash @@ -166,41 +162,64 @@ public async Task PostRegisterFinish([FromBody] Reg switch (model.GetTokenType()) { case RegisterFinishTokenType.EmailVerification: + if (string.IsNullOrEmpty(model.EmailVerificationToken)) + throw new BadRequestException("Email verification token absent when processing an email token."); + identityResult = await _registerUserCommand.RegisterUserViaEmailVerificationToken( user, masterPasswordHash, model.EmailVerificationToken); return ProcessRegistrationResult(identityResult, user); + case RegisterFinishTokenType.OrganizationInvite: + if (string.IsNullOrEmpty(model.OrgInviteToken)) + throw new BadRequestException("Organization invite token absent when processing an email token."); + identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken( user, masterPasswordHash, - model.OrgInviteToken, model.OrganizationUserId); - + model.OrgInviteToken, + model.OrganizationUserId); return ProcessRegistrationResult(identityResult, user); + case RegisterFinishTokenType.OrgSponsoredFreeFamilyPlan: + if (string.IsNullOrEmpty(model.OrgSponsoredFreeFamilyPlanToken)) + throw new BadRequestException("Organization sponsored free family plan token absent when processing an email token."); + identityResult = await _registerUserCommand.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken( user, masterPasswordHash, model.OrgSponsoredFreeFamilyPlanToken); - return ProcessRegistrationResult(identityResult, user); + case RegisterFinishTokenType.EmergencyAccessInvite: - Debug.Assert(model.AcceptEmergencyAccessId.HasValue); + if (string.IsNullOrEmpty(model.AcceptEmergencyAccessInviteToken)) + throw new BadRequestException("Accept emergency access invite token absent when processing an email token."); + + if (model.AcceptEmergencyAccessId == null || model.AcceptEmergencyAccessId == Guid.Empty) + throw new BadRequestException("Accept emergency access id absent when processing an email token."); + identityResult = await _registerUserCommand.RegisterUserViaAcceptEmergencyAccessInviteToken( user, masterPasswordHash, - model.AcceptEmergencyAccessInviteToken, model.AcceptEmergencyAccessId.Value); - + model.AcceptEmergencyAccessInviteToken, + model.AcceptEmergencyAccessId.Value); return ProcessRegistrationResult(identityResult, user); + case RegisterFinishTokenType.ProviderInvite: - Debug.Assert(model.ProviderUserId.HasValue); + if (string.IsNullOrEmpty(model.ProviderInviteToken)) + throw new BadRequestException("Provider invite token absent when processing an email token."); + + if (model.ProviderUserId == null || model.ProviderUserId == Guid.Empty) + throw new BadRequestException("Provider user id absent when processing an email token."); + identityResult = await _registerUserCommand.RegisterUserViaProviderInviteToken( user, masterPasswordHash, - model.ProviderInviteToken, model.ProviderUserId.Value); - + model.ProviderInviteToken, + model.ProviderUserId.Value); return ProcessRegistrationResult(identityResult, user); + default: throw new BadRequestException("Invalid registration finish request"); } From 08f3acdab601d1f3c04065c3fbe754a4978ab3ae Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Thu, 11 Dec 2025 12:25:39 -0500 Subject: [PATCH 05/44] docs(register): [PM-27084] Account Register Uses New Data Types - Made thrown error messages more appropriate --- src/Identity/Controllers/AccountsController.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Identity/Controllers/AccountsController.cs b/src/Identity/Controllers/AccountsController.cs index 805a98c543ca..a521816985fe 100644 --- a/src/Identity/Controllers/AccountsController.cs +++ b/src/Identity/Controllers/AccountsController.cs @@ -163,7 +163,7 @@ public async Task PostRegisterFinish([FromBody] Reg { case RegisterFinishTokenType.EmailVerification: if (string.IsNullOrEmpty(model.EmailVerificationToken)) - throw new BadRequestException("Email verification token absent when processing an email token."); + throw new BadRequestException("Email verification token absent when processing register/finish."); identityResult = await _registerUserCommand.RegisterUserViaEmailVerificationToken( user, @@ -173,7 +173,7 @@ public async Task PostRegisterFinish([FromBody] Reg case RegisterFinishTokenType.OrganizationInvite: if (string.IsNullOrEmpty(model.OrgInviteToken)) - throw new BadRequestException("Organization invite token absent when processing an email token."); + throw new BadRequestException("Organization invite token absent when processing register/finish."); identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken( user, @@ -184,7 +184,7 @@ public async Task PostRegisterFinish([FromBody] Reg case RegisterFinishTokenType.OrgSponsoredFreeFamilyPlan: if (string.IsNullOrEmpty(model.OrgSponsoredFreeFamilyPlanToken)) - throw new BadRequestException("Organization sponsored free family plan token absent when processing an email token."); + throw new BadRequestException("Organization sponsored free family plan token absent when processing register/finish."); identityResult = await _registerUserCommand.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken( user, @@ -194,10 +194,10 @@ public async Task PostRegisterFinish([FromBody] Reg case RegisterFinishTokenType.EmergencyAccessInvite: if (string.IsNullOrEmpty(model.AcceptEmergencyAccessInviteToken)) - throw new BadRequestException("Accept emergency access invite token absent when processing an email token."); + throw new BadRequestException("Accept emergency access invite token absent when processing register/finish."); if (model.AcceptEmergencyAccessId == null || model.AcceptEmergencyAccessId == Guid.Empty) - throw new BadRequestException("Accept emergency access id absent when processing an email token."); + throw new BadRequestException("Accept emergency access id absent when processing register/finish."); identityResult = await _registerUserCommand.RegisterUserViaAcceptEmergencyAccessInviteToken( user, @@ -208,10 +208,10 @@ public async Task PostRegisterFinish([FromBody] Reg case RegisterFinishTokenType.ProviderInvite: if (string.IsNullOrEmpty(model.ProviderInviteToken)) - throw new BadRequestException("Provider invite token absent when processing an email token."); + throw new BadRequestException("Provider invite token absent when processing register/finish."); if (model.ProviderUserId == null || model.ProviderUserId == Guid.Empty) - throw new BadRequestException("Provider user id absent when processing an email token."); + throw new BadRequestException("Provider user id absent when processing register/finish."); identityResult = await _registerUserCommand.RegisterUserViaProviderInviteToken( user, From f47dac977d223885740ed99798858c3d46ea912c Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Thu, 11 Dec 2025 17:32:54 -0500 Subject: [PATCH 06/44] test(register): [PM-27084] Account Register Uses New Data Types - Added tests. --- .../Controllers/AccountsControllerTests.cs | 227 ++++++++++++++++++ 1 file changed, 227 insertions(+) diff --git a/test/Identity.Test/Controllers/AccountsControllerTests.cs b/test/Identity.Test/Controllers/AccountsControllerTests.cs index 42e033bdd71a..8f8b38f9eaec 100644 --- a/test/Identity.Test/Controllers/AccountsControllerTests.cs +++ b/test/Identity.Test/Controllers/AccountsControllerTests.cs @@ -9,6 +9,7 @@ using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; +using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Models.Data; using Bit.Core.Repositories; using Bit.Core.Services; @@ -590,6 +591,232 @@ public async Task PostRegisterVerificationEmailClicked_WhenTokenIsValidButExisti await Assert.ThrowsAsync(() => _sut.PostRegisterVerificationEmailClicked(requestModel)); } + [Theory, BitAutoData] + public async Task PostRegisterFinish_EmailVerification_BothDataForms_ProduceEquivalentOutcomes( + string email, + string emailVerificationToken, + string masterPasswordHash, + string masterKeyWrappedUserKey, + string publicKey, + string encryptedPrivateKey) + { + // Arrange: new-form model (MasterPasswordAuthenticationData + MasterPasswordUnlockData) + var newModel = new RegisterFinishRequestModel + { + Email = email, + EmailVerificationToken = emailVerificationToken, + MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData + { + Kdf = new KdfSettings + { + KdfType = KdfType.Argon2id, + Iterations = AuthConstants.ARGON2_ITERATIONS.Default, + Memory = AuthConstants.ARGON2_MEMORY.Default, + Parallelism = AuthConstants.ARGON2_PARALLELISM.Default + }, + MasterPasswordAuthenticationHash = masterPasswordHash, + Salt = email // salt choice is not validated here during registration + }, + MasterPasswordUnlockData = new MasterPasswordUnlockData + { + Kdf = new KdfSettings + { + KdfType = KdfType.Argon2id, + Iterations = AuthConstants.ARGON2_ITERATIONS.Default, + Memory = AuthConstants.ARGON2_MEMORY.Default, + Parallelism = AuthConstants.ARGON2_PARALLELISM.Default + }, + MasterKeyWrappedUserKey = masterKeyWrappedUserKey, + Salt = email + }, + UserAsymmetricKeys = new KeysRequestModel + { + PublicKey = publicKey, + EncryptedPrivateKey = encryptedPrivateKey + } + }; + + // Arrange: legacy-form model (MasterPasswordHash + legacy KDF + UserSymmetricKey) + var legacyModel = new RegisterFinishRequestModel + { + Email = email, + EmailVerificationToken = emailVerificationToken, + MasterPasswordHash = masterPasswordHash, + Kdf = KdfType.Argon2id, + KdfIterations = AuthConstants.ARGON2_ITERATIONS.Default, + KdfMemory = AuthConstants.ARGON2_MEMORY.Default, + KdfParallelism = AuthConstants.ARGON2_PARALLELISM.Default, + UserSymmetricKey = masterKeyWrappedUserKey, + UserAsymmetricKeys = new KeysRequestModel + { + PublicKey = publicKey, + EncryptedPrivateKey = encryptedPrivateKey + } + }; + + var newUser = newModel.ToUser(); + var legacyUser = legacyModel.ToUser(); + + _registerUserCommand + .RegisterUserViaEmailVerificationToken(Arg.Any(), masterPasswordHash, emailVerificationToken) + .Returns(Task.FromResult(IdentityResult.Success)); + + // Act: call with new form + var newResult = await _sut.PostRegisterFinish(newModel); + // Act: call with legacy form + var legacyResult = await _sut.PostRegisterFinish(legacyModel); + + // Assert: outcomes are identical in effect (success response) + Assert.NotNull(newResult); + Assert.NotNull(legacyResult); + + // Assert: effective users are equivalent + Assert.Equal(legacyUser.Email, newUser.Email); + Assert.Equal(legacyUser.MasterPasswordHint, newUser.MasterPasswordHint); + Assert.Equal(legacyUser.Kdf, newUser.Kdf); + Assert.Equal(legacyUser.KdfIterations, newUser.KdfIterations); + Assert.Equal(legacyUser.KdfMemory, newUser.KdfMemory); + Assert.Equal(legacyUser.KdfParallelism, newUser.KdfParallelism); + Assert.Equal(legacyUser.Key, newUser.Key); + Assert.Equal(legacyUser.PublicKey, newUser.PublicKey); + Assert.Equal(legacyUser.PrivateKey, newUser.PrivateKey); + + // Assert: hash forwarded identically from both inputs + await _registerUserCommand.Received(2).RegisterUserViaEmailVerificationToken( + Arg.Is(u => + u.Email == newUser.Email && + u.Kdf == newUser.Kdf && + u.KdfIterations == newUser.KdfIterations && + u.KdfMemory == newUser.KdfMemory && + u.KdfParallelism == newUser.KdfParallelism && + u.Key == newUser.Key), + masterPasswordHash, + emailVerificationToken); + + await _registerUserCommand.Received(2).RegisterUserViaEmailVerificationToken( + Arg.Is(u => + u.Email == legacyUser.Email && + u.Kdf == legacyUser.Kdf && + u.KdfIterations == legacyUser.KdfIterations && + u.KdfMemory == legacyUser.KdfMemory && + u.KdfParallelism == legacyUser.KdfParallelism && + u.Key == legacyUser.Key), + masterPasswordHash, + emailVerificationToken); + } + + [Theory, BitAutoData] + public async Task PostRegisterFinish_OrgInvite_BothDataForms_ProduceEquivalentOutcomes( + string email, + string orgInviteToken, + Guid organizationUserId, + string masterPasswordHash, + string masterKeyWrappedUserKey, + string publicKey, + string encryptedPrivateKey) + { + // Arrange: new-form model (MasterPasswordAuthenticationData + MasterPasswordUnlockData) + var newModel = new RegisterFinishRequestModel + { + Email = email, + OrgInviteToken = orgInviteToken, + OrganizationUserId = organizationUserId, + MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData + { + Kdf = new KdfSettings + { + KdfType = KdfType.PBKDF2_SHA256, + Iterations = AuthConstants.PBKDF2_ITERATIONS.Default + }, + MasterPasswordAuthenticationHash = masterPasswordHash, + Salt = email + }, + MasterPasswordUnlockData = new MasterPasswordUnlockData + { + Kdf = new KdfSettings + { + KdfType = KdfType.PBKDF2_SHA256, + Iterations = AuthConstants.PBKDF2_ITERATIONS.Default + }, + MasterKeyWrappedUserKey = masterKeyWrappedUserKey, + Salt = email + }, + UserAsymmetricKeys = new KeysRequestModel + { + PublicKey = publicKey, + EncryptedPrivateKey = encryptedPrivateKey + } + }; + + // Arrange: legacy-form model (MasterPasswordHash + legacy KDF + UserSymmetricKey) + var legacyModel = new RegisterFinishRequestModel + { + Email = email, + OrgInviteToken = orgInviteToken, + OrganizationUserId = organizationUserId, + MasterPasswordHash = masterPasswordHash, + Kdf = KdfType.PBKDF2_SHA256, + KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default, + UserSymmetricKey = masterKeyWrappedUserKey, + UserAsymmetricKeys = new KeysRequestModel + { + PublicKey = publicKey, + EncryptedPrivateKey = encryptedPrivateKey + } + }; + + var newUser = newModel.ToUser(); + var legacyUser = legacyModel.ToUser(); + + _registerUserCommand + .RegisterUserViaOrganizationInviteToken(Arg.Any(), masterPasswordHash, orgInviteToken, organizationUserId) + .Returns(Task.FromResult(IdentityResult.Success)); + + // Act + var newResult = await _sut.PostRegisterFinish(newModel); + var legacyResult = await _sut.PostRegisterFinish(legacyModel); + + // Assert success + Assert.NotNull(newResult); + Assert.NotNull(legacyResult); + + // Assert: effective users are equivalent + Assert.Equal(legacyUser.Email, newUser.Email); + Assert.Equal(legacyUser.MasterPasswordHint, newUser.MasterPasswordHint); + Assert.Equal(legacyUser.Kdf, newUser.Kdf); + Assert.Equal(legacyUser.KdfIterations, newUser.KdfIterations); + Assert.Equal(legacyUser.KdfMemory, newUser.KdfMemory); + Assert.Equal(legacyUser.KdfParallelism, newUser.KdfParallelism); + Assert.Equal(legacyUser.Key, newUser.Key); + Assert.Equal(legacyUser.PublicKey, newUser.PublicKey); + Assert.Equal(legacyUser.PrivateKey, newUser.PrivateKey); + + // Assert: hash forwarded identically from both inputs + await _registerUserCommand.Received(2).RegisterUserViaOrganizationInviteToken( + Arg.Is(u => + u.Email == newUser.Email && + u.Kdf == newUser.Kdf && + u.KdfIterations == newUser.KdfIterations && + u.KdfMemory == newUser.KdfMemory && + u.KdfParallelism == newUser.KdfParallelism && + u.Key == newUser.Key), + masterPasswordHash, + orgInviteToken, + organizationUserId); + + await _registerUserCommand.Received(2).RegisterUserViaOrganizationInviteToken( + Arg.Is(u => + u.Email == legacyUser.Email && + u.Kdf == legacyUser.Kdf && + u.KdfIterations == legacyUser.KdfIterations && + u.KdfMemory == legacyUser.KdfMemory && + u.KdfParallelism == legacyUser.KdfParallelism && + u.Key == legacyUser.Key), + masterPasswordHash, + orgInviteToken, + organizationUserId); + } + private void SetDefaultKdfHmacKey(byte[]? newKey) { var fieldInfo = typeof(AccountsController).GetField("_defaultKdfHmacKey", BindingFlags.NonPublic | BindingFlags.Instance); From bb2ba1468bc49ad21d228a55b3acb25a619e9830 Mon Sep 17 00:00:00 2001 From: Patrick-Pimentel-Bitwarden Date: Thu, 11 Dec 2025 17:44:41 -0500 Subject: [PATCH 07/44] Update src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> --- .../Models/Api/Request/Accounts/RegisterFinishRequestModel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 304a90f50b16..b185f8db3ca3 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -65,6 +65,7 @@ public User ToUser() MasterPasswordHint = MasterPasswordHint, Kdf = MasterPasswordUnlockData?.Kdf.KdfType ?? Kdf ?? throw new Exception("KdfType couldn't be found on either the MasterPasswordUnlockData or the Kdf property passed in."), KdfIterations = MasterPasswordUnlockData?.Kdf.Iterations ?? KdfIterations ?? throw new Exception("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in."), + // KdfMemory and KdfParallelism are optional (only used for Argon2id) KdfMemory = MasterPasswordUnlockData?.Kdf.Memory ?? KdfMemory, KdfParallelism = MasterPasswordUnlockData?.Kdf.Parallelism ?? KdfParallelism, // PM-28827 To be added when MasterPasswordSalt is added to the user column From b33e1aced24a3d231dedacf0e5bd4f74135a8850 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Thu, 11 Dec 2025 17:49:17 -0500 Subject: [PATCH 08/44] docs(register): [PM-27084] Account Register Uses New Data Types - Added comments for tests. --- test/Identity.Test/Controllers/AccountsControllerTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/Identity.Test/Controllers/AccountsControllerTests.cs b/test/Identity.Test/Controllers/AccountsControllerTests.cs index 8f8b38f9eaec..8d37ee10d7d7 100644 --- a/test/Identity.Test/Controllers/AccountsControllerTests.cs +++ b/test/Identity.Test/Controllers/AccountsControllerTests.cs @@ -591,6 +591,8 @@ public async Task PostRegisterVerificationEmailClicked_WhenTokenIsValidButExisti await Assert.ThrowsAsync(() => _sut.PostRegisterVerificationEmailClicked(requestModel)); } + // PM-28143 - When removing the old properties, update this test to just test the new properties working + // as expected. [Theory, BitAutoData] public async Task PostRegisterFinish_EmailVerification_BothDataForms_ProduceEquivalentOutcomes( string email, @@ -705,6 +707,8 @@ await _registerUserCommand.Received(2).RegisterUserViaEmailVerificationToken( emailVerificationToken); } + // PM-28143 - When removing the old properties, update this test to just test the new properties working + // as expected. [Theory, BitAutoData] public async Task PostRegisterFinish_OrgInvite_BothDataForms_ProduceEquivalentOutcomes( string email, From c21d7b43df211501ac9a39100bd00b737384adbe Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Tue, 16 Dec 2025 10:38:15 -0500 Subject: [PATCH 09/44] test(register): [PM-27084] Account Register Uses New Data Types - Test additions to make sure properties that were once required are still required. --- .../Accounts/RegisterFinishRequestModel.cs | 10 +- .../Controllers/AccountsControllerTests.cs | 219 ++++++++++++++++++ 2 files changed, 226 insertions(+), 3 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index b185f8db3ca3..1fa78390fdce 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -24,14 +24,18 @@ public class RegisterFinishRequestModel : IValidatableObject public MasterPasswordAuthenticationData? MasterPasswordAuthenticationData { get; set; } public MasterPasswordUnlockData? MasterPasswordUnlockData { get; set; } - // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData) + // PM-28143 - Remove property below (made optional during migration to MasterPasswordUnlockData) [StringLength(1000)] + // Made optional but there will still be a thrown error if it does not exist either here or + // in the MasterPasswordAuthenticationData. public string? MasterPasswordHash { get; set; } [StringLength(50)] public string? MasterPasswordHint { get; set; } - // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData) + // PM-28143 - Remove property below (made optional during migration to MasterPasswordUnlockData) + // Made optional but there will still be a thrown error if it does not exist either here or + // in the MasterPasswordAuthenticationData. public string? UserSymmetricKey { get; set; } public required KeysRequestModel UserAsymmetricKeys { get; set; } @@ -70,7 +74,7 @@ public User ToUser() KdfParallelism = MasterPasswordUnlockData?.Kdf.Parallelism ?? KdfParallelism, // PM-28827 To be added when MasterPasswordSalt is added to the user column // MasterPasswordSalt = MasterPasswordUnlockData?.Salt ?? Email.ToLower().Trim(), - Key = MasterPasswordUnlockData?.MasterKeyWrappedUserKey ?? UserSymmetricKey, + Key = MasterPasswordUnlockData?.MasterKeyWrappedUserKey ?? UserSymmetricKey ?? throw new Exception("MasterKeyWrappedUserKey couldn't be found on either the MasterPasswordUnlockData or the UserSymmetricKey property passed in."), }; UserAsymmetricKeys.ToUser(user); diff --git a/test/Identity.Test/Controllers/AccountsControllerTests.cs b/test/Identity.Test/Controllers/AccountsControllerTests.cs index 8d37ee10d7d7..468fed55cdb6 100644 --- a/test/Identity.Test/Controllers/AccountsControllerTests.cs +++ b/test/Identity.Test/Controllers/AccountsControllerTests.cs @@ -821,6 +821,225 @@ await _registerUserCommand.Received(2).RegisterUserViaOrganizationInviteToken( organizationUserId); } + [Theory, BitAutoData] + public async Task PostRegisterFinish_NewForm_UsesUnlockDataForKdfAndKey_WhenRootFieldsNull( + string email, + string emailVerificationToken, + string masterPasswordHash, + string masterKeyWrappedUserKey, + int iterations, + string publicKey, + string encryptedPrivateKey) + { + // Arrange: Provide only unlock-data KDF + key; leave root KDF fields null + var unlockKdf = new KdfSettings + { + KdfType = KdfType.PBKDF2_SHA256, + Iterations = iterations + }; + + var model = new RegisterFinishRequestModel + { + Email = email, + EmailVerificationToken = emailVerificationToken, + MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData + { + // present but not used by ToUser for KDF/Key + Kdf = new KdfSettings { KdfType = KdfType.Argon2id, Iterations = iterations }, + MasterPasswordAuthenticationHash = masterPasswordHash, + Salt = email + }, + MasterPasswordUnlockData = new MasterPasswordUnlockData + { + Kdf = unlockKdf, + MasterKeyWrappedUserKey = masterKeyWrappedUserKey, + Salt = email + }, + // root KDF fields intentionally null + Kdf = null, + KdfIterations = null, + UserAsymmetricKeys = new KeysRequestModel + { + PublicKey = publicKey, + EncryptedPrivateKey = encryptedPrivateKey + } + }; + + _registerUserCommand + .RegisterUserViaEmailVerificationToken(Arg.Any(), masterPasswordHash, emailVerificationToken) + .Returns(Task.FromResult(IdentityResult.Success)); + + // Act + var _ = await _sut.PostRegisterFinish(model); + + // Assert: The user passed to command uses unlock-data values + await _registerUserCommand.Received(1).RegisterUserViaEmailVerificationToken( + Arg.Is(u => + u.Email == email && + u.Kdf == unlockKdf.KdfType && + u.KdfIterations == unlockKdf.Iterations && + u.Key == masterKeyWrappedUserKey), + masterPasswordHash, + emailVerificationToken); + } + + [Theory, BitAutoData] + public async Task PostRegisterFinish_LegacyForm_UsesRootFields_WhenUnlockDataNull( + string email, + string emailVerificationToken, + string masterPasswordHash, + string legacyKey, + string publicKey, + string encryptedPrivateKey) + { + // Arrange: Provide only legacy root KDF + key; no unlock-data provided + var model = new RegisterFinishRequestModel + { + Email = email, + EmailVerificationToken = emailVerificationToken, + MasterPasswordHash = masterPasswordHash, + Kdf = KdfType.PBKDF2_SHA256, + KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default, + UserSymmetricKey = legacyKey, + MasterPasswordUnlockData = null, + UserAsymmetricKeys = new KeysRequestModel + { + PublicKey = publicKey, + EncryptedPrivateKey = encryptedPrivateKey + } + }; + + _registerUserCommand + .RegisterUserViaEmailVerificationToken(Arg.Any(), masterPasswordHash, emailVerificationToken) + .Returns(Task.FromResult(IdentityResult.Success)); + + // Act + var _ = await _sut.PostRegisterFinish(model); + + // Assert: The user passed to command uses root values + await _registerUserCommand.Received(1).RegisterUserViaEmailVerificationToken( + Arg.Is(u => + u.Email == email && + u.Kdf == KdfType.PBKDF2_SHA256 && + u.KdfIterations == AuthConstants.PBKDF2_ITERATIONS.Default && + u.Key == legacyKey), + masterPasswordHash, + emailVerificationToken); + } + + [Theory, BitAutoData] + public async Task PostRegisterFinish_WhenKdfMissingInAllSources_ShouldReturnBadRequest( + string email, + string emailVerificationToken, + string masterPasswordHash, + string masterKeyWrappedUserKey, + int iterations, + string publicKey, + string encryptedPrivateKey) + { + // Arrange: No KDF at root, and no unlock-data present + var model = new RegisterFinishRequestModel + { + Email = email, + EmailVerificationToken = emailVerificationToken, + MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData + { + // present but ToUser does not source KDF from here + Kdf = new KdfSettings { KdfType = KdfType.Argon2id, Iterations = iterations }, + MasterPasswordAuthenticationHash = masterPasswordHash, + Salt = email + }, + MasterPasswordUnlockData = null, + Kdf = null, + KdfIterations = iterations, + UserSymmetricKey = masterKeyWrappedUserKey, + UserAsymmetricKeys = new KeysRequestModel + { + PublicKey = publicKey, + EncryptedPrivateKey = encryptedPrivateKey + } + }; + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => _sut.PostRegisterFinish(model)); + Assert.Equal("KdfType couldn't be found on either the MasterPasswordUnlockData or the Kdf property passed in.", ex.Message); + } + + [Theory, BitAutoData] + public async Task PostRegisterFinish_WhenKdfIterationsMissingInAllSources_ShouldReturnBadRequest( + string email, + string emailVerificationToken, + string masterPasswordHash, + string masterKeyWrappedUserKey, + KdfType kdfType, + string publicKey, + string encryptedPrivateKey) + { + // Arrange: No KdfIterations at root, and no unlock-data present + var model = new RegisterFinishRequestModel + { + Email = email, + EmailVerificationToken = emailVerificationToken, + MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData + { + // present but ToUser does not source iterations from here + Kdf = new KdfSettings { KdfType = kdfType, Iterations = AuthConstants.PBKDF2_ITERATIONS.Default }, + MasterPasswordAuthenticationHash = masterPasswordHash, + Salt = email + }, + MasterPasswordUnlockData = null, + Kdf = kdfType, + KdfIterations = null, + UserSymmetricKey = masterKeyWrappedUserKey, + UserAsymmetricKeys = new KeysRequestModel + { + PublicKey = publicKey, + EncryptedPrivateKey = encryptedPrivateKey + } + }; + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => _sut.PostRegisterFinish(model)); + Assert.Equal("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in.", ex.Message); + } + + [Theory, BitAutoData] + public async Task PostRegisterFinish_WhenKeyMissingInAllSources_ShouldReturnBadRequest( + string email, + string emailVerificationToken, + string masterPasswordHash, + int iterations, + KdfType kdfType, + string publicKey, + string encryptedPrivateKey) + { + // Arrange: No key at root, and no unlock-data present + var model = new RegisterFinishRequestModel + { + Email = email, + EmailVerificationToken = emailVerificationToken, + MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData + { + Kdf = new KdfSettings { KdfType = kdfType, Iterations = iterations }, + MasterPasswordAuthenticationHash = masterPasswordHash, + Salt = email + }, + MasterPasswordUnlockData = null, + Kdf = kdfType, + KdfIterations = iterations, + UserSymmetricKey = null, + UserAsymmetricKeys = new KeysRequestModel + { + PublicKey = publicKey, + EncryptedPrivateKey = encryptedPrivateKey + } + }; + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => _sut.PostRegisterFinish(model)); + Assert.Equal("MasterKeyWrappedUserKey couldn't be found on either the MasterPasswordUnlockData or the UserSymmetricKey property passed in.", ex.Message); + } + private void SetDefaultKdfHmacKey(byte[]? newKey) { var fieldInfo = typeof(AccountsController).GetField("_defaultKdfHmacKey", BindingFlags.NonPublic | BindingFlags.Instance); From aa8e8cc868bf3270c8b693fa427c7f9c09b87b7d Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Tue, 16 Dec 2025 12:29:57 -0500 Subject: [PATCH 10/44] test(register): [PM-27084] Account Register Uses New Data Types - Fixted tests and added comments for the future. --- .../Factories/IdentityApplicationFactory.cs | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs index 3c0b55190864..d605cc9ff8f6 100644 --- a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs +++ b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs @@ -4,9 +4,11 @@ using System.Collections.Concurrent; using System.Net.Http.Json; using System.Text.Json; +using Bit.Core; using Bit.Core.Auth.Models.Api.Request.Accounts; using Bit.Core.Entities; using Bit.Core.Enums; +using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Services; using Bit.Identity; using Bit.Test.Common.Helpers; @@ -195,6 +197,71 @@ public async Task RegisterNewIdentityFactoryUserAsync( RegisterFinishRequestModel requestModel, bool marketingEmails = true) { + // Ensure required fields for registration finish are present. + // Prefer legacy-path defaults (root fields) to minimize changes to tests. + // PM-28143 - When MasterPasswordAuthenticationData is required, delete all handling of MasterPasswordHash. + requestModel.MasterPasswordHash ??= DefaultUserPasswordHash; + // PM-28143 - When KDF is sourced exclusively from MasterPasswordUnlockData, delete the root Kdf defaults below. + requestModel.Kdf ??= KdfType.PBKDF2_SHA256; + requestModel.KdfIterations ??= AuthConstants.PBKDF2_ITERATIONS.Default; + // Ensure a symmetric key is provided when no unlock data is present + // PM-28143 - When MasterPasswordUnlockData is required, delete the UserSymmetricKey fallback block below. + if (requestModel.MasterPasswordUnlockData == null && string.IsNullOrWhiteSpace(requestModel.UserSymmetricKey)) + { + requestModel.UserSymmetricKey = "user_symmetric_key"; + } + + // Align unlock/auth data KDF with root KDF so login uses the provided master password hash. + // PM-28143 - After removing root Kdf fields, build KDF exclusively from MasterPasswordUnlockData.Kdf and delete this alignment section. + var effectiveKdfType = requestModel.Kdf ?? KdfType.PBKDF2_SHA256; + var effectiveIterations = requestModel.KdfIterations ?? AuthConstants.PBKDF2_ITERATIONS.Default; + int? effectiveMemory = null; + int? effectiveParallelism = null; + if (effectiveKdfType == KdfType.Argon2id) + { + effectiveIterations = AuthConstants.ARGON2_ITERATIONS.InsideRange(effectiveIterations) + ? effectiveIterations + : AuthConstants.ARGON2_ITERATIONS.Default; + effectiveMemory = AuthConstants.ARGON2_MEMORY.Default; + effectiveParallelism = AuthConstants.ARGON2_PARALLELISM.Default; + } + + var alignedKdf = new KdfSettings + { + KdfType = effectiveKdfType, + Iterations = effectiveIterations, + Memory = effectiveMemory, + Parallelism = effectiveParallelism + }; + + if (requestModel.MasterPasswordUnlockData != null) + { + var unlock = requestModel.MasterPasswordUnlockData; + // PM-28143 - Once UserSymmetricKey is removed and UnlockData is required, delete the fallback to UserSymmetricKey below. + var masterKeyWrappedUserKey = !string.IsNullOrWhiteSpace(unlock.MasterKeyWrappedUserKey) + ? unlock.MasterKeyWrappedUserKey + : (string.IsNullOrWhiteSpace(requestModel.UserSymmetricKey) ? "user_symmetric_key" : requestModel.UserSymmetricKey); + requestModel.MasterPasswordUnlockData = new MasterPasswordUnlockData + { + Kdf = alignedKdf, + MasterKeyWrappedUserKey = masterKeyWrappedUserKey, + Salt = unlock.Salt + }; + } + + if (requestModel.MasterPasswordAuthenticationData != null) + { + // Ensure registration uses the same hash the tests will provide at login. + // PM-28143 - When MasterPasswordAuthenticationData is the only source of the auth hash, + // stop overriding it from MasterPasswordHash and delete this whole reassignment block. + requestModel.MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData + { + Kdf = alignedKdf, + MasterPasswordAuthenticationHash = requestModel.MasterPasswordHash, + Salt = requestModel.Email + }; + } + var sendVerificationEmailReqModel = new RegisterSendVerificationEmailRequestModel { Email = requestModel.Email, From 5b2edef7369064aa6f547cd3febfa5e58926d0a4 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Mon, 22 Dec 2025 21:49:38 -0500 Subject: [PATCH 11/44] test(register): [PM-27084] Account Register Uses New Data Types - Added new checks from review. --- .../Accounts/RegisterFinishRequestModel.cs | 16 +++++++++-- .../Data/MasterPasswordAuthenticationData.cs | 13 +++++++++ .../Models/Data/MasterPasswordUnlockData.cs | 27 +++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 1fa78390fdce..43e5a471d16e 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -63,12 +63,22 @@ public class RegisterFinishRequestModel : IValidatableObject public User ToUser() { + // PM-28143 - Remove line below + // When we process this request to a user object, check if the unlock and authentication + // data has been passed through, and if so they should have matching values. + MasterPasswordUnlockData.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthenticationData, MasterPasswordUnlockData); + + // PM-28143 - Remove line below + MasterPasswordAuthenticationData.ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthenticationData, MasterPasswordHash); + var user = new User { Email = Email, MasterPasswordHint = MasterPasswordHint, - Kdf = MasterPasswordUnlockData?.Kdf.KdfType ?? Kdf ?? throw new Exception("KdfType couldn't be found on either the MasterPasswordUnlockData or the Kdf property passed in."), - KdfIterations = MasterPasswordUnlockData?.Kdf.Iterations ?? KdfIterations ?? throw new Exception("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in."), + Kdf = MasterPasswordUnlockData?.Kdf.KdfType ?? Kdf + ?? throw new Exception("KdfType couldn't be found on either the MasterPasswordUnlockData or the Kdf property passed in."), + KdfIterations = MasterPasswordUnlockData?.Kdf.Iterations ?? KdfIterations + ?? throw new Exception("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in."), // KdfMemory and KdfParallelism are optional (only used for Argon2id) KdfMemory = MasterPasswordUnlockData?.Kdf.Memory ?? KdfMemory, KdfParallelism = MasterPasswordUnlockData?.Kdf.Parallelism ?? KdfParallelism, @@ -111,6 +121,8 @@ public RegisterFinishTokenType GetTokenType() public IEnumerable Validate(ValidationContext validationContext) { + MasterPasswordUnlockData.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthenticationData, MasterPasswordUnlockData); + // PM-28143 - Remove line below var kdf = MasterPasswordUnlockData?.Kdf.KdfType ?? Kdf diff --git a/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs b/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs index 1bc7006ceff5..be566aa4d0b2 100644 --- a/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs +++ b/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs @@ -16,4 +16,17 @@ public void ValidateSaltUnchangedForUser(User user) throw new BadRequestException("Invalid master password salt."); } } + + public static void ThrowIfExistsAndHashIsNotEqual( + MasterPasswordAuthenticationData? authenticationData, + string? hash) + { + if (authenticationData != null && hash != null) + { + if (authenticationData.MasterPasswordAuthenticationHash != hash) + { + throw new Exception("Master password hash and hash are not equal."); + } + } + } } diff --git a/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs b/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs index cb18ed2a78c2..609401b6433d 100644 --- a/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs +++ b/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs @@ -16,4 +16,31 @@ public void ValidateSaltUnchangedForUser(User user) throw new BadRequestException("Invalid master password salt."); } } + + public static void ThrowIfExistsAndNotMatchingAuthenticationData( + MasterPasswordAuthenticationData? authenticationData, + MasterPasswordUnlockData? unlockData) + { + if (unlockData != null && authenticationData != null) + { + var matches = MatchesAuthenticationData( + unlockData, + authenticationData); + + if (!matches) + { + throw new Exception("KDF settings and salt must match between authentication and unlock data."); + } + } + } + + private static bool MatchesAuthenticationData( + MasterPasswordUnlockData unlockData, + MasterPasswordAuthenticationData authenticationData) + { + var kdfMatches = unlockData.Kdf.Equals(authenticationData.Kdf); + var saltMatches = unlockData.Salt == authenticationData.Salt; + + return kdfMatches && saltMatches; + } } From 4f81d752923048d1116be66eb18608aed4a4455b Mon Sep 17 00:00:00 2001 From: Patrick-Pimentel-Bitwarden Date: Tue, 23 Dec 2025 10:48:55 -0500 Subject: [PATCH 12/44] Update src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs Co-authored-by: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com> --- .../Models/Api/Request/Accounts/RegisterFinishRequestModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 43e5a471d16e..252247f7c697 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -21,7 +21,7 @@ public class RegisterFinishRequestModel : IValidatableObject public required string Email { get; set; } public string? EmailVerificationToken { get; set; } - public MasterPasswordAuthenticationData? MasterPasswordAuthenticationData { get; set; } + public MasterPasswordAuthenticationData? MasterPasswordAuthentication { get; set; } public MasterPasswordUnlockData? MasterPasswordUnlockData { get; set; } // PM-28143 - Remove property below (made optional during migration to MasterPasswordUnlockData) From fac1d4bdc2328d8adab91116a1e2ec4c92a8ea7c Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Mon, 29 Dec 2025 11:57:25 -0500 Subject: [PATCH 13/44] fix(register): [PM-27084] Account Register Uses New Data Types - Added new checks for master password authentication data. --- .../Accounts/RegisterFinishRequestModel.cs | 30 +++++++++++-------- .../Controllers/AccountsController.cs | 2 +- .../Controllers/AccountsControllerTests.cs | 26 ++++++++-------- .../Factories/IdentityApplicationFactory.cs | 12 ++++---- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 252247f7c697..bc84e9cf5859 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -22,7 +22,7 @@ public class RegisterFinishRequestModel : IValidatableObject public string? EmailVerificationToken { get; set; } public MasterPasswordAuthenticationData? MasterPasswordAuthentication { get; set; } - public MasterPasswordUnlockData? MasterPasswordUnlockData { get; set; } + public MasterPasswordUnlockData? MasterPasswordUnlock { get; set; } // PM-28143 - Remove property below (made optional during migration to MasterPasswordUnlockData) [StringLength(1000)] @@ -66,25 +66,25 @@ public User ToUser() // PM-28143 - Remove line below // When we process this request to a user object, check if the unlock and authentication // data has been passed through, and if so they should have matching values. - MasterPasswordUnlockData.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthenticationData, MasterPasswordUnlockData); + MasterPasswordUnlockData.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthentication, MasterPasswordUnlock); // PM-28143 - Remove line below - MasterPasswordAuthenticationData.ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthenticationData, MasterPasswordHash); + MasterPasswordAuthenticationData.ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash); var user = new User { Email = Email, MasterPasswordHint = MasterPasswordHint, - Kdf = MasterPasswordUnlockData?.Kdf.KdfType ?? Kdf + Kdf = MasterPasswordUnlock?.Kdf.KdfType ?? Kdf ?? throw new Exception("KdfType couldn't be found on either the MasterPasswordUnlockData or the Kdf property passed in."), - KdfIterations = MasterPasswordUnlockData?.Kdf.Iterations ?? KdfIterations + KdfIterations = MasterPasswordUnlock?.Kdf.Iterations ?? KdfIterations ?? throw new Exception("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in."), // KdfMemory and KdfParallelism are optional (only used for Argon2id) - KdfMemory = MasterPasswordUnlockData?.Kdf.Memory ?? KdfMemory, - KdfParallelism = MasterPasswordUnlockData?.Kdf.Parallelism ?? KdfParallelism, + KdfMemory = MasterPasswordUnlock?.Kdf.Memory ?? KdfMemory, + KdfParallelism = MasterPasswordUnlock?.Kdf.Parallelism ?? KdfParallelism, // PM-28827 To be added when MasterPasswordSalt is added to the user column // MasterPasswordSalt = MasterPasswordUnlockData?.Salt ?? Email.ToLower().Trim(), - Key = MasterPasswordUnlockData?.MasterKeyWrappedUserKey ?? UserSymmetricKey ?? throw new Exception("MasterKeyWrappedUserKey couldn't be found on either the MasterPasswordUnlockData or the UserSymmetricKey property passed in."), + Key = MasterPasswordUnlock?.MasterKeyWrappedUserKey ?? UserSymmetricKey ?? throw new Exception("MasterKeyWrappedUserKey couldn't be found on either the MasterPasswordUnlockData or the UserSymmetricKey property passed in."), }; UserAsymmetricKeys.ToUser(user); @@ -121,24 +121,28 @@ public RegisterFinishTokenType GetTokenType() public IEnumerable Validate(ValidationContext validationContext) { - MasterPasswordUnlockData.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthenticationData, MasterPasswordUnlockData); + // PM-28143 - Remove line below + MasterPasswordUnlockData.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthentication, MasterPasswordUnlock); + + // PM-28143 - Remove line below + MasterPasswordAuthenticationData.ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash); // PM-28143 - Remove line below - var kdf = MasterPasswordUnlockData?.Kdf.KdfType + var kdf = MasterPasswordUnlock?.Kdf.KdfType ?? Kdf ?? throw new Exception($"{nameof(Kdf)} not found on RequestModel"); // PM-28143 - Remove line below - var kdfIterations = MasterPasswordUnlockData?.Kdf.Iterations + var kdfIterations = MasterPasswordUnlock?.Kdf.Iterations ?? KdfIterations ?? throw new Exception($"{nameof(KdfIterations)} not found on RequestModel"); // PM-28143 - Remove line below - var kdfMemory = MasterPasswordUnlockData?.Kdf.Memory + var kdfMemory = MasterPasswordUnlock?.Kdf.Memory ?? KdfMemory; // PM-28143 - Remove line below - var kdfParallelism = MasterPasswordUnlockData?.Kdf.Parallelism + var kdfParallelism = MasterPasswordUnlock?.Kdf.Parallelism ?? KdfParallelism; // PM-28143 - Remove line below in favor of using the unlock data. diff --git a/src/Identity/Controllers/AccountsController.cs b/src/Identity/Controllers/AccountsController.cs index a521816985fe..06ee097f3212 100644 --- a/src/Identity/Controllers/AccountsController.cs +++ b/src/Identity/Controllers/AccountsController.cs @@ -156,7 +156,7 @@ public async Task PostRegisterFinish([FromBody] Reg IdentityResult? identityResult = null; // PM-28143 - Just use the MasterPasswordAuthenticationData.MasterPasswordAuthenticationHash - string masterPasswordHash = model.MasterPasswordAuthenticationData?.MasterPasswordAuthenticationHash + string masterPasswordHash = model.MasterPasswordAuthentication?.MasterPasswordAuthenticationHash ?? model.MasterPasswordHash ?? throw new BadRequestException("MasterPasswordHash couldn't be found on either the MasterPasswordAuthenticationData or the MasterPasswordHash property passed in."); switch (model.GetTokenType()) diff --git a/test/Identity.Test/Controllers/AccountsControllerTests.cs b/test/Identity.Test/Controllers/AccountsControllerTests.cs index 468fed55cdb6..e64e9b88fb39 100644 --- a/test/Identity.Test/Controllers/AccountsControllerTests.cs +++ b/test/Identity.Test/Controllers/AccountsControllerTests.cs @@ -607,7 +607,7 @@ public async Task PostRegisterFinish_EmailVerification_BothDataForms_ProduceEqui { Email = email, EmailVerificationToken = emailVerificationToken, - MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData + MasterPasswordAuthentication = new MasterPasswordAuthenticationData { Kdf = new KdfSettings { @@ -619,7 +619,7 @@ public async Task PostRegisterFinish_EmailVerification_BothDataForms_ProduceEqui MasterPasswordAuthenticationHash = masterPasswordHash, Salt = email // salt choice is not validated here during registration }, - MasterPasswordUnlockData = new MasterPasswordUnlockData + MasterPasswordUnlock = new MasterPasswordUnlockData { Kdf = new KdfSettings { @@ -725,7 +725,7 @@ public async Task PostRegisterFinish_OrgInvite_BothDataForms_ProduceEquivalentOu Email = email, OrgInviteToken = orgInviteToken, OrganizationUserId = organizationUserId, - MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData + MasterPasswordAuthentication = new MasterPasswordAuthenticationData { Kdf = new KdfSettings { @@ -735,7 +735,7 @@ public async Task PostRegisterFinish_OrgInvite_BothDataForms_ProduceEquivalentOu MasterPasswordAuthenticationHash = masterPasswordHash, Salt = email }, - MasterPasswordUnlockData = new MasterPasswordUnlockData + MasterPasswordUnlock = new MasterPasswordUnlockData { Kdf = new KdfSettings { @@ -842,14 +842,14 @@ public async Task PostRegisterFinish_NewForm_UsesUnlockDataForKdfAndKey_WhenRoot { Email = email, EmailVerificationToken = emailVerificationToken, - MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData + MasterPasswordAuthentication = new MasterPasswordAuthenticationData { // present but not used by ToUser for KDF/Key Kdf = new KdfSettings { KdfType = KdfType.Argon2id, Iterations = iterations }, MasterPasswordAuthenticationHash = masterPasswordHash, Salt = email }, - MasterPasswordUnlockData = new MasterPasswordUnlockData + MasterPasswordUnlock = new MasterPasswordUnlockData { Kdf = unlockKdf, MasterKeyWrappedUserKey = masterKeyWrappedUserKey, @@ -901,7 +901,7 @@ public async Task PostRegisterFinish_LegacyForm_UsesRootFields_WhenUnlockDataNul Kdf = KdfType.PBKDF2_SHA256, KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default, UserSymmetricKey = legacyKey, - MasterPasswordUnlockData = null, + MasterPasswordUnlock = null, UserAsymmetricKeys = new KeysRequestModel { PublicKey = publicKey, @@ -942,14 +942,14 @@ public async Task PostRegisterFinish_WhenKdfMissingInAllSources_ShouldReturnBadR { Email = email, EmailVerificationToken = emailVerificationToken, - MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData + MasterPasswordAuthentication = new MasterPasswordAuthenticationData { // present but ToUser does not source KDF from here Kdf = new KdfSettings { KdfType = KdfType.Argon2id, Iterations = iterations }, MasterPasswordAuthenticationHash = masterPasswordHash, Salt = email }, - MasterPasswordUnlockData = null, + MasterPasswordUnlock = null, Kdf = null, KdfIterations = iterations, UserSymmetricKey = masterKeyWrappedUserKey, @@ -980,14 +980,14 @@ public async Task PostRegisterFinish_WhenKdfIterationsMissingInAllSources_Should { Email = email, EmailVerificationToken = emailVerificationToken, - MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData + MasterPasswordAuthentication = new MasterPasswordAuthenticationData { // present but ToUser does not source iterations from here Kdf = new KdfSettings { KdfType = kdfType, Iterations = AuthConstants.PBKDF2_ITERATIONS.Default }, MasterPasswordAuthenticationHash = masterPasswordHash, Salt = email }, - MasterPasswordUnlockData = null, + MasterPasswordUnlock = null, Kdf = kdfType, KdfIterations = null, UserSymmetricKey = masterKeyWrappedUserKey, @@ -1018,13 +1018,13 @@ public async Task PostRegisterFinish_WhenKeyMissingInAllSources_ShouldReturnBadR { Email = email, EmailVerificationToken = emailVerificationToken, - MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData + MasterPasswordAuthentication = new MasterPasswordAuthenticationData { Kdf = new KdfSettings { KdfType = kdfType, Iterations = iterations }, MasterPasswordAuthenticationHash = masterPasswordHash, Salt = email }, - MasterPasswordUnlockData = null, + MasterPasswordUnlock = null, Kdf = kdfType, KdfIterations = iterations, UserSymmetricKey = null, diff --git a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs index d605cc9ff8f6..4101553c01c6 100644 --- a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs +++ b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs @@ -206,7 +206,7 @@ public async Task RegisterNewIdentityFactoryUserAsync( requestModel.KdfIterations ??= AuthConstants.PBKDF2_ITERATIONS.Default; // Ensure a symmetric key is provided when no unlock data is present // PM-28143 - When MasterPasswordUnlockData is required, delete the UserSymmetricKey fallback block below. - if (requestModel.MasterPasswordUnlockData == null && string.IsNullOrWhiteSpace(requestModel.UserSymmetricKey)) + if (requestModel.MasterPasswordUnlock == null && string.IsNullOrWhiteSpace(requestModel.UserSymmetricKey)) { requestModel.UserSymmetricKey = "user_symmetric_key"; } @@ -234,14 +234,14 @@ public async Task RegisterNewIdentityFactoryUserAsync( Parallelism = effectiveParallelism }; - if (requestModel.MasterPasswordUnlockData != null) + if (requestModel.MasterPasswordUnlock != null) { - var unlock = requestModel.MasterPasswordUnlockData; + var unlock = requestModel.MasterPasswordUnlock; // PM-28143 - Once UserSymmetricKey is removed and UnlockData is required, delete the fallback to UserSymmetricKey below. var masterKeyWrappedUserKey = !string.IsNullOrWhiteSpace(unlock.MasterKeyWrappedUserKey) ? unlock.MasterKeyWrappedUserKey : (string.IsNullOrWhiteSpace(requestModel.UserSymmetricKey) ? "user_symmetric_key" : requestModel.UserSymmetricKey); - requestModel.MasterPasswordUnlockData = new MasterPasswordUnlockData + requestModel.MasterPasswordUnlock = new MasterPasswordUnlockData { Kdf = alignedKdf, MasterKeyWrappedUserKey = masterKeyWrappedUserKey, @@ -249,12 +249,12 @@ public async Task RegisterNewIdentityFactoryUserAsync( }; } - if (requestModel.MasterPasswordAuthenticationData != null) + if (requestModel.MasterPasswordAuthentication != null) { // Ensure registration uses the same hash the tests will provide at login. // PM-28143 - When MasterPasswordAuthenticationData is the only source of the auth hash, // stop overriding it from MasterPasswordHash and delete this whole reassignment block. - requestModel.MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData + requestModel.MasterPasswordAuthentication = new MasterPasswordAuthenticationData { Kdf = alignedKdf, MasterPasswordAuthenticationHash = requestModel.MasterPasswordHash, From 2d9786e09db7e8398d18bd60b02f67da31e563a4 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Mon, 29 Dec 2025 13:20:58 -0500 Subject: [PATCH 14/44] fix(register): [PM-27084] Account Register Uses New Data Types - Fixed some tests up. --- .../Request/Accounts/PasswordRequestModel.cs | 6 +- .../MasterPasswordUnlockDataRequestModel.cs | 22 ----- .../Accounts/RegisterFinishRequestModel.cs | 14 ++-- ...rPasswordAuthenticationDataRequestModel.cs | 15 +++- .../MasterPasswordUnlockDataRequestModel.cs | 49 ++++++++++++ .../Models/Data}/KdfRequestModel.cs | 3 +- .../Data/MasterPasswordAuthenticationData.cs | 19 ++--- .../Models/Data/MasterPasswordUnlockData.cs | 27 ------- .../Controllers/AccountsControllerTests.cs | 80 +++++++++---------- .../Factories/IdentityApplicationFactory.cs | 7 +- 10 files changed, 123 insertions(+), 119 deletions(-) delete mode 100644 src/Api/KeyManagement/Models/Requests/MasterPasswordUnlockDataRequestModel.cs rename src/{Api/KeyManagement/Models/Requests => Core/KeyManagement/Models/Api/Request}/MasterPasswordAuthenticationDataRequestModel.cs (56%) create mode 100644 src/Core/KeyManagement/Models/Api/Request/MasterPasswordUnlockDataRequestModel.cs rename src/{Api/KeyManagement/Models/Requests => Core/KeyManagement/Models/Data}/KdfRequestModel.cs (85%) diff --git a/src/Api/Auth/Models/Request/Accounts/PasswordRequestModel.cs b/src/Api/Auth/Models/Request/Accounts/PasswordRequestModel.cs index 8fa51e9f3480..ab8c727852dd 100644 --- a/src/Api/Auth/Models/Request/Accounts/PasswordRequestModel.cs +++ b/src/Api/Auth/Models/Request/Accounts/PasswordRequestModel.cs @@ -1,7 +1,5 @@ -#nullable enable - -using System.ComponentModel.DataAnnotations; -using Bit.Api.KeyManagement.Models.Requests; +using System.ComponentModel.DataAnnotations; +using Bit.Core.KeyManagement.Models.Api.Request; namespace Bit.Api.Auth.Models.Request.Accounts; diff --git a/src/Api/KeyManagement/Models/Requests/MasterPasswordUnlockDataRequestModel.cs b/src/Api/KeyManagement/Models/Requests/MasterPasswordUnlockDataRequestModel.cs deleted file mode 100644 index ce7a2b343f88..000000000000 --- a/src/Api/KeyManagement/Models/Requests/MasterPasswordUnlockDataRequestModel.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Bit.Core.KeyManagement.Models.Data; -using Bit.Core.Utilities; - -namespace Bit.Api.KeyManagement.Models.Requests; - -public class MasterPasswordUnlockDataRequestModel -{ - public required KdfRequestModel Kdf { get; init; } - [EncryptedString] public required string MasterKeyWrappedUserKey { get; init; } - [StringLength(256)] public required string Salt { get; init; } - - public MasterPasswordUnlockData ToData() - { - return new MasterPasswordUnlockData - { - Kdf = Kdf.ToData(), - MasterKeyWrappedUserKey = MasterKeyWrappedUserKey, - Salt = Salt - }; - } -} diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index bc84e9cf5859..81b85387a678 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -1,6 +1,6 @@ using Bit.Core.Entities; using Bit.Core.Enums; -using Bit.Core.KeyManagement.Models.Data; +using Bit.Core.KeyManagement.Models.Api.Request; using Bit.Core.Utilities; namespace Bit.Core.Auth.Models.Api.Request.Accounts; @@ -21,8 +21,8 @@ public class RegisterFinishRequestModel : IValidatableObject public required string Email { get; set; } public string? EmailVerificationToken { get; set; } - public MasterPasswordAuthenticationData? MasterPasswordAuthentication { get; set; } - public MasterPasswordUnlockData? MasterPasswordUnlock { get; set; } + public MasterPasswordAuthenticationDataRequestModel? MasterPasswordAuthentication { get; set; } + public MasterPasswordUnlockDataRequestModel? MasterPasswordUnlock { get; set; } // PM-28143 - Remove property below (made optional during migration to MasterPasswordUnlockData) [StringLength(1000)] @@ -66,10 +66,10 @@ public User ToUser() // PM-28143 - Remove line below // When we process this request to a user object, check if the unlock and authentication // data has been passed through, and if so they should have matching values. - MasterPasswordUnlockData.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthentication, MasterPasswordUnlock); + MasterPasswordUnlockDataRequestModel.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthentication, MasterPasswordUnlock); // PM-28143 - Remove line below - MasterPasswordAuthenticationData.ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash); + MasterPasswordAuthenticationDataRequestModel.ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash); var user = new User { @@ -122,10 +122,10 @@ public RegisterFinishTokenType GetTokenType() public IEnumerable Validate(ValidationContext validationContext) { // PM-28143 - Remove line below - MasterPasswordUnlockData.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthentication, MasterPasswordUnlock); + MasterPasswordUnlockDataRequestModel.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthentication, MasterPasswordUnlock); // PM-28143 - Remove line below - MasterPasswordAuthenticationData.ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash); + MasterPasswordAuthenticationDataRequestModel.ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash); // PM-28143 - Remove line below var kdf = MasterPasswordUnlock?.Kdf.KdfType diff --git a/src/Api/KeyManagement/Models/Requests/MasterPasswordAuthenticationDataRequestModel.cs b/src/Core/KeyManagement/Models/Api/Request/MasterPasswordAuthenticationDataRequestModel.cs similarity index 56% rename from src/Api/KeyManagement/Models/Requests/MasterPasswordAuthenticationDataRequestModel.cs rename to src/Core/KeyManagement/Models/Api/Request/MasterPasswordAuthenticationDataRequestModel.cs index d65dc8fcb71c..8a3283fc5b94 100644 --- a/src/Api/KeyManagement/Models/Requests/MasterPasswordAuthenticationDataRequestModel.cs +++ b/src/Core/KeyManagement/Models/Api/Request/MasterPasswordAuthenticationDataRequestModel.cs @@ -1,7 +1,7 @@ using System.ComponentModel.DataAnnotations; using Bit.Core.KeyManagement.Models.Data; -namespace Bit.Api.KeyManagement.Models.Requests; +namespace Bit.Core.KeyManagement.Models.Api.Request; public class MasterPasswordAuthenticationDataRequestModel { @@ -18,4 +18,17 @@ public MasterPasswordAuthenticationData ToData() Salt = Salt }; } + + public static void ThrowIfExistsAndHashIsNotEqual( + MasterPasswordAuthenticationDataRequestModel? authenticationData, + string? hash) + { + if (authenticationData != null && hash != null) + { + if (authenticationData.MasterPasswordAuthenticationHash != hash) + { + throw new Exception("Master password hash and hash are not equal."); + } + } + } } diff --git a/src/Core/KeyManagement/Models/Api/Request/MasterPasswordUnlockDataRequestModel.cs b/src/Core/KeyManagement/Models/Api/Request/MasterPasswordUnlockDataRequestModel.cs new file mode 100644 index 000000000000..a4baf01a19a7 --- /dev/null +++ b/src/Core/KeyManagement/Models/Api/Request/MasterPasswordUnlockDataRequestModel.cs @@ -0,0 +1,49 @@ +using System.ComponentModel.DataAnnotations; +using Bit.Core.KeyManagement.Models.Data; +using Bit.Core.Utilities; + +namespace Bit.Core.KeyManagement.Models.Api.Request; + +public class MasterPasswordUnlockDataRequestModel +{ + public required KdfRequestModel Kdf { get; init; } + [EncryptedString] public required string MasterKeyWrappedUserKey { get; init; } + [StringLength(256)] public required string Salt { get; init; } + + public MasterPasswordUnlockData ToData() + { + return new MasterPasswordUnlockData + { + Kdf = Kdf.ToData(), + MasterKeyWrappedUserKey = MasterKeyWrappedUserKey, + Salt = Salt + }; + } + + public static void ThrowIfExistsAndNotMatchingAuthenticationData( + MasterPasswordAuthenticationDataRequestModel? authenticationData, + MasterPasswordUnlockDataRequestModel? unlockData) + { + if (unlockData != null && authenticationData != null) + { + var matches = MatchesAuthenticationData( + unlockData, + authenticationData); + + if (!matches) + { + throw new Exception("KDF settings and salt must match between authentication and unlock data."); + } + } + } + + private static bool MatchesAuthenticationData( + MasterPasswordUnlockDataRequestModel unlockData, + MasterPasswordAuthenticationDataRequestModel authenticationData) + { + var kdfMatches = unlockData.Kdf.Equals(authenticationData.Kdf); + var saltMatches = unlockData.Salt == authenticationData.Salt; + + return kdfMatches && saltMatches; + } +} diff --git a/src/Api/KeyManagement/Models/Requests/KdfRequestModel.cs b/src/Core/KeyManagement/Models/Data/KdfRequestModel.cs similarity index 85% rename from src/Api/KeyManagement/Models/Requests/KdfRequestModel.cs rename to src/Core/KeyManagement/Models/Data/KdfRequestModel.cs index 904304a63322..ae38490e0036 100644 --- a/src/Api/KeyManagement/Models/Requests/KdfRequestModel.cs +++ b/src/Core/KeyManagement/Models/Data/KdfRequestModel.cs @@ -1,8 +1,7 @@ using System.ComponentModel.DataAnnotations; using Bit.Core.Enums; -using Bit.Core.KeyManagement.Models.Data; -namespace Bit.Api.KeyManagement.Models.Requests; +namespace Bit.Core.KeyManagement.Models.Data; public class KdfRequestModel { diff --git a/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs b/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs index be566aa4d0b2..25b864f0bdd5 100644 --- a/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs +++ b/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs @@ -3,6 +3,12 @@ namespace Bit.Core.KeyManagement.Models.Data; +/// +/// The data used for authentication of a master password. +/// +/// This data model does not have any validation, consider using MasterPasswordAuthenticationDataRequestModel +/// if validation is required. +/// public class MasterPasswordAuthenticationData { public required KdfSettings Kdf { get; init; } @@ -16,17 +22,4 @@ public void ValidateSaltUnchangedForUser(User user) throw new BadRequestException("Invalid master password salt."); } } - - public static void ThrowIfExistsAndHashIsNotEqual( - MasterPasswordAuthenticationData? authenticationData, - string? hash) - { - if (authenticationData != null && hash != null) - { - if (authenticationData.MasterPasswordAuthenticationHash != hash) - { - throw new Exception("Master password hash and hash are not equal."); - } - } - } } diff --git a/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs b/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs index 609401b6433d..cb18ed2a78c2 100644 --- a/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs +++ b/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs @@ -16,31 +16,4 @@ public void ValidateSaltUnchangedForUser(User user) throw new BadRequestException("Invalid master password salt."); } } - - public static void ThrowIfExistsAndNotMatchingAuthenticationData( - MasterPasswordAuthenticationData? authenticationData, - MasterPasswordUnlockData? unlockData) - { - if (unlockData != null && authenticationData != null) - { - var matches = MatchesAuthenticationData( - unlockData, - authenticationData); - - if (!matches) - { - throw new Exception("KDF settings and salt must match between authentication and unlock data."); - } - } - } - - private static bool MatchesAuthenticationData( - MasterPasswordUnlockData unlockData, - MasterPasswordAuthenticationData authenticationData) - { - var kdfMatches = unlockData.Kdf.Equals(authenticationData.Kdf); - var saltMatches = unlockData.Salt == authenticationData.Salt; - - return kdfMatches && saltMatches; - } } diff --git a/test/Identity.Test/Controllers/AccountsControllerTests.cs b/test/Identity.Test/Controllers/AccountsControllerTests.cs index e64e9b88fb39..88899de80a23 100644 --- a/test/Identity.Test/Controllers/AccountsControllerTests.cs +++ b/test/Identity.Test/Controllers/AccountsControllerTests.cs @@ -9,6 +9,7 @@ using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; +using Bit.Core.KeyManagement.Models.Api.Request; using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Models.Data; using Bit.Core.Repositories; @@ -603,31 +604,28 @@ public async Task PostRegisterFinish_EmailVerification_BothDataForms_ProduceEqui string encryptedPrivateKey) { // Arrange: new-form model (MasterPasswordAuthenticationData + MasterPasswordUnlockData) + + var kdfData = new KdfRequestModel + { + KdfType = KdfType.Argon2id, + Iterations = AuthConstants.ARGON2_ITERATIONS.Default, + Memory = AuthConstants.ARGON2_MEMORY.Default, + Parallelism = AuthConstants.ARGON2_PARALLELISM.Default + }; + var newModel = new RegisterFinishRequestModel { Email = email, EmailVerificationToken = emailVerificationToken, - MasterPasswordAuthentication = new MasterPasswordAuthenticationData + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel { - Kdf = new KdfSettings - { - KdfType = KdfType.Argon2id, - Iterations = AuthConstants.ARGON2_ITERATIONS.Default, - Memory = AuthConstants.ARGON2_MEMORY.Default, - Parallelism = AuthConstants.ARGON2_PARALLELISM.Default - }, + Kdf = kdfData, MasterPasswordAuthenticationHash = masterPasswordHash, Salt = email // salt choice is not validated here during registration }, - MasterPasswordUnlock = new MasterPasswordUnlockData + MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel { - Kdf = new KdfSettings - { - KdfType = KdfType.Argon2id, - Iterations = AuthConstants.ARGON2_ITERATIONS.Default, - Memory = AuthConstants.ARGON2_MEMORY.Default, - Parallelism = AuthConstants.ARGON2_PARALLELISM.Default - }, + Kdf = kdfData, MasterKeyWrappedUserKey = masterKeyWrappedUserKey, Salt = email }, @@ -719,29 +717,29 @@ public async Task PostRegisterFinish_OrgInvite_BothDataForms_ProduceEquivalentOu string publicKey, string encryptedPrivateKey) { + var kdfData = new KdfRequestModel + { + KdfType = KdfType.Argon2id, + Iterations = AuthConstants.ARGON2_ITERATIONS.Default, + Memory = AuthConstants.ARGON2_MEMORY.Default, + Parallelism = AuthConstants.ARGON2_PARALLELISM.Default + }; + // Arrange: new-form model (MasterPasswordAuthenticationData + MasterPasswordUnlockData) var newModel = new RegisterFinishRequestModel { Email = email, OrgInviteToken = orgInviteToken, OrganizationUserId = organizationUserId, - MasterPasswordAuthentication = new MasterPasswordAuthenticationData + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel { - Kdf = new KdfSettings - { - KdfType = KdfType.PBKDF2_SHA256, - Iterations = AuthConstants.PBKDF2_ITERATIONS.Default - }, + Kdf = kdfData, MasterPasswordAuthenticationHash = masterPasswordHash, Salt = email }, - MasterPasswordUnlock = new MasterPasswordUnlockData + MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel { - Kdf = new KdfSettings - { - KdfType = KdfType.PBKDF2_SHA256, - Iterations = AuthConstants.PBKDF2_ITERATIONS.Default - }, + Kdf = kdfData, MasterKeyWrappedUserKey = masterKeyWrappedUserKey, Salt = email }, @@ -759,8 +757,10 @@ public async Task PostRegisterFinish_OrgInvite_BothDataForms_ProduceEquivalentOu OrgInviteToken = orgInviteToken, OrganizationUserId = organizationUserId, MasterPasswordHash = masterPasswordHash, - Kdf = KdfType.PBKDF2_SHA256, - KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default, + Kdf = kdfData.KdfType, + KdfIterations = kdfData.Iterations, + KdfMemory = kdfData.Memory, + KdfParallelism = kdfData.Parallelism, UserSymmetricKey = masterKeyWrappedUserKey, UserAsymmetricKeys = new KeysRequestModel { @@ -832,7 +832,7 @@ public async Task PostRegisterFinish_NewForm_UsesUnlockDataForKdfAndKey_WhenRoot string encryptedPrivateKey) { // Arrange: Provide only unlock-data KDF + key; leave root KDF fields null - var unlockKdf = new KdfSettings + var unlockKdf = new KdfRequestModel { KdfType = KdfType.PBKDF2_SHA256, Iterations = iterations @@ -842,14 +842,14 @@ public async Task PostRegisterFinish_NewForm_UsesUnlockDataForKdfAndKey_WhenRoot { Email = email, EmailVerificationToken = emailVerificationToken, - MasterPasswordAuthentication = new MasterPasswordAuthenticationData + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel { // present but not used by ToUser for KDF/Key - Kdf = new KdfSettings { KdfType = KdfType.Argon2id, Iterations = iterations }, + Kdf = unlockKdf, MasterPasswordAuthenticationHash = masterPasswordHash, Salt = email }, - MasterPasswordUnlock = new MasterPasswordUnlockData + MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel { Kdf = unlockKdf, MasterKeyWrappedUserKey = masterKeyWrappedUserKey, @@ -942,10 +942,10 @@ public async Task PostRegisterFinish_WhenKdfMissingInAllSources_ShouldReturnBadR { Email = email, EmailVerificationToken = emailVerificationToken, - MasterPasswordAuthentication = new MasterPasswordAuthenticationData + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel { // present but ToUser does not source KDF from here - Kdf = new KdfSettings { KdfType = KdfType.Argon2id, Iterations = iterations }, + Kdf = new KdfRequestModel { KdfType = KdfType.Argon2id, Iterations = iterations }, MasterPasswordAuthenticationHash = masterPasswordHash, Salt = email }, @@ -980,10 +980,10 @@ public async Task PostRegisterFinish_WhenKdfIterationsMissingInAllSources_Should { Email = email, EmailVerificationToken = emailVerificationToken, - MasterPasswordAuthentication = new MasterPasswordAuthenticationData + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel { // present but ToUser does not source iterations from here - Kdf = new KdfSettings { KdfType = kdfType, Iterations = AuthConstants.PBKDF2_ITERATIONS.Default }, + Kdf = new KdfRequestModel { KdfType = kdfType, Iterations = AuthConstants.PBKDF2_ITERATIONS.Default }, MasterPasswordAuthenticationHash = masterPasswordHash, Salt = email }, @@ -1018,9 +1018,9 @@ public async Task PostRegisterFinish_WhenKeyMissingInAllSources_ShouldReturnBadR { Email = email, EmailVerificationToken = emailVerificationToken, - MasterPasswordAuthentication = new MasterPasswordAuthenticationData + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel { - Kdf = new KdfSettings { KdfType = kdfType, Iterations = iterations }, + Kdf = new KdfRequestModel { KdfType = kdfType, Iterations = iterations }, MasterPasswordAuthenticationHash = masterPasswordHash, Salt = email }, diff --git a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs index 4101553c01c6..830275af70c7 100644 --- a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs +++ b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs @@ -8,6 +8,7 @@ using Bit.Core.Auth.Models.Api.Request.Accounts; using Bit.Core.Entities; using Bit.Core.Enums; +using Bit.Core.KeyManagement.Models.Api.Request; using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Services; using Bit.Identity; @@ -226,7 +227,7 @@ public async Task RegisterNewIdentityFactoryUserAsync( effectiveParallelism = AuthConstants.ARGON2_PARALLELISM.Default; } - var alignedKdf = new KdfSettings + var alignedKdf = new KdfRequestModel { KdfType = effectiveKdfType, Iterations = effectiveIterations, @@ -241,7 +242,7 @@ public async Task RegisterNewIdentityFactoryUserAsync( var masterKeyWrappedUserKey = !string.IsNullOrWhiteSpace(unlock.MasterKeyWrappedUserKey) ? unlock.MasterKeyWrappedUserKey : (string.IsNullOrWhiteSpace(requestModel.UserSymmetricKey) ? "user_symmetric_key" : requestModel.UserSymmetricKey); - requestModel.MasterPasswordUnlock = new MasterPasswordUnlockData + requestModel.MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel { Kdf = alignedKdf, MasterKeyWrappedUserKey = masterKeyWrappedUserKey, @@ -254,7 +255,7 @@ public async Task RegisterNewIdentityFactoryUserAsync( // Ensure registration uses the same hash the tests will provide at login. // PM-28143 - When MasterPasswordAuthenticationData is the only source of the auth hash, // stop overriding it from MasterPasswordHash and delete this whole reassignment block. - requestModel.MasterPasswordAuthentication = new MasterPasswordAuthenticationData + requestModel.MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel { Kdf = alignedKdf, MasterPasswordAuthenticationHash = requestModel.MasterPasswordHash, From cc895d059812bf0b97b625e568b6edb083237a23 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Mon, 29 Dec 2025 13:39:08 -0500 Subject: [PATCH 15/44] fix(register): [PM-27084] Account Register Uses New Data Types - Added more tests. --- .../Accounts/RegisterFinishRequestModel.cs | 8 - .../Controllers/AccountsControllerTests.cs | 147 +++++++++++++++++- 2 files changed, 146 insertions(+), 9 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 81b85387a678..1379fc30fc67 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -63,14 +63,6 @@ public class RegisterFinishRequestModel : IValidatableObject public User ToUser() { - // PM-28143 - Remove line below - // When we process this request to a user object, check if the unlock and authentication - // data has been passed through, and if so they should have matching values. - MasterPasswordUnlockDataRequestModel.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthentication, MasterPasswordUnlock); - - // PM-28143 - Remove line below - MasterPasswordAuthenticationDataRequestModel.ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash); - var user = new User { Email = Email, diff --git a/test/Identity.Test/Controllers/AccountsControllerTests.cs b/test/Identity.Test/Controllers/AccountsControllerTests.cs index 88899de80a23..7c338e141f89 100644 --- a/test/Identity.Test/Controllers/AccountsControllerTests.cs +++ b/test/Identity.Test/Controllers/AccountsControllerTests.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System.ComponentModel.DataAnnotations; +using System.Reflection; using System.Text; using Bit.Core; using Bit.Core.Auth.Models.Api.Request.Accounts; @@ -1040,6 +1041,150 @@ public async Task PostRegisterFinish_WhenKeyMissingInAllSources_ShouldReturnBadR Assert.Equal("MasterKeyWrappedUserKey couldn't be found on either the MasterPasswordUnlockData or the UserSymmetricKey property passed in.", ex.Message); } + [Theory, BitAutoData] + public void RegisterFinishRequestModel_Validate_Throws_WhenUnlockAndAuthDataMismatch( + string email, + string authHash, + string masterKeyWrappedUserKey, + string publicKey, + string encryptedPrivateKey) + { + // Arrange: authentication and unlock have different KDF and/or salt + var authKdf = new KdfRequestModel + { + KdfType = KdfType.PBKDF2_SHA256, + Iterations = AuthConstants.PBKDF2_ITERATIONS.Default + }; + var unlockKdf = new KdfRequestModel + { + KdfType = KdfType.Argon2id, + Iterations = AuthConstants.ARGON2_ITERATIONS.Default, + Memory = AuthConstants.ARGON2_MEMORY.Default, + Parallelism = AuthConstants.ARGON2_PARALLELISM.Default + }; + + var model = new RegisterFinishRequestModel + { + Email = email, + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel + { + Kdf = authKdf, + MasterPasswordAuthenticationHash = authHash, + Salt = email + }, + MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel + { + Kdf = unlockKdf, + MasterKeyWrappedUserKey = masterKeyWrappedUserKey, + Salt = email + }, + UserAsymmetricKeys = new KeysRequestModel + { + PublicKey = publicKey, + EncryptedPrivateKey = encryptedPrivateKey + } + }; + + var ctx = new ValidationContext(model); + + // Act & Assert + var ex = Assert.Throws(() => model.Validate(ctx).ToList()); + Assert.Equal("KDF settings and salt must match between authentication and unlock data.", ex.Message); + } + + [Theory, BitAutoData] + public void RegisterFinishRequestModel_Validate_Throws_WhenSaltMismatch( + string email, + string authHash, + string masterKeyWrappedUserKey, + string publicKey, + string encryptedPrivateKey) + { + var unlockKdf = new KdfRequestModel + { + KdfType = KdfType.Argon2id, + Iterations = AuthConstants.ARGON2_ITERATIONS.Default, + Memory = AuthConstants.ARGON2_MEMORY.Default, + Parallelism = AuthConstants.ARGON2_PARALLELISM.Default + }; + + var model = new RegisterFinishRequestModel + { + Email = email, + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel + { + Kdf = unlockKdf, + MasterPasswordAuthenticationHash = authHash, + Salt = email + }, + MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel + { + Kdf = unlockKdf, + MasterKeyWrappedUserKey = masterKeyWrappedUserKey, + // Intentionally different salt to force mismatch + Salt = email + ".mismatch" + }, + UserAsymmetricKeys = new KeysRequestModel + { + PublicKey = publicKey, + EncryptedPrivateKey = encryptedPrivateKey + } + }; + + var ctx = new ValidationContext(model); + + // Act & Assert + var ex = Assert.Throws(() => model.Validate(ctx).ToList()); + Assert.Equal("KDF settings and salt must match between authentication and unlock data.", ex.Message); + } + + [Theory, BitAutoData] + public void RegisterFinishRequestModel_Validate_Throws_WhenAuthHashAndRootHashMismatch( + string email, + string authHash, + string differentRootHash, + string masterKeyWrappedUserKey, + string publicKey, + string encryptedPrivateKey) + { + // Arrange: same KDF/salt, but authentication hash differs from legacy root hash + var kdf = new KdfRequestModel + { + KdfType = KdfType.PBKDF2_SHA256, + Iterations = AuthConstants.PBKDF2_ITERATIONS.Default + }; + + var model = new RegisterFinishRequestModel + { + Email = email, + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel + { + Kdf = kdf, + MasterPasswordAuthenticationHash = authHash, + Salt = email + }, + MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel + { + Kdf = kdf, + MasterKeyWrappedUserKey = masterKeyWrappedUserKey, + Salt = email + }, + // Intentionally set the legacy field to a different value to trigger the throw + MasterPasswordHash = differentRootHash, + UserAsymmetricKeys = new KeysRequestModel + { + PublicKey = publicKey, + EncryptedPrivateKey = encryptedPrivateKey + } + }; + + var ctx = new ValidationContext(model); + + // Act & Assert + var ex = Assert.Throws(() => model.Validate(ctx).ToList()); + Assert.Equal("Master password hash and hash are not equal.", ex.Message); + } + private void SetDefaultKdfHmacKey(byte[]? newKey) { var fieldInfo = typeof(AccountsController).GetField("_defaultKdfHmacKey", BindingFlags.NonPublic | BindingFlags.Instance); From 4475649bdc4812dbdf5d9d77457276c94c3737d1 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Tue, 30 Dec 2025 16:06:59 -0500 Subject: [PATCH 16/44] fix(register): [PM-27084] Account Register Uses New Data Types - Removed invalid check. --- .../Accounts/RegisterFinishRequestModel.cs | 3 --- .../MasterPasswordUnlockDataRequestModel.cs | 27 ------------------- .../Data/MasterPasswordAuthenticationData.cs | 6 ----- 3 files changed, 36 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 1379fc30fc67..c49acb3a49c8 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -113,9 +113,6 @@ public RegisterFinishTokenType GetTokenType() public IEnumerable Validate(ValidationContext validationContext) { - // PM-28143 - Remove line below - MasterPasswordUnlockDataRequestModel.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthentication, MasterPasswordUnlock); - // PM-28143 - Remove line below MasterPasswordAuthenticationDataRequestModel.ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash); diff --git a/src/Core/KeyManagement/Models/Api/Request/MasterPasswordUnlockDataRequestModel.cs b/src/Core/KeyManagement/Models/Api/Request/MasterPasswordUnlockDataRequestModel.cs index a4baf01a19a7..9b4e9599d3f0 100644 --- a/src/Core/KeyManagement/Models/Api/Request/MasterPasswordUnlockDataRequestModel.cs +++ b/src/Core/KeyManagement/Models/Api/Request/MasterPasswordUnlockDataRequestModel.cs @@ -19,31 +19,4 @@ public MasterPasswordUnlockData ToData() Salt = Salt }; } - - public static void ThrowIfExistsAndNotMatchingAuthenticationData( - MasterPasswordAuthenticationDataRequestModel? authenticationData, - MasterPasswordUnlockDataRequestModel? unlockData) - { - if (unlockData != null && authenticationData != null) - { - var matches = MatchesAuthenticationData( - unlockData, - authenticationData); - - if (!matches) - { - throw new Exception("KDF settings and salt must match between authentication and unlock data."); - } - } - } - - private static bool MatchesAuthenticationData( - MasterPasswordUnlockDataRequestModel unlockData, - MasterPasswordAuthenticationDataRequestModel authenticationData) - { - var kdfMatches = unlockData.Kdf.Equals(authenticationData.Kdf); - var saltMatches = unlockData.Salt == authenticationData.Salt; - - return kdfMatches && saltMatches; - } } diff --git a/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs b/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs index 25b864f0bdd5..1bc7006ceff5 100644 --- a/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs +++ b/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs @@ -3,12 +3,6 @@ namespace Bit.Core.KeyManagement.Models.Data; -/// -/// The data used for authentication of a master password. -/// -/// This data model does not have any validation, consider using MasterPasswordAuthenticationDataRequestModel -/// if validation is required. -/// public class MasterPasswordAuthenticationData { public required KdfSettings Kdf { get; init; } From a705ea4c579f7cfd2c0f77d7337e1f5a7428a058 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Tue, 30 Dec 2025 22:23:44 -0500 Subject: [PATCH 17/44] fix(register): [PM-27084] Account Register Uses New Data Types - Shuffled around validation checks to the request model instead of the controller. --- .../Accounts/RegisterFinishRequestModel.cs | 157 +++++++++++++++--- ...rPasswordAuthenticationDataRequestModel.cs | 17 +- .../Models/Data/KdfRequestModel.cs | 9 +- src/Core/Utilities/KdfSettingsValidator.cs | 1 - .../Controllers/AccountsController.cs | 48 +----- .../Controllers/AccountsControllerTests.cs | 24 ++- 6 files changed, 169 insertions(+), 87 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index c49acb3a49c8..cff5e68062ac 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -1,5 +1,6 @@ using Bit.Core.Entities; using Bit.Core.Enums; +using Bit.Core.Exceptions; using Bit.Core.KeyManagement.Models.Api.Request; using Bit.Core.Utilities; @@ -61,6 +62,40 @@ public class RegisterFinishRequestModel : IValidatableObject public Guid? ProviderUserId { get; set; } + // Strongly-typed accessors for post-validation usage to satisfy nullability + [System.Text.Json.Serialization.JsonIgnore] + [Newtonsoft.Json.JsonIgnore] + public string EmailVerificationTokenRequired => + EmailVerificationToken ?? throw new BadRequestException("Email verification token absent when processing register/finish."); + [System.Text.Json.Serialization.JsonIgnore] + [Newtonsoft.Json.JsonIgnore] + public string OrgInviteTokenRequired => + OrgInviteToken ?? throw new BadRequestException("Organization invite token absent when processing register/finish."); + [System.Text.Json.Serialization.JsonIgnore] + [Newtonsoft.Json.JsonIgnore] + public Guid OrganizationUserIdRequired => + OrganizationUserId ?? throw new BadRequestException("Organization user id absent when processing register/finish."); + [System.Text.Json.Serialization.JsonIgnore] + [Newtonsoft.Json.JsonIgnore] + public string OrgSponsoredFreeFamilyPlanTokenRequired => + OrgSponsoredFreeFamilyPlanToken ?? throw new BadRequestException("Organization sponsored free family plan token absent when processing register/finish."); + [System.Text.Json.Serialization.JsonIgnore] + [Newtonsoft.Json.JsonIgnore] + public string AcceptEmergencyAccessInviteTokenRequired => + AcceptEmergencyAccessInviteToken ?? throw new BadRequestException("Accept emergency access invite token absent when processing register/finish."); + [System.Text.Json.Serialization.JsonIgnore] + [Newtonsoft.Json.JsonIgnore] + public Guid AcceptEmergencyAccessIdRequired => + AcceptEmergencyAccessId ?? throw new BadRequestException("Accept emergency access id absent when processing register/finish."); + [System.Text.Json.Serialization.JsonIgnore] + [Newtonsoft.Json.JsonIgnore] + public string ProviderInviteTokenRequired => + ProviderInviteToken ?? throw new BadRequestException("Provider invite token absent when processing register/finish."); + [System.Text.Json.Serialization.JsonIgnore] + [Newtonsoft.Json.JsonIgnore] + public Guid ProviderUserIdRequired => + ProviderUserId ?? throw new BadRequestException("Provider user id absent when processing register/finish."); + public User ToUser() { var user = new User @@ -68,15 +103,15 @@ public User ToUser() Email = Email, MasterPasswordHint = MasterPasswordHint, Kdf = MasterPasswordUnlock?.Kdf.KdfType ?? Kdf - ?? throw new Exception("KdfType couldn't be found on either the MasterPasswordUnlockData or the Kdf property passed in."), + ?? throw new BadRequestException("KdfType couldn't be found on either the MasterPasswordUnlockData or the Kdf property passed in."), KdfIterations = MasterPasswordUnlock?.Kdf.Iterations ?? KdfIterations - ?? throw new Exception("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in."), + ?? throw new BadRequestException("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in."), // KdfMemory and KdfParallelism are optional (only used for Argon2id) KdfMemory = MasterPasswordUnlock?.Kdf.Memory ?? KdfMemory, KdfParallelism = MasterPasswordUnlock?.Kdf.Parallelism ?? KdfParallelism, // PM-28827 To be added when MasterPasswordSalt is added to the user column // MasterPasswordSalt = MasterPasswordUnlockData?.Salt ?? Email.ToLower().Trim(), - Key = MasterPasswordUnlock?.MasterKeyWrappedUserKey ?? UserSymmetricKey ?? throw new Exception("MasterKeyWrappedUserKey couldn't be found on either the MasterPasswordUnlockData or the UserSymmetricKey property passed in."), + Key = MasterPasswordUnlock?.MasterKeyWrappedUserKey ?? UserSymmetricKey ?? throw new BadRequestException("MasterKeyWrappedUserKey couldn't be found on either the MasterPasswordUnlockData or the UserSymmetricKey property passed in."), }; UserAsymmetricKeys.ToUser(user); @@ -110,34 +145,104 @@ public RegisterFinishTokenType GetTokenType() throw new InvalidOperationException("Invalid token type."); } - public IEnumerable Validate(ValidationContext validationContext) { - // PM-28143 - Remove line below - MasterPasswordAuthenticationDataRequestModel.ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash); - - // PM-28143 - Remove line below - var kdf = MasterPasswordUnlock?.Kdf.KdfType - ?? Kdf - ?? throw new Exception($"{nameof(Kdf)} not found on RequestModel"); - - // PM-28143 - Remove line below - var kdfIterations = MasterPasswordUnlock?.Kdf.Iterations - ?? KdfIterations - ?? throw new Exception($"{nameof(KdfIterations)} not found on RequestModel"); + // PM-28143 - Remove this check + ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash); + + // Ensure exactly one registration token type is provided + var hasEmailVerification = !string.IsNullOrWhiteSpace(EmailVerificationToken); + var hasOrgInvite = !string.IsNullOrEmpty(OrgInviteToken) && OrganizationUserId.HasValue; + var hasOrgSponsoredFreeFamilyPlan = !string.IsNullOrWhiteSpace(OrgSponsoredFreeFamilyPlanToken); + var hasEmergencyAccessInvite = !string.IsNullOrWhiteSpace(AcceptEmergencyAccessInviteToken) && AcceptEmergencyAccessId.HasValue; + var hasProviderInvite = !string.IsNullOrWhiteSpace(ProviderInviteToken) && ProviderUserId.HasValue; + var tokenCount = (hasEmailVerification ? 1 : 0) + + (hasOrgInvite ? 1 : 0) + + (hasOrgSponsoredFreeFamilyPlan ? 1 : 0) + + (hasEmergencyAccessInvite ? 1 : 0) + + (hasProviderInvite ? 1 : 0); + if (tokenCount == 0) + { + throw new BadRequestException("Invalid registration finish request"); + } + if (tokenCount > 1) + { + throw new BadRequestException("Multiple registration token types provided."); + } - // PM-28143 - Remove line below - var kdfMemory = MasterPasswordUnlock?.Kdf.Memory - ?? KdfMemory; + IEnumerable kdfValidationResults; + if (MasterPasswordUnlock != null && MasterPasswordAuthentication != null) + { + kdfValidationResults = KdfSettingsValidator.Validate(MasterPasswordUnlock.ToData()); + } + else + { + kdfValidationResults = KdfSettingsValidator.Validate( + Kdf ?? throw new BadRequestException($"{nameof(Kdf)} not found on RequestModel"), + KdfIterations ?? throw new BadRequestException($"{nameof(KdfIterations)} not found on RequestModel"), + KdfMemory, + KdfParallelism); + } - // PM-28143 - Remove line below - var kdfParallelism = MasterPasswordUnlock?.Kdf.Parallelism - ?? KdfParallelism; + // Move token presence validation from controller into the request model + switch (GetTokenType()) + { + case RegisterFinishTokenType.EmailVerification: + if (string.IsNullOrEmpty(EmailVerificationToken)) + { + throw new BadRequestException("Email verification token absent when processing register/finish."); + } + break; + case RegisterFinishTokenType.OrganizationInvite: + if (string.IsNullOrEmpty(OrgInviteToken)) + { + throw new BadRequestException("Organization invite token absent when processing register/finish."); + } + break; + case RegisterFinishTokenType.OrgSponsoredFreeFamilyPlan: + if (string.IsNullOrEmpty(OrgSponsoredFreeFamilyPlanToken)) + { + throw new BadRequestException("Organization sponsored free family plan token absent when processing register/finish."); + } + break; + case RegisterFinishTokenType.EmergencyAccessInvite: + if (string.IsNullOrEmpty(AcceptEmergencyAccessInviteToken)) + { + throw new BadRequestException("Accept emergency access invite token absent when processing register/finish."); + } + if (!AcceptEmergencyAccessId.HasValue || AcceptEmergencyAccessId.Value == Guid.Empty) + { + throw new BadRequestException("Accept emergency access id absent when processing register/finish."); + } + break; + case RegisterFinishTokenType.ProviderInvite: + if (string.IsNullOrEmpty(ProviderInviteToken)) + { + throw new BadRequestException("Provider invite token absent when processing register/finish."); + } + if (!ProviderUserId.HasValue || ProviderUserId.Value == Guid.Empty) + { + throw new BadRequestException("Provider user id absent when processing register/finish."); + } + break; + default: + throw new BadRequestException("Invalid registration finish request"); + } - // PM-28143 - Remove line below in favor of using the unlock data. - return KdfSettingsValidator.Validate(kdf, kdfIterations, kdfMemory, kdfParallelism); + return kdfValidationResults; + } - // PM-28143 - Uncomment - // return KdfSettingsValidator.Validate(MasterPasswordUnlockData); + // PM-28143 - Remove function + private static void ThrowIfExistsAndHashIsNotEqual( + MasterPasswordAuthenticationDataRequestModel? authenticationData, + string? hash) + { + if (authenticationData != null && hash != null) + { + if (authenticationData.MasterPasswordAuthenticationHash != hash) + { + throw new BadRequestException("Master password hash and hash are not equal."); + } + } } } diff --git a/src/Core/KeyManagement/Models/Api/Request/MasterPasswordAuthenticationDataRequestModel.cs b/src/Core/KeyManagement/Models/Api/Request/MasterPasswordAuthenticationDataRequestModel.cs index 8a3283fc5b94..f89c870eff1e 100644 --- a/src/Core/KeyManagement/Models/Api/Request/MasterPasswordAuthenticationDataRequestModel.cs +++ b/src/Core/KeyManagement/Models/Api/Request/MasterPasswordAuthenticationDataRequestModel.cs @@ -3,6 +3,10 @@ namespace Bit.Core.KeyManagement.Models.Api.Request; +/// +/// Use this datatype when interfacing with requests to create a separation of concern. +/// See to +/// public class MasterPasswordAuthenticationDataRequestModel { public required KdfRequestModel Kdf { get; init; } @@ -18,17 +22,4 @@ public MasterPasswordAuthenticationData ToData() Salt = Salt }; } - - public static void ThrowIfExistsAndHashIsNotEqual( - MasterPasswordAuthenticationDataRequestModel? authenticationData, - string? hash) - { - if (authenticationData != null && hash != null) - { - if (authenticationData.MasterPasswordAuthenticationHash != hash) - { - throw new Exception("Master password hash and hash are not equal."); - } - } - } } diff --git a/src/Core/KeyManagement/Models/Data/KdfRequestModel.cs b/src/Core/KeyManagement/Models/Data/KdfRequestModel.cs index ae38490e0036..ea84fa12d275 100644 --- a/src/Core/KeyManagement/Models/Data/KdfRequestModel.cs +++ b/src/Core/KeyManagement/Models/Data/KdfRequestModel.cs @@ -1,9 +1,10 @@ using System.ComponentModel.DataAnnotations; using Bit.Core.Enums; +using Bit.Core.Utilities; namespace Bit.Core.KeyManagement.Models.Data; -public class KdfRequestModel +public class KdfRequestModel : IValidatableObject { [Required] public required KdfType KdfType { get; init; } @@ -22,4 +23,10 @@ public KdfSettings ToData() Parallelism = Parallelism }; } + + public IEnumerable Validate(ValidationContext validationContext) + { + // Generic per-request KDF validation for any request model embedding KdfRequestModel + return KdfSettingsValidator.Validate(ToData()); + } } diff --git a/src/Core/Utilities/KdfSettingsValidator.cs b/src/Core/Utilities/KdfSettingsValidator.cs index be444d826da3..07ce73907d3c 100644 --- a/src/Core/Utilities/KdfSettingsValidator.cs +++ b/src/Core/Utilities/KdfSettingsValidator.cs @@ -37,7 +37,6 @@ public static IEnumerable Validate(KdfType kdfType, int kdfIte } } - // PM-28143 - Will be used in the referenced ticket. public static IEnumerable Validate(MasterPasswordUnlockData masterPasswordUnlockData) { switch (masterPasswordUnlockData.Kdf.KdfType) diff --git a/src/Identity/Controllers/AccountsController.cs b/src/Identity/Controllers/AccountsController.cs index 06ee097f3212..f079d39770e9 100644 --- a/src/Identity/Controllers/AccountsController.cs +++ b/src/Identity/Controllers/AccountsController.cs @@ -141,16 +141,7 @@ public async Task PostRegisterVerificationEmailClicked([FromBody] [HttpPost("register/finish")] public async Task PostRegisterFinish([FromBody] RegisterFinishRequestModel model) { - User user; - - try - { - user = model.ToUser(); - } - catch (Exception e) - { - throw new BadRequestException(e.Message); - } + User user = model.ToUser(); // Users will either have an emailed token or an email verification token - not both. IdentityResult? identityResult = null; @@ -162,62 +153,41 @@ public async Task PostRegisterFinish([FromBody] Reg switch (model.GetTokenType()) { case RegisterFinishTokenType.EmailVerification: - if (string.IsNullOrEmpty(model.EmailVerificationToken)) - throw new BadRequestException("Email verification token absent when processing register/finish."); - identityResult = await _registerUserCommand.RegisterUserViaEmailVerificationToken( user, masterPasswordHash, - model.EmailVerificationToken); + model.EmailVerificationTokenRequired); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.OrganizationInvite: - if (string.IsNullOrEmpty(model.OrgInviteToken)) - throw new BadRequestException("Organization invite token absent when processing register/finish."); - identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken( user, masterPasswordHash, - model.OrgInviteToken, - model.OrganizationUserId); + model.OrgInviteTokenRequired, + model.OrganizationUserIdRequired); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.OrgSponsoredFreeFamilyPlan: - if (string.IsNullOrEmpty(model.OrgSponsoredFreeFamilyPlanToken)) - throw new BadRequestException("Organization sponsored free family plan token absent when processing register/finish."); - identityResult = await _registerUserCommand.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken( user, masterPasswordHash, - model.OrgSponsoredFreeFamilyPlanToken); + model.OrgSponsoredFreeFamilyPlanTokenRequired); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.EmergencyAccessInvite: - if (string.IsNullOrEmpty(model.AcceptEmergencyAccessInviteToken)) - throw new BadRequestException("Accept emergency access invite token absent when processing register/finish."); - - if (model.AcceptEmergencyAccessId == null || model.AcceptEmergencyAccessId == Guid.Empty) - throw new BadRequestException("Accept emergency access id absent when processing register/finish."); - identityResult = await _registerUserCommand.RegisterUserViaAcceptEmergencyAccessInviteToken( user, masterPasswordHash, - model.AcceptEmergencyAccessInviteToken, - model.AcceptEmergencyAccessId.Value); + model.AcceptEmergencyAccessInviteTokenRequired, + model.AcceptEmergencyAccessIdRequired); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.ProviderInvite: - if (string.IsNullOrEmpty(model.ProviderInviteToken)) - throw new BadRequestException("Provider invite token absent when processing register/finish."); - - if (model.ProviderUserId == null || model.ProviderUserId == Guid.Empty) - throw new BadRequestException("Provider user id absent when processing register/finish."); - identityResult = await _registerUserCommand.RegisterUserViaProviderInviteToken( user, masterPasswordHash, - model.ProviderInviteToken, - model.ProviderUserId.Value); + model.ProviderInviteTokenRequired, + model.ProviderUserIdRequired); return ProcessRegistrationResult(identityResult, user); default: diff --git a/test/Identity.Test/Controllers/AccountsControllerTests.cs b/test/Identity.Test/Controllers/AccountsControllerTests.cs index 7c338e141f89..86d407256e0d 100644 --- a/test/Identity.Test/Controllers/AccountsControllerTests.cs +++ b/test/Identity.Test/Controllers/AccountsControllerTests.cs @@ -1085,11 +1085,16 @@ public void RegisterFinishRequestModel_Validate_Throws_WhenUnlockAndAuthDataMism } }; + // Provide a minimal valid token type to satisfy model-level token validation + model.EmailVerificationToken = "test-token"; + var ctx = new ValidationContext(model); - // Act & Assert - var ex = Assert.Throws(() => model.Validate(ctx).ToList()); - Assert.Equal("KDF settings and salt must match between authentication and unlock data.", ex.Message); + // Act + var results = model.Validate(ctx).ToList(); + + // Assert mismatched auth/unlock is allowed + Assert.Empty(results); } [Theory, BitAutoData] @@ -1131,11 +1136,16 @@ public void RegisterFinishRequestModel_Validate_Throws_WhenSaltMismatch( } }; + // Provide a minimal valid token type to satisfy model-level token validation + model.EmailVerificationToken = "test-token"; + var ctx = new ValidationContext(model); - // Act & Assert - var ex = Assert.Throws(() => model.Validate(ctx).ToList()); - Assert.Equal("KDF settings and salt must match between authentication and unlock data.", ex.Message); + // Act + var results = model.Validate(ctx).ToList(); + + // Assert mismatched salts between auth/unlock are allowed + Assert.Empty(results); } [Theory, BitAutoData] @@ -1181,7 +1191,7 @@ public void RegisterFinishRequestModel_Validate_Throws_WhenAuthHashAndRootHashMi var ctx = new ValidationContext(model); // Act & Assert - var ex = Assert.Throws(() => model.Validate(ctx).ToList()); + var ex = Assert.Throws(() => model.Validate(ctx).ToList()); Assert.Equal("Master password hash and hash are not equal.", ex.Message); } From 227f3a01df1d62c65893782b2eba583b6d77e037 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Tue, 30 Dec 2025 22:38:08 -0500 Subject: [PATCH 18/44] comment(register): [PM-27084] Account Register Uses New Data Types - Added comments to clarify how these data types should be used. --- .../Request/MasterPasswordAuthenticationDataRequestModel.cs | 2 +- .../Api/Request/MasterPasswordUnlockDataRequestModel.cs | 4 ++++ .../Models/Data/MasterPasswordAuthenticationData.cs | 5 +++++ .../Data/MasterPasswordUnlockAndAuthenticationData.cs | 6 ++++++ .../KeyManagement/Models/Data/MasterPasswordUnlockData.cs | 5 +++++ 5 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Core/KeyManagement/Models/Api/Request/MasterPasswordAuthenticationDataRequestModel.cs b/src/Core/KeyManagement/Models/Api/Request/MasterPasswordAuthenticationDataRequestModel.cs index f89c870eff1e..f1160782a25f 100644 --- a/src/Core/KeyManagement/Models/Api/Request/MasterPasswordAuthenticationDataRequestModel.cs +++ b/src/Core/KeyManagement/Models/Api/Request/MasterPasswordAuthenticationDataRequestModel.cs @@ -5,7 +5,7 @@ namespace Bit.Core.KeyManagement.Models.Api.Request; /// /// Use this datatype when interfacing with requests to create a separation of concern. -/// See to +/// See to use for commands, queries, services. /// public class MasterPasswordAuthenticationDataRequestModel { diff --git a/src/Core/KeyManagement/Models/Api/Request/MasterPasswordUnlockDataRequestModel.cs b/src/Core/KeyManagement/Models/Api/Request/MasterPasswordUnlockDataRequestModel.cs index 9b4e9599d3f0..e3dfd2fb7991 100644 --- a/src/Core/KeyManagement/Models/Api/Request/MasterPasswordUnlockDataRequestModel.cs +++ b/src/Core/KeyManagement/Models/Api/Request/MasterPasswordUnlockDataRequestModel.cs @@ -4,6 +4,10 @@ namespace Bit.Core.KeyManagement.Models.Api.Request; +/// +/// Use this datatype when interfacing with requests to create a separation of concern. +/// See to use for commands, queries, services. +/// public class MasterPasswordUnlockDataRequestModel { public required KdfRequestModel Kdf { get; init; } diff --git a/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs b/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs index 1bc7006ceff5..6e53dfa744a4 100644 --- a/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs +++ b/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs @@ -1,8 +1,13 @@ using Bit.Core.Entities; using Bit.Core.Exceptions; +using Bit.Core.KeyManagement.Models.Api.Request; namespace Bit.Core.KeyManagement.Models.Data; +/// +/// Use this datatype when interfacing with commands, queries, services to create a separation of concern. +/// See to use for requests. +/// public class MasterPasswordAuthenticationData { public required KdfSettings Kdf { get; init; } diff --git a/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockAndAuthenticationData.cs b/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockAndAuthenticationData.cs index ad3a0b692b2f..e60cb3bb952c 100644 --- a/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockAndAuthenticationData.cs +++ b/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockAndAuthenticationData.cs @@ -4,6 +4,12 @@ namespace Bit.Core.KeyManagement.Models.Data; +/// +/// Probably shouldn't be used as we want to make sure that the unlock data and authentication data +/// can use separate kdf settings. +/// +/// Should be cleaned up in the near future. +/// public class MasterPasswordUnlockAndAuthenticationData { public KdfType KdfType { get; set; } diff --git a/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs b/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs index cb18ed2a78c2..f8139cba99ff 100644 --- a/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs +++ b/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs @@ -1,8 +1,13 @@ using Bit.Core.Entities; using Bit.Core.Exceptions; +using Bit.Core.KeyManagement.Models.Api.Request; namespace Bit.Core.KeyManagement.Models.Data; +/// +/// Use this datatype when interfacing with commands, queries, services to create a separation of concern. +/// See to use for requests. +/// public class MasterPasswordUnlockData { public required KdfSettings Kdf { get; init; } From fb54d21cbcef941ea0a1c82033dbd5d8dd636682 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Wed, 31 Dec 2025 10:07:29 -0500 Subject: [PATCH 19/44] comment(register): [PM-27084] Account Register Uses New Data Types - Fixed import. --- test/Api.IntegrationTest/Controllers/AccountsControllerTest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Api.IntegrationTest/Controllers/AccountsControllerTest.cs b/test/Api.IntegrationTest/Controllers/AccountsControllerTest.cs index 09ec5b010fc3..f0312bca780d 100644 --- a/test/Api.IntegrationTest/Controllers/AccountsControllerTest.cs +++ b/test/Api.IntegrationTest/Controllers/AccountsControllerTest.cs @@ -2,11 +2,12 @@ using Bit.Api.Auth.Models.Request.Accounts; using Bit.Api.IntegrationTest.Factories; using Bit.Api.IntegrationTest.Helpers; -using Bit.Api.KeyManagement.Models.Requests; using Bit.Api.Models.Response; using Bit.Core; using Bit.Core.Entities; using Bit.Core.Enums; +using Bit.Core.KeyManagement.Models.Api.Request; +using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Platform.Push; using Bit.Core.Repositories; using Bit.Core.Services; From b0cce70c59200603fe327fcbe62014fc80a60b10 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Wed, 31 Dec 2025 13:57:23 -0500 Subject: [PATCH 20/44] comment(register): [PM-27084] Account Register Uses New Data Types - Updated docs around the validation --- .../Accounts/RegisterFinishRequestModel.cs | 60 +++++++++++-------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index cff5e68062ac..891bb8fd00b4 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -63,38 +63,47 @@ public class RegisterFinishRequestModel : IValidatableObject public Guid? ProviderUserId { get; set; } // Strongly-typed accessors for post-validation usage to satisfy nullability + // Ignore serialization, these are just null safe accessors. [System.Text.Json.Serialization.JsonIgnore] [Newtonsoft.Json.JsonIgnore] public string EmailVerificationTokenRequired => - EmailVerificationToken ?? throw new BadRequestException("Email verification token absent when processing register/finish."); + EmailVerificationToken + ?? throw new BadRequestException("Email verification token absent when processing register/finish."); [System.Text.Json.Serialization.JsonIgnore] [Newtonsoft.Json.JsonIgnore] public string OrgInviteTokenRequired => - OrgInviteToken ?? throw new BadRequestException("Organization invite token absent when processing register/finish."); + OrgInviteToken + ?? throw new BadRequestException("Organization invite token absent when processing register/finish."); [System.Text.Json.Serialization.JsonIgnore] [Newtonsoft.Json.JsonIgnore] public Guid OrganizationUserIdRequired => - OrganizationUserId ?? throw new BadRequestException("Organization user id absent when processing register/finish."); + OrganizationUserId + ?? throw new BadRequestException("Organization user id absent when processing register/finish."); [System.Text.Json.Serialization.JsonIgnore] [Newtonsoft.Json.JsonIgnore] public string OrgSponsoredFreeFamilyPlanTokenRequired => - OrgSponsoredFreeFamilyPlanToken ?? throw new BadRequestException("Organization sponsored free family plan token absent when processing register/finish."); + OrgSponsoredFreeFamilyPlanToken + ?? throw new BadRequestException("Organization sponsored free family plan token absent when processing register/finish."); [System.Text.Json.Serialization.JsonIgnore] [Newtonsoft.Json.JsonIgnore] public string AcceptEmergencyAccessInviteTokenRequired => - AcceptEmergencyAccessInviteToken ?? throw new BadRequestException("Accept emergency access invite token absent when processing register/finish."); + AcceptEmergencyAccessInviteToken + ?? throw new BadRequestException("Accept emergency access invite token absent when processing register/finish."); [System.Text.Json.Serialization.JsonIgnore] [Newtonsoft.Json.JsonIgnore] public Guid AcceptEmergencyAccessIdRequired => - AcceptEmergencyAccessId ?? throw new BadRequestException("Accept emergency access id absent when processing register/finish."); + AcceptEmergencyAccessId + ?? throw new BadRequestException("Accept emergency access id absent when processing register/finish."); [System.Text.Json.Serialization.JsonIgnore] [Newtonsoft.Json.JsonIgnore] public string ProviderInviteTokenRequired => - ProviderInviteToken ?? throw new BadRequestException("Provider invite token absent when processing register/finish."); + ProviderInviteToken + ?? throw new BadRequestException("Provider invite token absent when processing register/finish."); [System.Text.Json.Serialization.JsonIgnore] [Newtonsoft.Json.JsonIgnore] public Guid ProviderUserIdRequired => - ProviderUserId ?? throw new BadRequestException("Provider user id absent when processing register/finish."); + ProviderUserId + ?? throw new BadRequestException("Provider user id absent when processing register/finish."); public User ToUser() { @@ -110,7 +119,7 @@ public User ToUser() KdfMemory = MasterPasswordUnlock?.Kdf.Memory ?? KdfMemory, KdfParallelism = MasterPasswordUnlock?.Kdf.Parallelism ?? KdfParallelism, // PM-28827 To be added when MasterPasswordSalt is added to the user column - // MasterPasswordSalt = MasterPasswordUnlockData?.Salt ?? Email.ToLower().Trim(), + // MasterPasswordSalt = MasterPasswordUnlock?.Salt ?? Email.ToLower().Trim(), Key = MasterPasswordUnlock?.MasterKeyWrappedUserKey ?? UserSymmetricKey ?? throw new BadRequestException("MasterKeyWrappedUserKey couldn't be found on either the MasterPasswordUnlockData or the UserSymmetricKey property passed in."), }; @@ -150,6 +159,8 @@ public IEnumerable Validate(ValidationContext validationContex // PM-28143 - Remove this check ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash); + // 1. Access Token Presence Verification Check + // Ensure exactly one registration token type is provided var hasEmailVerification = !string.IsNullOrWhiteSpace(EmailVerificationToken); var hasOrgInvite = !string.IsNullOrEmpty(OrgInviteToken) && OrganizationUserId.HasValue; @@ -170,21 +181,6 @@ public IEnumerable Validate(ValidationContext validationContex throw new BadRequestException("Multiple registration token types provided."); } - IEnumerable kdfValidationResults; - if (MasterPasswordUnlock != null && MasterPasswordAuthentication != null) - { - kdfValidationResults = KdfSettingsValidator.Validate(MasterPasswordUnlock.ToData()); - } - else - { - kdfValidationResults = KdfSettingsValidator.Validate( - Kdf ?? throw new BadRequestException($"{nameof(Kdf)} not found on RequestModel"), - KdfIterations ?? throw new BadRequestException($"{nameof(KdfIterations)} not found on RequestModel"), - KdfMemory, - KdfParallelism); - } - - // Move token presence validation from controller into the request model switch (GetTokenType()) { case RegisterFinishTokenType.EmailVerification: @@ -229,6 +225,22 @@ public IEnumerable Validate(ValidationContext validationContex throw new BadRequestException("Invalid registration finish request"); } + // 2. Validate kdf settings. + + IEnumerable kdfValidationResults; + if (MasterPasswordUnlock != null && MasterPasswordAuthentication != null) + { + kdfValidationResults = KdfSettingsValidator.Validate(MasterPasswordUnlock.ToData()); + } + else + { + kdfValidationResults = KdfSettingsValidator.Validate( + Kdf ?? throw new BadRequestException($"{nameof(Kdf)} not found on RequestModel"), + KdfIterations ?? throw new BadRequestException($"{nameof(KdfIterations)} not found on RequestModel"), + KdfMemory, + KdfParallelism); + } + return kdfValidationResults; } From af2f70403381b0ee1e04521ab339ee52177ebaca Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Wed, 31 Dec 2025 17:01:21 -0500 Subject: [PATCH 21/44] comment(register): [PM-27084] Account Register Uses New Data Types - Removed troublesome null safeguarding. --- perf/logs/debug.ndjson | 498 ++++++++++++++++++ .../Accounts/RegisterFinishRequestModel.cs | 69 +-- .../Controllers/AccountsController.cs | 34 +- .../Factories/IdentityApplicationFactory.cs | 2 +- 4 files changed, 537 insertions(+), 66 deletions(-) create mode 100644 perf/logs/debug.ndjson diff --git a/perf/logs/debug.ndjson b/perf/logs/debug.ndjson new file mode 100644 index 000000000000..96fbe5d6b129 --- /dev/null +++ b/perf/logs/debug.ndjson @@ -0,0 +1,498 @@ +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-teste286b7f0-b730-48d6-b7ba-a2629a9ed0d8@bitwarden.com","status":204},"timestamp":1767216787835} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-teste286b7f0-b730-48d6-b7ba-a2629a9ed0d8@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767216787840} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-teste286b7f0-b730-48d6-b7ba-a2629a9ed0d8@bitwarden.com","status":400},"timestamp":1767216787865} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-teste286b7f0-b730-48d6-b7ba-a2629a9ed0d8@bitwarden.com","actual":400,"expected":200},"timestamp":1767216787865} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test9b9bf154-f5ed-49ae-8dbe-2869b8ec9e27@bitwarden.com","status":204},"timestamp":1767216964948} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test9b9bf154-f5ed-49ae-8dbe-2869b8ec9e27@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767216964956} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test9b9bf154-f5ed-49ae-8dbe-2869b8ec9e27@bitwarden.com","status":400},"timestamp":1767216964984} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test9b9bf154-f5ed-49ae-8dbe-2869b8ec9e27@bitwarden.com","actual":400,"expected":200},"timestamp":1767216964984} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test76636f55-7232-42c6-8809-32866e17f533@bitwarden.com","status":204},"timestamp":1767217020454} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test76636f55-7232-42c6-8809-32866e17f533@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020459} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test76636f55-7232-42c6-8809-32866e17f533@bitwarden.com","status":400},"timestamp":1767217020483} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test76636f55-7232-42c6-8809-32866e17f533@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020483} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test16c2bb43-8805-47d8-b126-599de6bc1071@bitwarden.com","status":204},"timestamp":1767217020510} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test16c2bb43-8805-47d8-b126-599de6bc1071@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020510} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test16c2bb43-8805-47d8-b126-599de6bc1071@bitwarden.com","status":400},"timestamp":1767217020511} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test16c2bb43-8805-47d8-b126-599de6bc1071@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020511} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test36321664-7dd7-4bc3-885d-ae4eea7d98d2@bitwarden.com","status":204},"timestamp":1767217020512} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test36321664-7dd7-4bc3-885d-ae4eea7d98d2@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020513} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test36321664-7dd7-4bc3-885d-ae4eea7d98d2@bitwarden.com","status":400},"timestamp":1767217020513} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test36321664-7dd7-4bc3-885d-ae4eea7d98d2@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020513} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test07b40214-c78d-4515-b84d-af7f949c510e@bitwarden.com","status":204},"timestamp":1767217020514} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test07b40214-c78d-4515-b84d-af7f949c510e@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020514} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test07b40214-c78d-4515-b84d-af7f949c510e@bitwarden.com","status":400},"timestamp":1767217020514} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test07b40214-c78d-4515-b84d-af7f949c510e@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020515} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test727329d5-e222-4961-a30f-d4e4e93d63f0@bitwarden.com","status":204},"timestamp":1767217020515} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test727329d5-e222-4961-a30f-d4e4e93d63f0@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020515} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test727329d5-e222-4961-a30f-d4e4e93d63f0@bitwarden.com","status":400},"timestamp":1767217020516} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test727329d5-e222-4961-a30f-d4e4e93d63f0@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020516} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testbef4e89d-cf16-4d2b-b7b5-7e9adebb1992@bitwarden.com","status":204},"timestamp":1767217020517} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testbef4e89d-cf16-4d2b-b7b5-7e9adebb1992@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020517} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testbef4e89d-cf16-4d2b-b7b5-7e9adebb1992@bitwarden.com","status":400},"timestamp":1767217020517} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testbef4e89d-cf16-4d2b-b7b5-7e9adebb1992@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020517} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test12d4d6ea-949c-4728-9217-2dfc70ba2844@bitwarden.com","status":204},"timestamp":1767217020518} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test12d4d6ea-949c-4728-9217-2dfc70ba2844@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020518} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test12d4d6ea-949c-4728-9217-2dfc70ba2844@bitwarden.com","status":400},"timestamp":1767217020518} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test12d4d6ea-949c-4728-9217-2dfc70ba2844@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020519} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test6f98e688-7780-4fb5-be65-c48d22fe5e47@bitwarden.com","status":204},"timestamp":1767217020519} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test6f98e688-7780-4fb5-be65-c48d22fe5e47@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020519} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test6f98e688-7780-4fb5-be65-c48d22fe5e47@bitwarden.com","status":400},"timestamp":1767217020520} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test6f98e688-7780-4fb5-be65-c48d22fe5e47@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020520} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testa299ba20-7b83-4f48-9245-0cb5217ee060@bitwarden.com","status":204},"timestamp":1767217020521} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testa299ba20-7b83-4f48-9245-0cb5217ee060@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020521} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testa299ba20-7b83-4f48-9245-0cb5217ee060@bitwarden.com","status":400},"timestamp":1767217020521} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testa299ba20-7b83-4f48-9245-0cb5217ee060@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020521} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test1494b8fc-e12b-4309-b266-e70a5aa5cd38@bitwarden.com","status":204},"timestamp":1767217020522} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test1494b8fc-e12b-4309-b266-e70a5aa5cd38@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020522} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test1494b8fc-e12b-4309-b266-e70a5aa5cd38@bitwarden.com","status":400},"timestamp":1767217020522} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test1494b8fc-e12b-4309-b266-e70a5aa5cd38@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020523} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testb9a59f9c-d3cc-421a-bbe1-3b091cf44b00@bitwarden.com","status":204},"timestamp":1767217020523} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testb9a59f9c-d3cc-421a-bbe1-3b091cf44b00@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020523} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testb9a59f9c-d3cc-421a-bbe1-3b091cf44b00@bitwarden.com","status":400},"timestamp":1767217020524} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testb9a59f9c-d3cc-421a-bbe1-3b091cf44b00@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020524} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test0bbdda90-34c4-4012-9758-bbc5b9dde0d0@bitwarden.com","status":204},"timestamp":1767217020525} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test0bbdda90-34c4-4012-9758-bbc5b9dde0d0@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020525} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test0bbdda90-34c4-4012-9758-bbc5b9dde0d0@bitwarden.com","status":400},"timestamp":1767217020525} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test0bbdda90-34c4-4012-9758-bbc5b9dde0d0@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020525} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test3adc0011-03f4-4d6c-8a65-a31e3c103030@bitwarden.com","status":204},"timestamp":1767217020526} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test3adc0011-03f4-4d6c-8a65-a31e3c103030@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020526} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test3adc0011-03f4-4d6c-8a65-a31e3c103030@bitwarden.com","status":400},"timestamp":1767217020526} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test3adc0011-03f4-4d6c-8a65-a31e3c103030@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020526} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testd700873d-b6fb-4aa1-a3f1-47678deb6126@bitwarden.com","status":204},"timestamp":1767217020527} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testd700873d-b6fb-4aa1-a3f1-47678deb6126@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020527} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testd700873d-b6fb-4aa1-a3f1-47678deb6126@bitwarden.com","status":400},"timestamp":1767217020527} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testd700873d-b6fb-4aa1-a3f1-47678deb6126@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020527} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testbd1a71d9-fc52-4628-b26c-fdc2c349a5a3@bitwarden.com","status":204},"timestamp":1767217020528} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testbd1a71d9-fc52-4628-b26c-fdc2c349a5a3@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020528} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testbd1a71d9-fc52-4628-b26c-fdc2c349a5a3@bitwarden.com","status":400},"timestamp":1767217020529} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testbd1a71d9-fc52-4628-b26c-fdc2c349a5a3@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020529} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test980cfd2c-a566-4b6e-9e35-414cb00998a9@bitwarden.com","status":204},"timestamp":1767217020529} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test980cfd2c-a566-4b6e-9e35-414cb00998a9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020529} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test980cfd2c-a566-4b6e-9e35-414cb00998a9@bitwarden.com","status":400},"timestamp":1767217020530} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test980cfd2c-a566-4b6e-9e35-414cb00998a9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020530} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test11049bfb-2c3c-4364-aa1e-99c397aea172@bitwarden.com","status":204},"timestamp":1767217020531} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test11049bfb-2c3c-4364-aa1e-99c397aea172@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020531} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test11049bfb-2c3c-4364-aa1e-99c397aea172@bitwarden.com","status":400},"timestamp":1767217020531} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test11049bfb-2c3c-4364-aa1e-99c397aea172@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020531} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testbd6b6099-beeb-4bbb-ad5a-85211d6f7fbb@bitwarden.com","status":204},"timestamp":1767217020532} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testbd6b6099-beeb-4bbb-ad5a-85211d6f7fbb@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020532} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testbd6b6099-beeb-4bbb-ad5a-85211d6f7fbb@bitwarden.com","status":400},"timestamp":1767217020532} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testbd6b6099-beeb-4bbb-ad5a-85211d6f7fbb@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020532} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test43502847-1dd6-434b-84b1-201a7e5f3de9@bitwarden.com","status":204},"timestamp":1767217020533} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test43502847-1dd6-434b-84b1-201a7e5f3de9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020533} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test43502847-1dd6-434b-84b1-201a7e5f3de9@bitwarden.com","status":400},"timestamp":1767217020533} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test43502847-1dd6-434b-84b1-201a7e5f3de9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020533} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test2ea77a32-13b0-40aa-977a-2b867cb4b31e@bitwarden.com","status":204},"timestamp":1767217020534} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test2ea77a32-13b0-40aa-977a-2b867cb4b31e@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020534} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test2ea77a32-13b0-40aa-977a-2b867cb4b31e@bitwarden.com","status":400},"timestamp":1767217020535} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test2ea77a32-13b0-40aa-977a-2b867cb4b31e@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020535} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testea097f7a-9fc2-437d-8f15-34f369d0102e@bitwarden.com","status":204},"timestamp":1767217020535} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testea097f7a-9fc2-437d-8f15-34f369d0102e@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020535} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testea097f7a-9fc2-437d-8f15-34f369d0102e@bitwarden.com","status":400},"timestamp":1767217020536} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testea097f7a-9fc2-437d-8f15-34f369d0102e@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020536} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test68eec49a-18cd-4089-8a71-ce7ddc89f9fc@bitwarden.com","status":204},"timestamp":1767217020536} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test68eec49a-18cd-4089-8a71-ce7ddc89f9fc@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020537} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test68eec49a-18cd-4089-8a71-ce7ddc89f9fc@bitwarden.com","status":400},"timestamp":1767217020537} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test68eec49a-18cd-4089-8a71-ce7ddc89f9fc@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020537} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test667f4950-0904-42f4-add9-2c687737e28c@bitwarden.com","status":204},"timestamp":1767217020538} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test667f4950-0904-42f4-add9-2c687737e28c@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020538} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test667f4950-0904-42f4-add9-2c687737e28c@bitwarden.com","status":400},"timestamp":1767217020538} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test667f4950-0904-42f4-add9-2c687737e28c@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020538} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test85f7d187-e41e-4408-ad91-05d1f6ff819f@bitwarden.com","status":204},"timestamp":1767217020539} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test85f7d187-e41e-4408-ad91-05d1f6ff819f@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020539} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test85f7d187-e41e-4408-ad91-05d1f6ff819f@bitwarden.com","status":400},"timestamp":1767217020539} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test85f7d187-e41e-4408-ad91-05d1f6ff819f@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020539} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testc128c32b-89f9-4f8f-a5f9-cd5dc3b5f560@bitwarden.com","status":204},"timestamp":1767217020540} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testc128c32b-89f9-4f8f-a5f9-cd5dc3b5f560@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020540} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testc128c32b-89f9-4f8f-a5f9-cd5dc3b5f560@bitwarden.com","status":400},"timestamp":1767217020540} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testc128c32b-89f9-4f8f-a5f9-cd5dc3b5f560@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020541} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test7bb19614-a3e6-44d9-811f-c1daf336d0d9@bitwarden.com","status":204},"timestamp":1767217020541} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test7bb19614-a3e6-44d9-811f-c1daf336d0d9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020541} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test7bb19614-a3e6-44d9-811f-c1daf336d0d9@bitwarden.com","status":400},"timestamp":1767217020542} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test7bb19614-a3e6-44d9-811f-c1daf336d0d9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020542} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testfd53fb58-c9b9-4a76-9c5e-cf2a38582ec6@bitwarden.com","status":204},"timestamp":1767217020542} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testfd53fb58-c9b9-4a76-9c5e-cf2a38582ec6@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020542} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testfd53fb58-c9b9-4a76-9c5e-cf2a38582ec6@bitwarden.com","status":400},"timestamp":1767217020543} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testfd53fb58-c9b9-4a76-9c5e-cf2a38582ec6@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020543} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testb5c37c94-67c3-423a-b263-eba1fc3d2f2b@bitwarden.com","status":204},"timestamp":1767217020544} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testb5c37c94-67c3-423a-b263-eba1fc3d2f2b@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020544} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testb5c37c94-67c3-423a-b263-eba1fc3d2f2b@bitwarden.com","status":400},"timestamp":1767217020544} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testb5c37c94-67c3-423a-b263-eba1fc3d2f2b@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020544} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test65447f69-9213-47e2-bf99-609eb03bd7e8@bitwarden.com","status":204},"timestamp":1767217020545} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test65447f69-9213-47e2-bf99-609eb03bd7e8@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020545} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test65447f69-9213-47e2-bf99-609eb03bd7e8@bitwarden.com","status":400},"timestamp":1767217020545} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test65447f69-9213-47e2-bf99-609eb03bd7e8@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020545} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test05b927c5-10e6-4a4e-8cb4-375f42f0da4b@bitwarden.com","status":204},"timestamp":1767217020546} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test05b927c5-10e6-4a4e-8cb4-375f42f0da4b@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020546} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test05b927c5-10e6-4a4e-8cb4-375f42f0da4b@bitwarden.com","status":400},"timestamp":1767217020546} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test05b927c5-10e6-4a4e-8cb4-375f42f0da4b@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020547} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test282e9e9e-b455-4e02-b81a-6fed3d66d471@bitwarden.com","status":204},"timestamp":1767217020547} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test282e9e9e-b455-4e02-b81a-6fed3d66d471@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020547} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test282e9e9e-b455-4e02-b81a-6fed3d66d471@bitwarden.com","status":400},"timestamp":1767217020548} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test282e9e9e-b455-4e02-b81a-6fed3d66d471@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020548} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test5a359b82-080e-4ae3-a374-b02e46221b6a@bitwarden.com","status":204},"timestamp":1767217020549} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test5a359b82-080e-4ae3-a374-b02e46221b6a@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020549} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test5a359b82-080e-4ae3-a374-b02e46221b6a@bitwarden.com","status":400},"timestamp":1767217020549} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test5a359b82-080e-4ae3-a374-b02e46221b6a@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020550} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test56ca8f0c-ff9b-49a2-a781-0d4bb08717f2@bitwarden.com","status":204},"timestamp":1767217020551} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test56ca8f0c-ff9b-49a2-a781-0d4bb08717f2@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020551} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test56ca8f0c-ff9b-49a2-a781-0d4bb08717f2@bitwarden.com","status":400},"timestamp":1767217020552} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test56ca8f0c-ff9b-49a2-a781-0d4bb08717f2@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020552} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test2abb5376-14ae-421d-8b54-1c5ec7951ad6@bitwarden.com","status":204},"timestamp":1767217020553} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test2abb5376-14ae-421d-8b54-1c5ec7951ad6@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020553} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test2abb5376-14ae-421d-8b54-1c5ec7951ad6@bitwarden.com","status":400},"timestamp":1767217020553} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test2abb5376-14ae-421d-8b54-1c5ec7951ad6@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020553} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test684997a1-5339-4944-b639-8894ba27de74@bitwarden.com","status":204},"timestamp":1767217020554} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test684997a1-5339-4944-b639-8894ba27de74@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020554} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test684997a1-5339-4944-b639-8894ba27de74@bitwarden.com","status":400},"timestamp":1767217020555} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test684997a1-5339-4944-b639-8894ba27de74@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020555} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testc4fad0d2-047e-4496-ba98-3dc8b5735be9@bitwarden.com","status":204},"timestamp":1767217020556} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testc4fad0d2-047e-4496-ba98-3dc8b5735be9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020556} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testc4fad0d2-047e-4496-ba98-3dc8b5735be9@bitwarden.com","status":400},"timestamp":1767217020556} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testc4fad0d2-047e-4496-ba98-3dc8b5735be9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020556} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testb832b0aa-4fcd-4eab-baec-909d63e3a0c4@bitwarden.com","status":204},"timestamp":1767217020557} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testb832b0aa-4fcd-4eab-baec-909d63e3a0c4@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020557} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testb832b0aa-4fcd-4eab-baec-909d63e3a0c4@bitwarden.com","status":400},"timestamp":1767217020558} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testb832b0aa-4fcd-4eab-baec-909d63e3a0c4@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020558} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test92c7eb22-6169-407b-9fe3-f53f250e6e75@bitwarden.com","status":204},"timestamp":1767217020559} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test92c7eb22-6169-407b-9fe3-f53f250e6e75@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020559} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test92c7eb22-6169-407b-9fe3-f53f250e6e75@bitwarden.com","status":400},"timestamp":1767217020559} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test92c7eb22-6169-407b-9fe3-f53f250e6e75@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020559} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test940fc891-93bb-4551-874c-553fbbe3a9c9@bitwarden.com","status":204},"timestamp":1767217020560} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test940fc891-93bb-4551-874c-553fbbe3a9c9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020560} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test940fc891-93bb-4551-874c-553fbbe3a9c9@bitwarden.com","status":400},"timestamp":1767217020561} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test940fc891-93bb-4551-874c-553fbbe3a9c9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020561} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testf1524f7f-d5cc-411f-90be-2e683820045c@bitwarden.com","status":204},"timestamp":1767217020562} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testf1524f7f-d5cc-411f-90be-2e683820045c@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020562} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testf1524f7f-d5cc-411f-90be-2e683820045c@bitwarden.com","status":400},"timestamp":1767217020562} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testf1524f7f-d5cc-411f-90be-2e683820045c@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020563} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test47bf8e0e-aa62-4cae-950e-1c6369c3cbe0@bitwarden.com","status":204},"timestamp":1767217020563} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test47bf8e0e-aa62-4cae-950e-1c6369c3cbe0@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020563} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test47bf8e0e-aa62-4cae-950e-1c6369c3cbe0@bitwarden.com","status":400},"timestamp":1767217020564} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test47bf8e0e-aa62-4cae-950e-1c6369c3cbe0@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020564} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testbfe7fe00-309b-40cb-bff9-f1c9eeb4527c@bitwarden.com","status":204},"timestamp":1767217020565} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testbfe7fe00-309b-40cb-bff9-f1c9eeb4527c@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020565} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testbfe7fe00-309b-40cb-bff9-f1c9eeb4527c@bitwarden.com","status":400},"timestamp":1767217020565} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testbfe7fe00-309b-40cb-bff9-f1c9eeb4527c@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020565} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test6096f30c-f92f-4fa8-90db-d5eecb490054@bitwarden.com","status":204},"timestamp":1767217020566} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test6096f30c-f92f-4fa8-90db-d5eecb490054@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020566} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test6096f30c-f92f-4fa8-90db-d5eecb490054@bitwarden.com","status":400},"timestamp":1767217020567} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test6096f30c-f92f-4fa8-90db-d5eecb490054@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020567} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testa829ff2a-5db9-43cb-917f-3606575d08b6@bitwarden.com","status":204},"timestamp":1767217020567} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testa829ff2a-5db9-43cb-917f-3606575d08b6@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020568} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testa829ff2a-5db9-43cb-917f-3606575d08b6@bitwarden.com","status":400},"timestamp":1767217020568} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testa829ff2a-5db9-43cb-917f-3606575d08b6@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020568} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test47bca56c-71f3-40f5-a357-a0c05b22c1d2@bitwarden.com","status":204},"timestamp":1767217020569} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test47bca56c-71f3-40f5-a357-a0c05b22c1d2@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020569} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test47bca56c-71f3-40f5-a357-a0c05b22c1d2@bitwarden.com","status":400},"timestamp":1767217020569} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test47bca56c-71f3-40f5-a357-a0c05b22c1d2@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020569} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test0d3eb1e0-2db7-49cf-ae69-02c6dbfa8da9@bitwarden.com","status":204},"timestamp":1767217020570} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test0d3eb1e0-2db7-49cf-ae69-02c6dbfa8da9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020570} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test0d3eb1e0-2db7-49cf-ae69-02c6dbfa8da9@bitwarden.com","status":400},"timestamp":1767217020571} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test0d3eb1e0-2db7-49cf-ae69-02c6dbfa8da9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020571} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test9d0f3ec8-7e02-4cb7-955a-0b075127ae29@bitwarden.com","status":204},"timestamp":1767217020572} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test9d0f3ec8-7e02-4cb7-955a-0b075127ae29@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020572} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test9d0f3ec8-7e02-4cb7-955a-0b075127ae29@bitwarden.com","status":400},"timestamp":1767217020572} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test9d0f3ec8-7e02-4cb7-955a-0b075127ae29@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020572} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test5db9fa5c-7ad6-4d2d-98b8-2b42d8cae01e@bitwarden.com","status":204},"timestamp":1767217020573} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test5db9fa5c-7ad6-4d2d-98b8-2b42d8cae01e@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020573} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test5db9fa5c-7ad6-4d2d-98b8-2b42d8cae01e@bitwarden.com","status":400},"timestamp":1767217020573} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test5db9fa5c-7ad6-4d2d-98b8-2b42d8cae01e@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020573} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testbd7b099e-1ac0-4c26-971f-26aa00766b2d@bitwarden.com","status":204},"timestamp":1767217020574} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testbd7b099e-1ac0-4c26-971f-26aa00766b2d@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020574} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testbd7b099e-1ac0-4c26-971f-26aa00766b2d@bitwarden.com","status":400},"timestamp":1767217020575} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testbd7b099e-1ac0-4c26-971f-26aa00766b2d@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020575} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test5c60f7d9-e411-47ea-bd92-227d4cd1a528@bitwarden.com","status":204},"timestamp":1767217020576} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test5c60f7d9-e411-47ea-bd92-227d4cd1a528@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020576} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test5c60f7d9-e411-47ea-bd92-227d4cd1a528@bitwarden.com","status":400},"timestamp":1767217020576} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test5c60f7d9-e411-47ea-bd92-227d4cd1a528@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020576} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testb2bf83b6-70ce-41c4-9d78-68c213ba5e79@bitwarden.com","status":204},"timestamp":1767217020577} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testb2bf83b6-70ce-41c4-9d78-68c213ba5e79@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020577} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testb2bf83b6-70ce-41c4-9d78-68c213ba5e79@bitwarden.com","status":400},"timestamp":1767217020577} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testb2bf83b6-70ce-41c4-9d78-68c213ba5e79@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020577} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testd59854e0-071e-4b92-87d3-b1361ddf4f19@bitwarden.com","status":204},"timestamp":1767217020578} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testd59854e0-071e-4b92-87d3-b1361ddf4f19@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020578} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testd59854e0-071e-4b92-87d3-b1361ddf4f19@bitwarden.com","status":400},"timestamp":1767217020578} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testd59854e0-071e-4b92-87d3-b1361ddf4f19@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020578} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test1e570338-9121-4290-87cc-b9b9c997b23a@bitwarden.com","status":204},"timestamp":1767217020579} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test1e570338-9121-4290-87cc-b9b9c997b23a@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020579} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test1e570338-9121-4290-87cc-b9b9c997b23a@bitwarden.com","status":400},"timestamp":1767217020580} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test1e570338-9121-4290-87cc-b9b9c997b23a@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020580} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test7200c4de-c3f4-40d5-8f96-350a3bd9670b@bitwarden.com","status":204},"timestamp":1767217103057} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test7200c4de-c3f4-40d5-8f96-350a3bd9670b@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103062} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test7200c4de-c3f4-40d5-8f96-350a3bd9670b@bitwarden.com","status":400},"timestamp":1767217103091} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test7200c4de-c3f4-40d5-8f96-350a3bd9670b@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103091} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test22cf0a3f-aebd-4051-9798-7c50006fe0e1@bitwarden.com","status":204},"timestamp":1767217103123} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test22cf0a3f-aebd-4051-9798-7c50006fe0e1@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103123} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test22cf0a3f-aebd-4051-9798-7c50006fe0e1@bitwarden.com","status":400},"timestamp":1767217103124} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test22cf0a3f-aebd-4051-9798-7c50006fe0e1@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103124} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test6f3b2ee8-6e14-4c72-8f7e-849f03b74a3e@bitwarden.com","status":204},"timestamp":1767217103125} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test6f3b2ee8-6e14-4c72-8f7e-849f03b74a3e@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103126} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test6f3b2ee8-6e14-4c72-8f7e-849f03b74a3e@bitwarden.com","status":400},"timestamp":1767217103126} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test6f3b2ee8-6e14-4c72-8f7e-849f03b74a3e@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103126} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test71e2b905-de2e-4aa6-974a-5400475517b9@bitwarden.com","status":204},"timestamp":1767217103127} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test71e2b905-de2e-4aa6-974a-5400475517b9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103127} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test71e2b905-de2e-4aa6-974a-5400475517b9@bitwarden.com","status":400},"timestamp":1767217103128} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test71e2b905-de2e-4aa6-974a-5400475517b9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103128} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test4b3133c1-7c1c-48da-a0c1-db54592de41e@bitwarden.com","status":204},"timestamp":1767217103129} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test4b3133c1-7c1c-48da-a0c1-db54592de41e@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103129} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test4b3133c1-7c1c-48da-a0c1-db54592de41e@bitwarden.com","status":400},"timestamp":1767217103130} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test4b3133c1-7c1c-48da-a0c1-db54592de41e@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103130} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testf8e99a1f-35f6-41fa-bf6e-c3fc9a2d89aa@bitwarden.com","status":204},"timestamp":1767217103131} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testf8e99a1f-35f6-41fa-bf6e-c3fc9a2d89aa@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103131} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testf8e99a1f-35f6-41fa-bf6e-c3fc9a2d89aa@bitwarden.com","status":400},"timestamp":1767217103131} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testf8e99a1f-35f6-41fa-bf6e-c3fc9a2d89aa@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103132} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test2a91889e-2dbd-4400-91d2-a7a1277b1976@bitwarden.com","status":204},"timestamp":1767217103133} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test2a91889e-2dbd-4400-91d2-a7a1277b1976@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103133} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test2a91889e-2dbd-4400-91d2-a7a1277b1976@bitwarden.com","status":400},"timestamp":1767217103133} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test2a91889e-2dbd-4400-91d2-a7a1277b1976@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103133} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test115626a7-035b-47c2-926d-51728656bfbb@bitwarden.com","status":204},"timestamp":1767217103134} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test115626a7-035b-47c2-926d-51728656bfbb@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103134} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test115626a7-035b-47c2-926d-51728656bfbb@bitwarden.com","status":400},"timestamp":1767217103135} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test115626a7-035b-47c2-926d-51728656bfbb@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103135} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testd5c06a44-76ed-4f30-8647-344f9abca932@bitwarden.com","status":204},"timestamp":1767217103135} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testd5c06a44-76ed-4f30-8647-344f9abca932@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103135} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testd5c06a44-76ed-4f30-8647-344f9abca932@bitwarden.com","status":400},"timestamp":1767217103136} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testd5c06a44-76ed-4f30-8647-344f9abca932@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103136} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test1eef218b-d7b4-4cd9-a85d-25bf167a90a4@bitwarden.com","status":204},"timestamp":1767217103137} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test1eef218b-d7b4-4cd9-a85d-25bf167a90a4@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103137} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test1eef218b-d7b4-4cd9-a85d-25bf167a90a4@bitwarden.com","status":400},"timestamp":1767217103137} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test1eef218b-d7b4-4cd9-a85d-25bf167a90a4@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103137} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testb091c01c-44d9-459b-8049-c1facc30d939@bitwarden.com","status":204},"timestamp":1767217103138} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testb091c01c-44d9-459b-8049-c1facc30d939@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103138} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testb091c01c-44d9-459b-8049-c1facc30d939@bitwarden.com","status":400},"timestamp":1767217103139} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testb091c01c-44d9-459b-8049-c1facc30d939@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103139} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test50bd241c-49f3-4ca8-b38a-31865ce78ca2@bitwarden.com","status":204},"timestamp":1767217103140} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test50bd241c-49f3-4ca8-b38a-31865ce78ca2@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103140} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test50bd241c-49f3-4ca8-b38a-31865ce78ca2@bitwarden.com","status":400},"timestamp":1767217103140} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test50bd241c-49f3-4ca8-b38a-31865ce78ca2@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103140} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-teste5d41d19-617c-4250-801f-2d5a794f80f2@bitwarden.com","status":204},"timestamp":1767217103141} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-teste5d41d19-617c-4250-801f-2d5a794f80f2@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103141} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-teste5d41d19-617c-4250-801f-2d5a794f80f2@bitwarden.com","status":400},"timestamp":1767217103141} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-teste5d41d19-617c-4250-801f-2d5a794f80f2@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103141} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test2ff5b61a-725f-4cfa-add9-647c15abd0b1@bitwarden.com","status":204},"timestamp":1767217103142} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test2ff5b61a-725f-4cfa-add9-647c15abd0b1@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103142} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test2ff5b61a-725f-4cfa-add9-647c15abd0b1@bitwarden.com","status":400},"timestamp":1767217103143} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test2ff5b61a-725f-4cfa-add9-647c15abd0b1@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103143} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test43bfa58f-6639-4130-9f9e-7810a7a19a33@bitwarden.com","status":204},"timestamp":1767217103144} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test43bfa58f-6639-4130-9f9e-7810a7a19a33@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103144} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test43bfa58f-6639-4130-9f9e-7810a7a19a33@bitwarden.com","status":400},"timestamp":1767217103144} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test43bfa58f-6639-4130-9f9e-7810a7a19a33@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103144} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test54110a4c-6317-49f3-aaeb-159a9f278187@bitwarden.com","status":204},"timestamp":1767217103145} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test54110a4c-6317-49f3-aaeb-159a9f278187@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103145} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test54110a4c-6317-49f3-aaeb-159a9f278187@bitwarden.com","status":400},"timestamp":1767217103145} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test54110a4c-6317-49f3-aaeb-159a9f278187@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103145} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test978a4a77-75c1-4538-b7fd-d9799598044b@bitwarden.com","status":204},"timestamp":1767217103146} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test978a4a77-75c1-4538-b7fd-d9799598044b@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103146} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test978a4a77-75c1-4538-b7fd-d9799598044b@bitwarden.com","status":400},"timestamp":1767217103147} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test978a4a77-75c1-4538-b7fd-d9799598044b@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103147} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testc8c69ee4-d9bd-4279-9429-66af89027f68@bitwarden.com","status":204},"timestamp":1767217103148} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testc8c69ee4-d9bd-4279-9429-66af89027f68@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103148} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testc8c69ee4-d9bd-4279-9429-66af89027f68@bitwarden.com","status":400},"timestamp":1767217103148} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testc8c69ee4-d9bd-4279-9429-66af89027f68@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103148} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test354e62ae-dd73-4aaf-ac17-54e59c5a57ed@bitwarden.com","status":204},"timestamp":1767217103149} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test354e62ae-dd73-4aaf-ac17-54e59c5a57ed@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103149} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test354e62ae-dd73-4aaf-ac17-54e59c5a57ed@bitwarden.com","status":400},"timestamp":1767217103149} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test354e62ae-dd73-4aaf-ac17-54e59c5a57ed@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103149} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testecb04cb7-f79b-4f08-8858-055c67794c91@bitwarden.com","status":204},"timestamp":1767217103150} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testecb04cb7-f79b-4f08-8858-055c67794c91@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103150} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testecb04cb7-f79b-4f08-8858-055c67794c91@bitwarden.com","status":400},"timestamp":1767217103151} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testecb04cb7-f79b-4f08-8858-055c67794c91@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103151} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test39bc2bf4-0357-4335-ac7b-788f5252d6ab@bitwarden.com","status":204},"timestamp":1767217103151} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test39bc2bf4-0357-4335-ac7b-788f5252d6ab@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103151} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test39bc2bf4-0357-4335-ac7b-788f5252d6ab@bitwarden.com","status":400},"timestamp":1767217103152} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test39bc2bf4-0357-4335-ac7b-788f5252d6ab@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103152} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test1d40b477-ad56-4022-a521-c2392ead447d@bitwarden.com","status":204},"timestamp":1767217103153} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test1d40b477-ad56-4022-a521-c2392ead447d@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103153} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test1d40b477-ad56-4022-a521-c2392ead447d@bitwarden.com","status":400},"timestamp":1767217103153} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test1d40b477-ad56-4022-a521-c2392ead447d@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103153} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testad7b05b3-e46f-43ef-bfae-e1a8684f64fb@bitwarden.com","status":204},"timestamp":1767217103154} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testad7b05b3-e46f-43ef-bfae-e1a8684f64fb@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103154} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testad7b05b3-e46f-43ef-bfae-e1a8684f64fb@bitwarden.com","status":400},"timestamp":1767217103155} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testad7b05b3-e46f-43ef-bfae-e1a8684f64fb@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103155} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testf6aae59a-4b31-4def-858c-6365785110da@bitwarden.com","status":204},"timestamp":1767217103155} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testf6aae59a-4b31-4def-858c-6365785110da@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103155} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testf6aae59a-4b31-4def-858c-6365785110da@bitwarden.com","status":400},"timestamp":1767217103156} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testf6aae59a-4b31-4def-858c-6365785110da@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103156} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test81720b75-4dfb-4af2-aced-5aa02eab6af7@bitwarden.com","status":204},"timestamp":1767217103157} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test81720b75-4dfb-4af2-aced-5aa02eab6af7@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103157} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test81720b75-4dfb-4af2-aced-5aa02eab6af7@bitwarden.com","status":400},"timestamp":1767217103157} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test81720b75-4dfb-4af2-aced-5aa02eab6af7@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103157} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test921afa7a-0e2b-42d9-9976-b63e440d3bee@bitwarden.com","status":204},"timestamp":1767217103158} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test921afa7a-0e2b-42d9-9976-b63e440d3bee@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103158} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test921afa7a-0e2b-42d9-9976-b63e440d3bee@bitwarden.com","status":400},"timestamp":1767217103158} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test921afa7a-0e2b-42d9-9976-b63e440d3bee@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103158} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test5ec86a7b-9f74-4aa5-aa7e-53101210f238@bitwarden.com","status":204},"timestamp":1767217103159} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test5ec86a7b-9f74-4aa5-aa7e-53101210f238@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103159} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test5ec86a7b-9f74-4aa5-aa7e-53101210f238@bitwarden.com","status":400},"timestamp":1767217103160} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test5ec86a7b-9f74-4aa5-aa7e-53101210f238@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103160} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testc92383a7-25ed-412b-bd6a-d67ac186ed6a@bitwarden.com","status":204},"timestamp":1767217103160} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testc92383a7-25ed-412b-bd6a-d67ac186ed6a@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103161} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testc92383a7-25ed-412b-bd6a-d67ac186ed6a@bitwarden.com","status":400},"timestamp":1767217103161} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testc92383a7-25ed-412b-bd6a-d67ac186ed6a@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103161} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test5eb8a550-5643-4d1f-bc62-a199361cd71d@bitwarden.com","status":204},"timestamp":1767217103162} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test5eb8a550-5643-4d1f-bc62-a199361cd71d@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103162} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test5eb8a550-5643-4d1f-bc62-a199361cd71d@bitwarden.com","status":400},"timestamp":1767217103162} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test5eb8a550-5643-4d1f-bc62-a199361cd71d@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103162} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testc945b848-4cad-4df2-9c6e-75a053ffcd07@bitwarden.com","status":204},"timestamp":1767217103163} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testc945b848-4cad-4df2-9c6e-75a053ffcd07@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103163} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testc945b848-4cad-4df2-9c6e-75a053ffcd07@bitwarden.com","status":400},"timestamp":1767217103164} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testc945b848-4cad-4df2-9c6e-75a053ffcd07@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103164} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testb059029d-47c0-4a44-ab13-94fbb856f9c6@bitwarden.com","status":204},"timestamp":1767217103164} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testb059029d-47c0-4a44-ab13-94fbb856f9c6@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103164} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testb059029d-47c0-4a44-ab13-94fbb856f9c6@bitwarden.com","status":400},"timestamp":1767217103165} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testb059029d-47c0-4a44-ab13-94fbb856f9c6@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103165} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testba148834-d085-40a5-866e-c75ab6701336@bitwarden.com","status":204},"timestamp":1767217103166} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testba148834-d085-40a5-866e-c75ab6701336@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103166} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testba148834-d085-40a5-866e-c75ab6701336@bitwarden.com","status":400},"timestamp":1767217103166} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testba148834-d085-40a5-866e-c75ab6701336@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103166} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testbbc13657-891c-4eb3-9cb0-df592f1851f9@bitwarden.com","status":204},"timestamp":1767217103167} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testbbc13657-891c-4eb3-9cb0-df592f1851f9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103167} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testbbc13657-891c-4eb3-9cb0-df592f1851f9@bitwarden.com","status":400},"timestamp":1767217103167} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testbbc13657-891c-4eb3-9cb0-df592f1851f9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103167} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test0d54c6cf-99e0-4e35-a40d-2cffce8546b0@bitwarden.com","status":204},"timestamp":1767217103168} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test0d54c6cf-99e0-4e35-a40d-2cffce8546b0@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103168} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test0d54c6cf-99e0-4e35-a40d-2cffce8546b0@bitwarden.com","status":400},"timestamp":1767217103169} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test0d54c6cf-99e0-4e35-a40d-2cffce8546b0@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103169} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test232750b1-74c6-4b7f-a62e-58187503e18a@bitwarden.com","status":204},"timestamp":1767217103170} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test232750b1-74c6-4b7f-a62e-58187503e18a@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103170} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test232750b1-74c6-4b7f-a62e-58187503e18a@bitwarden.com","status":400},"timestamp":1767217103170} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test232750b1-74c6-4b7f-a62e-58187503e18a@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103170} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-teste5efeaef-c249-4bc3-8c03-9710d951c387@bitwarden.com","status":204},"timestamp":1767217103171} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-teste5efeaef-c249-4bc3-8c03-9710d951c387@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103171} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-teste5efeaef-c249-4bc3-8c03-9710d951c387@bitwarden.com","status":400},"timestamp":1767217103171} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-teste5efeaef-c249-4bc3-8c03-9710d951c387@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103171} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test182de32c-2248-4162-bc0c-93a2ebd69d15@bitwarden.com","status":204},"timestamp":1767217103172} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test182de32c-2248-4162-bc0c-93a2ebd69d15@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103172} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test182de32c-2248-4162-bc0c-93a2ebd69d15@bitwarden.com","status":400},"timestamp":1767217103173} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test182de32c-2248-4162-bc0c-93a2ebd69d15@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103173} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test6bb810ef-4c54-4536-9e4c-34c7e1568e53@bitwarden.com","status":204},"timestamp":1767217103173} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test6bb810ef-4c54-4536-9e4c-34c7e1568e53@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103173} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test6bb810ef-4c54-4536-9e4c-34c7e1568e53@bitwarden.com","status":400},"timestamp":1767217103174} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test6bb810ef-4c54-4536-9e4c-34c7e1568e53@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103174} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test153442a5-d749-44a5-b3c2-271d20e3d742@bitwarden.com","status":204},"timestamp":1767217103175} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test153442a5-d749-44a5-b3c2-271d20e3d742@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103175} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test153442a5-d749-44a5-b3c2-271d20e3d742@bitwarden.com","status":400},"timestamp":1767217103175} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test153442a5-d749-44a5-b3c2-271d20e3d742@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103175} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test77a5db79-1f93-4cd2-a31f-e71a9c0ff6fb@bitwarden.com","status":204},"timestamp":1767217103176} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test77a5db79-1f93-4cd2-a31f-e71a9c0ff6fb@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103176} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test77a5db79-1f93-4cd2-a31f-e71a9c0ff6fb@bitwarden.com","status":400},"timestamp":1767217103177} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test77a5db79-1f93-4cd2-a31f-e71a9c0ff6fb@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103177} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test565494b8-b4c5-42bc-8864-c70f0478841b@bitwarden.com","status":204},"timestamp":1767217103178} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test565494b8-b4c5-42bc-8864-c70f0478841b@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103178} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test565494b8-b4c5-42bc-8864-c70f0478841b@bitwarden.com","status":400},"timestamp":1767217103178} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test565494b8-b4c5-42bc-8864-c70f0478841b@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103178} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test44316529-92ac-4e0b-8636-2da6c6a9a4dc@bitwarden.com","status":204},"timestamp":1767217103179} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test44316529-92ac-4e0b-8636-2da6c6a9a4dc@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103179} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test44316529-92ac-4e0b-8636-2da6c6a9a4dc@bitwarden.com","status":400},"timestamp":1767217103179} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test44316529-92ac-4e0b-8636-2da6c6a9a4dc@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103179} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testdf718129-6d96-47b5-9654-241e7e4e744a@bitwarden.com","status":204},"timestamp":1767217103180} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testdf718129-6d96-47b5-9654-241e7e4e744a@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103180} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testdf718129-6d96-47b5-9654-241e7e4e744a@bitwarden.com","status":400},"timestamp":1767217103181} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testdf718129-6d96-47b5-9654-241e7e4e744a@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103181} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test07c96543-cc34-46d3-acbb-12c3a0722c6b@bitwarden.com","status":204},"timestamp":1767217103181} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test07c96543-cc34-46d3-acbb-12c3a0722c6b@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103182} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test07c96543-cc34-46d3-acbb-12c3a0722c6b@bitwarden.com","status":400},"timestamp":1767217103182} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test07c96543-cc34-46d3-acbb-12c3a0722c6b@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103182} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test5d724177-636a-48f6-afad-d93c99d61a64@bitwarden.com","status":204},"timestamp":1767217103183} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test5d724177-636a-48f6-afad-d93c99d61a64@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103183} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test5d724177-636a-48f6-afad-d93c99d61a64@bitwarden.com","status":400},"timestamp":1767217103183} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test5d724177-636a-48f6-afad-d93c99d61a64@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103183} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testcdba648a-3094-48d9-91b0-4a21bc5836b7@bitwarden.com","status":204},"timestamp":1767217103184} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testcdba648a-3094-48d9-91b0-4a21bc5836b7@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103184} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testcdba648a-3094-48d9-91b0-4a21bc5836b7@bitwarden.com","status":400},"timestamp":1767217103184} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testcdba648a-3094-48d9-91b0-4a21bc5836b7@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103185} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testbae00768-696a-4d71-bfcd-5dc6cbaee70c@bitwarden.com","status":204},"timestamp":1767217103185} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testbae00768-696a-4d71-bfcd-5dc6cbaee70c@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103185} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testbae00768-696a-4d71-bfcd-5dc6cbaee70c@bitwarden.com","status":400},"timestamp":1767217103186} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testbae00768-696a-4d71-bfcd-5dc6cbaee70c@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103186} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test684b9e50-4425-459a-8923-5bde8186cec9@bitwarden.com","status":204},"timestamp":1767217103187} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test684b9e50-4425-459a-8923-5bde8186cec9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103187} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test684b9e50-4425-459a-8923-5bde8186cec9@bitwarden.com","status":400},"timestamp":1767217103187} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test684b9e50-4425-459a-8923-5bde8186cec9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103187} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testcfbcfd1e-a767-4269-bee6-34f71af4b0a1@bitwarden.com","status":204},"timestamp":1767217103188} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testcfbcfd1e-a767-4269-bee6-34f71af4b0a1@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103188} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testcfbcfd1e-a767-4269-bee6-34f71af4b0a1@bitwarden.com","status":400},"timestamp":1767217103188} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testcfbcfd1e-a767-4269-bee6-34f71af4b0a1@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103188} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test619d38cd-1900-4600-a268-f87cbbbefdc5@bitwarden.com","status":204},"timestamp":1767217103189} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test619d38cd-1900-4600-a268-f87cbbbefdc5@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103189} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test619d38cd-1900-4600-a268-f87cbbbefdc5@bitwarden.com","status":400},"timestamp":1767217103190} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test619d38cd-1900-4600-a268-f87cbbbefdc5@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103190} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testd58d7931-7c74-4557-9957-ba8d56f6dbcf@bitwarden.com","status":204},"timestamp":1767217103190} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testd58d7931-7c74-4557-9957-ba8d56f6dbcf@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103190} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testd58d7931-7c74-4557-9957-ba8d56f6dbcf@bitwarden.com","status":400},"timestamp":1767217103191} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testd58d7931-7c74-4557-9957-ba8d56f6dbcf@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103191} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testa2b761a9-ed49-447c-bd63-b31c6285c565@bitwarden.com","status":204},"timestamp":1767217103192} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testa2b761a9-ed49-447c-bd63-b31c6285c565@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103192} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testa2b761a9-ed49-447c-bd63-b31c6285c565@bitwarden.com","status":400},"timestamp":1767217103192} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testa2b761a9-ed49-447c-bd63-b31c6285c565@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103192} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testd6b5aac7-5432-4f8e-8cfb-b3fa49d968d6@bitwarden.com","status":204},"timestamp":1767217103193} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testd6b5aac7-5432-4f8e-8cfb-b3fa49d968d6@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103193} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testd6b5aac7-5432-4f8e-8cfb-b3fa49d968d6@bitwarden.com","status":400},"timestamp":1767217103194} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testd6b5aac7-5432-4f8e-8cfb-b3fa49d968d6@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103194} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test286a3058-9578-4f8a-812b-fabafd97faa0@bitwarden.com","status":204},"timestamp":1767217229982} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test286a3058-9578-4f8a-812b-fabafd97faa0@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217229988} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test286a3058-9578-4f8a-812b-fabafd97faa0@bitwarden.com","status":400},"timestamp":1767217230012} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test286a3058-9578-4f8a-812b-fabafd97faa0@bitwarden.com","actual":400,"expected":200},"timestamp":1767217230012} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testd61adf5e-8f98-48d9-b625-18cd639065fd@bitwarden.com","status":204},"timestamp":1767217240393} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testd61adf5e-8f98-48d9-b625-18cd639065fd@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217240403} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testd61adf5e-8f98-48d9-b625-18cd639065fd@bitwarden.com","status":400},"timestamp":1767217240500} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testd61adf5e-8f98-48d9-b625-18cd639065fd@bitwarden.com","actual":400,"expected":200},"timestamp":1767217240501} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test68356f6b-6d24-4160-a256-04dfe65e2682@bitwarden.com","status":204},"timestamp":1767217297031} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test68356f6b-6d24-4160-a256-04dfe65e2682@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217303691} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test68356f6b-6d24-4160-a256-04dfe65e2682@bitwarden.com","status":400},"timestamp":1767217303806} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test68356f6b-6d24-4160-a256-04dfe65e2682@bitwarden.com","actual":400,"expected":200},"timestamp":1767217328723} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testdd047e03-bfb1-42b6-9d8a-4e12f68d09c0@bitwarden.com","status":204},"timestamp":1767217411260} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testdd047e03-bfb1-42b6-9d8a-4e12f68d09c0@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217411267} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testdd047e03-bfb1-42b6-9d8a-4e12f68d09c0@bitwarden.com","status":400},"timestamp":1767217411294} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testdd047e03-bfb1-42b6-9d8a-4e12f68d09c0@bitwarden.com","actual":400,"expected":200},"timestamp":1767217411294} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testb4cc71c9-625c-4935-ad34-3f44f2f48086@bitwarden.com","status":204},"timestamp":1767217431762} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testb4cc71c9-625c-4935-ad34-3f44f2f48086@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217434718} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testb4cc71c9-625c-4935-ad34-3f44f2f48086@bitwarden.com","status":400},"timestamp":1767217434856} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testb4cc71c9-625c-4935-ad34-3f44f2f48086@bitwarden.com","actual":400,"expected":200},"timestamp":1767217436616} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testfad6d4db-1542-4f80-b7eb-962224760601@bitwarden.com","status":204},"timestamp":1767217567241} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testfad6d4db-1542-4f80-b7eb-962224760601@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217569544} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testfad6d4db-1542-4f80-b7eb-962224760601@bitwarden.com","status":400},"timestamp":1767217569650} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testfad6d4db-1542-4f80-b7eb-962224760601@bitwarden.com","actual":400,"expected":200},"timestamp":1767217570091} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testae200d65-2cb9-4e7e-9072-e85b568ebbeb@bitwarden.com","status":204},"timestamp":1767217718921} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testae200d65-2cb9-4e7e-9072-e85b568ebbeb@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217719883} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testae200d65-2cb9-4e7e-9072-e85b568ebbeb@bitwarden.com","status":400},"timestamp":1767217720008} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testae200d65-2cb9-4e7e-9072-e85b568ebbeb@bitwarden.com","actual":400,"expected":200},"timestamp":1767217720452} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test37b691b1-46a1-465f-90be-bf31882069be@bitwarden.com","status":204},"timestamp":1767218009023} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test37b691b1-46a1-465f-90be-bf31882069be@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767218009755} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test37b691b1-46a1-465f-90be-bf31882069be@bitwarden.com","status":400},"timestamp":1767218009829} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test37b691b1-46a1-465f-90be-bf31882069be@bitwarden.com","actual":400,"expected":200},"timestamp":1767218009829} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test76740011-3f81-4f3e-a0b4-01cf23e17a78@bitwarden.com","status":204},"timestamp":1767218084895} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test76740011-3f81-4f3e-a0b4-01cf23e17a78@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767218084901} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test76740011-3f81-4f3e-a0b4-01cf23e17a78@bitwarden.com","status":400},"timestamp":1767218084928} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test76740011-3f81-4f3e-a0b4-01cf23e17a78@bitwarden.com","actual":400,"expected":200},"timestamp":1767218084929} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testb44f7181-a39d-4720-85e6-cdbed603f14b@bitwarden.com","status":204},"timestamp":1767218107041} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testb44f7181-a39d-4720-85e6-cdbed603f14b@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767218107047} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testb44f7181-a39d-4720-85e6-cdbed603f14b@bitwarden.com","status":400},"timestamp":1767218107073} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testb44f7181-a39d-4720-85e6-cdbed603f14b@bitwarden.com","actual":400,"expected":200},"timestamp":1767218107074} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test5bc32e3a-df8e-407b-9574-cbe0588aaa95@bitwarden.com","status":204},"timestamp":1767218247090} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test5bc32e3a-df8e-407b-9574-cbe0588aaa95@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767218247096} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"A","location":"RegisterFinishRequestModel.Validate","message":"validate_enter","data":{"hasUnlock":false,"hasAuth":false},"timestamp":1767218247119} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"A","location":"RegisterFinishRequestModel.Validate","message":"token_flags","data":{"hasEmailVerification":true,"hasOrgInvite":false,"hasOrgSponsoredFreeFamilyPlan":false,"hasEmergencyAccessInvite":false,"hasProviderInvite":false,"tokenCount":1},"timestamp":1767218247121} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"A","location":"RegisterFinishRequestModel.Validate","message":"token_type_resolved","data":{"tokenType":"EmailVerification"},"timestamp":1767218247122} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"C","location":"RegisterFinishRequestModel.Validate","message":"kdf_source_root_fields","data":{"Kdf":"PBKDF2_SHA256","KdfIterations":600000,"KdfMemory":null,"KdfParallelism":null},"timestamp":1767218247122} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"C","location":"RegisterFinishRequestModel.Validate","message":"kdf_validation_results","data":{"count":0,"errors":[]},"timestamp":1767218247123} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"D","location":"AccountsController.PostRegisterFinish","message":"register_finish_enter","data":{"hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokenFlags":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false}},"timestamp":1767218247125} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"D","location":"AccountsController.PostRegisterFinish","message":"register_finish_token_type","data":{"tokenType":"EmailVerification"},"timestamp":1767218247127} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"D","location":"AccountsController.PostRegisterFinish","message":"register_finish_kdf_preview","data":{"source":"root","kdf":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null},"timestamp":1767218247127} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"D","location":"AccountsController.PostRegisterFinish","message":"register_finish_process_result","data":{"succeeded":true,"errorCount":0},"timestamp":1767218247278} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test5bc32e3a-df8e-407b-9574-cbe0588aaa95@bitwarden.com","status":200},"timestamp":1767218247281} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test5bc32e3a-df8e-407b-9574-cbe0588aaa95@bitwarden.com","actual":200,"expected":200},"timestamp":1767218247282} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test23dce254-28d2-465f-9ec3-9321a1b9ac1b@bitwarden.com","status":204},"timestamp":1767218248131} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test23dce254-28d2-465f-9ec3-9321a1b9ac1b@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767218248131} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"A","location":"RegisterFinishRequestModel.Validate","message":"validate_enter","data":{"hasUnlock":false,"hasAuth":false},"timestamp":1767218248132} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"A","location":"RegisterFinishRequestModel.Validate","message":"token_flags","data":{"hasEmailVerification":true,"hasOrgInvite":false,"hasOrgSponsoredFreeFamilyPlan":false,"hasEmergencyAccessInvite":false,"hasProviderInvite":false,"tokenCount":1},"timestamp":1767218248132} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"A","location":"RegisterFinishRequestModel.Validate","message":"token_type_resolved","data":{"tokenType":"EmailVerification"},"timestamp":1767218248132} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"C","location":"RegisterFinishRequestModel.Validate","message":"kdf_source_root_fields","data":{"Kdf":"PBKDF2_SHA256","KdfIterations":600000,"KdfMemory":null,"KdfParallelism":null},"timestamp":1767218248132} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"C","location":"RegisterFinishRequestModel.Validate","message":"kdf_validation_results","data":{"count":0,"errors":[]},"timestamp":1767218248132} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"D","location":"AccountsController.PostRegisterFinish","message":"register_finish_enter","data":{"hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokenFlags":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false}},"timestamp":1767218248132} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"D","location":"AccountsController.PostRegisterFinish","message":"register_finish_token_type","data":{"tokenType":"EmailVerification"},"timestamp":1767218248132} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"D","location":"AccountsController.PostRegisterFinish","message":"register_finish_kdf_preview","data":{"source":"root","kdf":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null},"timestamp":1767218248133} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"D","location":"AccountsController.PostRegisterFinish","message":"register_finish_process_result","data":{"succeeded":true,"errorCount":0},"timestamp":1767218248169} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test23dce254-28d2-465f-9ec3-9321a1b9ac1b@bitwarden.com","status":200},"timestamp":1767218248170} +{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test23dce254-28d2-465f-9ec3-9321a1b9ac1b@bitwarden.com","actual":200,"expected":200},"timestamp":1767218248170} diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 891bb8fd00b4..b6b5737e85c7 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -62,49 +62,6 @@ public class RegisterFinishRequestModel : IValidatableObject public Guid? ProviderUserId { get; set; } - // Strongly-typed accessors for post-validation usage to satisfy nullability - // Ignore serialization, these are just null safe accessors. - [System.Text.Json.Serialization.JsonIgnore] - [Newtonsoft.Json.JsonIgnore] - public string EmailVerificationTokenRequired => - EmailVerificationToken - ?? throw new BadRequestException("Email verification token absent when processing register/finish."); - [System.Text.Json.Serialization.JsonIgnore] - [Newtonsoft.Json.JsonIgnore] - public string OrgInviteTokenRequired => - OrgInviteToken - ?? throw new BadRequestException("Organization invite token absent when processing register/finish."); - [System.Text.Json.Serialization.JsonIgnore] - [Newtonsoft.Json.JsonIgnore] - public Guid OrganizationUserIdRequired => - OrganizationUserId - ?? throw new BadRequestException("Organization user id absent when processing register/finish."); - [System.Text.Json.Serialization.JsonIgnore] - [Newtonsoft.Json.JsonIgnore] - public string OrgSponsoredFreeFamilyPlanTokenRequired => - OrgSponsoredFreeFamilyPlanToken - ?? throw new BadRequestException("Organization sponsored free family plan token absent when processing register/finish."); - [System.Text.Json.Serialization.JsonIgnore] - [Newtonsoft.Json.JsonIgnore] - public string AcceptEmergencyAccessInviteTokenRequired => - AcceptEmergencyAccessInviteToken - ?? throw new BadRequestException("Accept emergency access invite token absent when processing register/finish."); - [System.Text.Json.Serialization.JsonIgnore] - [Newtonsoft.Json.JsonIgnore] - public Guid AcceptEmergencyAccessIdRequired => - AcceptEmergencyAccessId - ?? throw new BadRequestException("Accept emergency access id absent when processing register/finish."); - [System.Text.Json.Serialization.JsonIgnore] - [Newtonsoft.Json.JsonIgnore] - public string ProviderInviteTokenRequired => - ProviderInviteToken - ?? throw new BadRequestException("Provider invite token absent when processing register/finish."); - [System.Text.Json.Serialization.JsonIgnore] - [Newtonsoft.Json.JsonIgnore] - public Guid ProviderUserIdRequired => - ProviderUserId - ?? throw new BadRequestException("Provider user id absent when processing register/finish."); - public User ToUser() { var user = new User @@ -134,7 +91,9 @@ public RegisterFinishTokenType GetTokenType() { return RegisterFinishTokenType.EmailVerification; } - if (!string.IsNullOrEmpty(OrgInviteToken) && OrganizationUserId.HasValue) + if (!string.IsNullOrEmpty(OrgInviteToken) + && OrganizationUserId.HasValue + && OrganizationUserId.Value != Guid.Empty) { return RegisterFinishTokenType.OrganizationInvite; } @@ -142,11 +101,15 @@ public RegisterFinishTokenType GetTokenType() { return RegisterFinishTokenType.OrgSponsoredFreeFamilyPlan; } - if (!string.IsNullOrWhiteSpace(AcceptEmergencyAccessInviteToken) && AcceptEmergencyAccessId.HasValue) + if (!string.IsNullOrWhiteSpace(AcceptEmergencyAccessInviteToken) + && AcceptEmergencyAccessId.HasValue + && AcceptEmergencyAccessId.Value != Guid.Empty) { return RegisterFinishTokenType.EmergencyAccessInvite; } - if (!string.IsNullOrWhiteSpace(ProviderInviteToken) && ProviderUserId.HasValue) + if (!string.IsNullOrWhiteSpace(ProviderInviteToken) + && ProviderUserId.HasValue + && ProviderUserId.Value != Guid.Empty) { return RegisterFinishTokenType.ProviderInvite; } @@ -163,10 +126,16 @@ public IEnumerable Validate(ValidationContext validationContex // Ensure exactly one registration token type is provided var hasEmailVerification = !string.IsNullOrWhiteSpace(EmailVerificationToken); - var hasOrgInvite = !string.IsNullOrEmpty(OrgInviteToken) && OrganizationUserId.HasValue; + var hasOrgInvite = !string.IsNullOrEmpty(OrgInviteToken) + && OrganizationUserId.HasValue + && OrganizationUserId.Value != Guid.Empty; var hasOrgSponsoredFreeFamilyPlan = !string.IsNullOrWhiteSpace(OrgSponsoredFreeFamilyPlanToken); - var hasEmergencyAccessInvite = !string.IsNullOrWhiteSpace(AcceptEmergencyAccessInviteToken) && AcceptEmergencyAccessId.HasValue; - var hasProviderInvite = !string.IsNullOrWhiteSpace(ProviderInviteToken) && ProviderUserId.HasValue; + var hasEmergencyAccessInvite = !string.IsNullOrWhiteSpace(AcceptEmergencyAccessInviteToken) + && AcceptEmergencyAccessId.HasValue + && AcceptEmergencyAccessId.Value != Guid.Empty; + var hasProviderInvite = !string.IsNullOrWhiteSpace(ProviderInviteToken) + && ProviderUserId.HasValue + && ProviderUserId.Value != Guid.Empty; var tokenCount = (hasEmailVerification ? 1 : 0) + (hasOrgInvite ? 1 : 0) + (hasOrgSponsoredFreeFamilyPlan ? 1 : 0) @@ -230,7 +199,7 @@ public IEnumerable Validate(ValidationContext validationContex IEnumerable kdfValidationResults; if (MasterPasswordUnlock != null && MasterPasswordAuthentication != null) { - kdfValidationResults = KdfSettingsValidator.Validate(MasterPasswordUnlock.ToData()); + kdfValidationResults = KdfSettingsValidator.Validate(MasterPasswordUnlock?.ToData() ?? throw new InvalidOperationException("Error Here")); } else { diff --git a/src/Identity/Controllers/AccountsController.cs b/src/Identity/Controllers/AccountsController.cs index f079d39770e9..aba344aeda1e 100644 --- a/src/Identity/Controllers/AccountsController.cs +++ b/src/Identity/Controllers/AccountsController.cs @@ -1,4 +1,7 @@ -using System.Text; +// FIXME: Update this file to be null safe and then delete the line below +#nullable disable + +using System.Text; using Bit.Core; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models.Api.Request.Accounts; @@ -38,7 +41,7 @@ public class AccountsController : Controller private readonly IFeatureService _featureService; private readonly IDataProtectorTokenFactory _registrationEmailVerificationTokenDataFactory; - private readonly byte[]? _defaultKdfHmacKey = null; + private readonly byte[] _defaultKdfHmacKey = null; private static readonly List _defaultKdfResults = [ // The first result (index 0) should always return the "normal" default. @@ -144,7 +147,7 @@ public async Task PostRegisterFinish([FromBody] Reg User user = model.ToUser(); // Users will either have an emailed token or an email verification token - not both. - IdentityResult? identityResult = null; + IdentityResult identityResult = null; // PM-28143 - Just use the MasterPasswordAuthenticationData.MasterPasswordAuthenticationHash string masterPasswordHash = model.MasterPasswordAuthentication?.MasterPasswordAuthenticationHash @@ -156,38 +159,38 @@ public async Task PostRegisterFinish([FromBody] Reg identityResult = await _registerUserCommand.RegisterUserViaEmailVerificationToken( user, masterPasswordHash, - model.EmailVerificationTokenRequired); + model.EmailVerificationToken); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.OrganizationInvite: identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken( user, masterPasswordHash, - model.OrgInviteTokenRequired, - model.OrganizationUserIdRequired); + model.OrgInviteToken, + model.OrganizationUserId); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.OrgSponsoredFreeFamilyPlan: identityResult = await _registerUserCommand.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken( user, masterPasswordHash, - model.OrgSponsoredFreeFamilyPlanTokenRequired); + model.OrgSponsoredFreeFamilyPlanToken); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.EmergencyAccessInvite: identityResult = await _registerUserCommand.RegisterUserViaAcceptEmergencyAccessInviteToken( user, masterPasswordHash, - model.AcceptEmergencyAccessInviteTokenRequired, - model.AcceptEmergencyAccessIdRequired); + model.AcceptEmergencyAccessInviteToken, + (Guid)model.AcceptEmergencyAccessId); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.ProviderInvite: identityResult = await _registerUserCommand.RegisterUserViaProviderInviteToken( user, masterPasswordHash, - model.ProviderInviteTokenRequired, - model.ProviderUserIdRequired); + model.ProviderInviteToken, + (Guid)model.ProviderUserId); return ProcessRegistrationResult(identityResult, user); default: @@ -202,10 +205,11 @@ private RegisterFinishResponseModel ProcessRegistrationResult(IdentityResult res return new RegisterFinishResponseModel(); } - foreach (var error in result.Errors.Where(e => e.Code != "DuplicateUserName")) - { - ModelState.AddModelError(string.Empty, error.Description); - } + if (result.Errors != null) + foreach (var error in result.Errors.Where(e => e.Code != "DuplicateUserName")) + { + ModelState.AddModelError(string.Empty, error.Description); + } throw new BadRequestException(ModelState); } diff --git a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs index 830275af70c7..3df80d267c1c 100644 --- a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs +++ b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs @@ -279,7 +279,6 @@ public async Task RegisterNewIdentityFactoryUserAsync( requestModel.EmailVerificationToken = RegistrationTokens[requestModel.Email]; var postRegisterFinishHttpContext = await PostRegisterFinishAsync(requestModel); - Assert.Equal(StatusCodes.Status200OK, postRegisterFinishHttpContext.Response.StatusCode); var database = GetDatabaseContext(); @@ -290,4 +289,5 @@ public async Task RegisterNewIdentityFactoryUserAsync( return user; } + } From 015d2ed9977da0a71357a0ca047020d21d172bfa Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Wed, 31 Dec 2025 17:02:58 -0500 Subject: [PATCH 22/44] comment(register): [PM-27084] Account Register Uses New Data Types - Removed debug file. --- perf/logs/debug.ndjson | 498 ----------------------------------------- 1 file changed, 498 deletions(-) delete mode 100644 perf/logs/debug.ndjson diff --git a/perf/logs/debug.ndjson b/perf/logs/debug.ndjson deleted file mode 100644 index 96fbe5d6b129..000000000000 --- a/perf/logs/debug.ndjson +++ /dev/null @@ -1,498 +0,0 @@ -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-teste286b7f0-b730-48d6-b7ba-a2629a9ed0d8@bitwarden.com","status":204},"timestamp":1767216787835} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-teste286b7f0-b730-48d6-b7ba-a2629a9ed0d8@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767216787840} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-teste286b7f0-b730-48d6-b7ba-a2629a9ed0d8@bitwarden.com","status":400},"timestamp":1767216787865} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-teste286b7f0-b730-48d6-b7ba-a2629a9ed0d8@bitwarden.com","actual":400,"expected":200},"timestamp":1767216787865} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test9b9bf154-f5ed-49ae-8dbe-2869b8ec9e27@bitwarden.com","status":204},"timestamp":1767216964948} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test9b9bf154-f5ed-49ae-8dbe-2869b8ec9e27@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767216964956} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test9b9bf154-f5ed-49ae-8dbe-2869b8ec9e27@bitwarden.com","status":400},"timestamp":1767216964984} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test9b9bf154-f5ed-49ae-8dbe-2869b8ec9e27@bitwarden.com","actual":400,"expected":200},"timestamp":1767216964984} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test76636f55-7232-42c6-8809-32866e17f533@bitwarden.com","status":204},"timestamp":1767217020454} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test76636f55-7232-42c6-8809-32866e17f533@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020459} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test76636f55-7232-42c6-8809-32866e17f533@bitwarden.com","status":400},"timestamp":1767217020483} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test76636f55-7232-42c6-8809-32866e17f533@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020483} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test16c2bb43-8805-47d8-b126-599de6bc1071@bitwarden.com","status":204},"timestamp":1767217020510} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test16c2bb43-8805-47d8-b126-599de6bc1071@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020510} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test16c2bb43-8805-47d8-b126-599de6bc1071@bitwarden.com","status":400},"timestamp":1767217020511} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test16c2bb43-8805-47d8-b126-599de6bc1071@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020511} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test36321664-7dd7-4bc3-885d-ae4eea7d98d2@bitwarden.com","status":204},"timestamp":1767217020512} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test36321664-7dd7-4bc3-885d-ae4eea7d98d2@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020513} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test36321664-7dd7-4bc3-885d-ae4eea7d98d2@bitwarden.com","status":400},"timestamp":1767217020513} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test36321664-7dd7-4bc3-885d-ae4eea7d98d2@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020513} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test07b40214-c78d-4515-b84d-af7f949c510e@bitwarden.com","status":204},"timestamp":1767217020514} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test07b40214-c78d-4515-b84d-af7f949c510e@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020514} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test07b40214-c78d-4515-b84d-af7f949c510e@bitwarden.com","status":400},"timestamp":1767217020514} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test07b40214-c78d-4515-b84d-af7f949c510e@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020515} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test727329d5-e222-4961-a30f-d4e4e93d63f0@bitwarden.com","status":204},"timestamp":1767217020515} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test727329d5-e222-4961-a30f-d4e4e93d63f0@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020515} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test727329d5-e222-4961-a30f-d4e4e93d63f0@bitwarden.com","status":400},"timestamp":1767217020516} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test727329d5-e222-4961-a30f-d4e4e93d63f0@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020516} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testbef4e89d-cf16-4d2b-b7b5-7e9adebb1992@bitwarden.com","status":204},"timestamp":1767217020517} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testbef4e89d-cf16-4d2b-b7b5-7e9adebb1992@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020517} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testbef4e89d-cf16-4d2b-b7b5-7e9adebb1992@bitwarden.com","status":400},"timestamp":1767217020517} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testbef4e89d-cf16-4d2b-b7b5-7e9adebb1992@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020517} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test12d4d6ea-949c-4728-9217-2dfc70ba2844@bitwarden.com","status":204},"timestamp":1767217020518} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test12d4d6ea-949c-4728-9217-2dfc70ba2844@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020518} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test12d4d6ea-949c-4728-9217-2dfc70ba2844@bitwarden.com","status":400},"timestamp":1767217020518} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test12d4d6ea-949c-4728-9217-2dfc70ba2844@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020519} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test6f98e688-7780-4fb5-be65-c48d22fe5e47@bitwarden.com","status":204},"timestamp":1767217020519} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test6f98e688-7780-4fb5-be65-c48d22fe5e47@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020519} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test6f98e688-7780-4fb5-be65-c48d22fe5e47@bitwarden.com","status":400},"timestamp":1767217020520} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test6f98e688-7780-4fb5-be65-c48d22fe5e47@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020520} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testa299ba20-7b83-4f48-9245-0cb5217ee060@bitwarden.com","status":204},"timestamp":1767217020521} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testa299ba20-7b83-4f48-9245-0cb5217ee060@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020521} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testa299ba20-7b83-4f48-9245-0cb5217ee060@bitwarden.com","status":400},"timestamp":1767217020521} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testa299ba20-7b83-4f48-9245-0cb5217ee060@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020521} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test1494b8fc-e12b-4309-b266-e70a5aa5cd38@bitwarden.com","status":204},"timestamp":1767217020522} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test1494b8fc-e12b-4309-b266-e70a5aa5cd38@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020522} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test1494b8fc-e12b-4309-b266-e70a5aa5cd38@bitwarden.com","status":400},"timestamp":1767217020522} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test1494b8fc-e12b-4309-b266-e70a5aa5cd38@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020523} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testb9a59f9c-d3cc-421a-bbe1-3b091cf44b00@bitwarden.com","status":204},"timestamp":1767217020523} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testb9a59f9c-d3cc-421a-bbe1-3b091cf44b00@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020523} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testb9a59f9c-d3cc-421a-bbe1-3b091cf44b00@bitwarden.com","status":400},"timestamp":1767217020524} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testb9a59f9c-d3cc-421a-bbe1-3b091cf44b00@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020524} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test0bbdda90-34c4-4012-9758-bbc5b9dde0d0@bitwarden.com","status":204},"timestamp":1767217020525} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test0bbdda90-34c4-4012-9758-bbc5b9dde0d0@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020525} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test0bbdda90-34c4-4012-9758-bbc5b9dde0d0@bitwarden.com","status":400},"timestamp":1767217020525} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test0bbdda90-34c4-4012-9758-bbc5b9dde0d0@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020525} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test3adc0011-03f4-4d6c-8a65-a31e3c103030@bitwarden.com","status":204},"timestamp":1767217020526} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test3adc0011-03f4-4d6c-8a65-a31e3c103030@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020526} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test3adc0011-03f4-4d6c-8a65-a31e3c103030@bitwarden.com","status":400},"timestamp":1767217020526} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test3adc0011-03f4-4d6c-8a65-a31e3c103030@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020526} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testd700873d-b6fb-4aa1-a3f1-47678deb6126@bitwarden.com","status":204},"timestamp":1767217020527} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testd700873d-b6fb-4aa1-a3f1-47678deb6126@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020527} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testd700873d-b6fb-4aa1-a3f1-47678deb6126@bitwarden.com","status":400},"timestamp":1767217020527} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testd700873d-b6fb-4aa1-a3f1-47678deb6126@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020527} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testbd1a71d9-fc52-4628-b26c-fdc2c349a5a3@bitwarden.com","status":204},"timestamp":1767217020528} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testbd1a71d9-fc52-4628-b26c-fdc2c349a5a3@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020528} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testbd1a71d9-fc52-4628-b26c-fdc2c349a5a3@bitwarden.com","status":400},"timestamp":1767217020529} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testbd1a71d9-fc52-4628-b26c-fdc2c349a5a3@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020529} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test980cfd2c-a566-4b6e-9e35-414cb00998a9@bitwarden.com","status":204},"timestamp":1767217020529} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test980cfd2c-a566-4b6e-9e35-414cb00998a9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020529} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test980cfd2c-a566-4b6e-9e35-414cb00998a9@bitwarden.com","status":400},"timestamp":1767217020530} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test980cfd2c-a566-4b6e-9e35-414cb00998a9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020530} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test11049bfb-2c3c-4364-aa1e-99c397aea172@bitwarden.com","status":204},"timestamp":1767217020531} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test11049bfb-2c3c-4364-aa1e-99c397aea172@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020531} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test11049bfb-2c3c-4364-aa1e-99c397aea172@bitwarden.com","status":400},"timestamp":1767217020531} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test11049bfb-2c3c-4364-aa1e-99c397aea172@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020531} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testbd6b6099-beeb-4bbb-ad5a-85211d6f7fbb@bitwarden.com","status":204},"timestamp":1767217020532} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testbd6b6099-beeb-4bbb-ad5a-85211d6f7fbb@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020532} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testbd6b6099-beeb-4bbb-ad5a-85211d6f7fbb@bitwarden.com","status":400},"timestamp":1767217020532} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testbd6b6099-beeb-4bbb-ad5a-85211d6f7fbb@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020532} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test43502847-1dd6-434b-84b1-201a7e5f3de9@bitwarden.com","status":204},"timestamp":1767217020533} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test43502847-1dd6-434b-84b1-201a7e5f3de9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020533} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test43502847-1dd6-434b-84b1-201a7e5f3de9@bitwarden.com","status":400},"timestamp":1767217020533} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test43502847-1dd6-434b-84b1-201a7e5f3de9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020533} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test2ea77a32-13b0-40aa-977a-2b867cb4b31e@bitwarden.com","status":204},"timestamp":1767217020534} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test2ea77a32-13b0-40aa-977a-2b867cb4b31e@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020534} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test2ea77a32-13b0-40aa-977a-2b867cb4b31e@bitwarden.com","status":400},"timestamp":1767217020535} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test2ea77a32-13b0-40aa-977a-2b867cb4b31e@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020535} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testea097f7a-9fc2-437d-8f15-34f369d0102e@bitwarden.com","status":204},"timestamp":1767217020535} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testea097f7a-9fc2-437d-8f15-34f369d0102e@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020535} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testea097f7a-9fc2-437d-8f15-34f369d0102e@bitwarden.com","status":400},"timestamp":1767217020536} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testea097f7a-9fc2-437d-8f15-34f369d0102e@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020536} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test68eec49a-18cd-4089-8a71-ce7ddc89f9fc@bitwarden.com","status":204},"timestamp":1767217020536} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test68eec49a-18cd-4089-8a71-ce7ddc89f9fc@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020537} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test68eec49a-18cd-4089-8a71-ce7ddc89f9fc@bitwarden.com","status":400},"timestamp":1767217020537} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test68eec49a-18cd-4089-8a71-ce7ddc89f9fc@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020537} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test667f4950-0904-42f4-add9-2c687737e28c@bitwarden.com","status":204},"timestamp":1767217020538} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test667f4950-0904-42f4-add9-2c687737e28c@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020538} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test667f4950-0904-42f4-add9-2c687737e28c@bitwarden.com","status":400},"timestamp":1767217020538} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test667f4950-0904-42f4-add9-2c687737e28c@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020538} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test85f7d187-e41e-4408-ad91-05d1f6ff819f@bitwarden.com","status":204},"timestamp":1767217020539} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test85f7d187-e41e-4408-ad91-05d1f6ff819f@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020539} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test85f7d187-e41e-4408-ad91-05d1f6ff819f@bitwarden.com","status":400},"timestamp":1767217020539} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test85f7d187-e41e-4408-ad91-05d1f6ff819f@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020539} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testc128c32b-89f9-4f8f-a5f9-cd5dc3b5f560@bitwarden.com","status":204},"timestamp":1767217020540} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testc128c32b-89f9-4f8f-a5f9-cd5dc3b5f560@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020540} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testc128c32b-89f9-4f8f-a5f9-cd5dc3b5f560@bitwarden.com","status":400},"timestamp":1767217020540} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testc128c32b-89f9-4f8f-a5f9-cd5dc3b5f560@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020541} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test7bb19614-a3e6-44d9-811f-c1daf336d0d9@bitwarden.com","status":204},"timestamp":1767217020541} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test7bb19614-a3e6-44d9-811f-c1daf336d0d9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020541} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test7bb19614-a3e6-44d9-811f-c1daf336d0d9@bitwarden.com","status":400},"timestamp":1767217020542} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test7bb19614-a3e6-44d9-811f-c1daf336d0d9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020542} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testfd53fb58-c9b9-4a76-9c5e-cf2a38582ec6@bitwarden.com","status":204},"timestamp":1767217020542} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testfd53fb58-c9b9-4a76-9c5e-cf2a38582ec6@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020542} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testfd53fb58-c9b9-4a76-9c5e-cf2a38582ec6@bitwarden.com","status":400},"timestamp":1767217020543} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testfd53fb58-c9b9-4a76-9c5e-cf2a38582ec6@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020543} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testb5c37c94-67c3-423a-b263-eba1fc3d2f2b@bitwarden.com","status":204},"timestamp":1767217020544} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testb5c37c94-67c3-423a-b263-eba1fc3d2f2b@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020544} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testb5c37c94-67c3-423a-b263-eba1fc3d2f2b@bitwarden.com","status":400},"timestamp":1767217020544} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testb5c37c94-67c3-423a-b263-eba1fc3d2f2b@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020544} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test65447f69-9213-47e2-bf99-609eb03bd7e8@bitwarden.com","status":204},"timestamp":1767217020545} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test65447f69-9213-47e2-bf99-609eb03bd7e8@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020545} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test65447f69-9213-47e2-bf99-609eb03bd7e8@bitwarden.com","status":400},"timestamp":1767217020545} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test65447f69-9213-47e2-bf99-609eb03bd7e8@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020545} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test05b927c5-10e6-4a4e-8cb4-375f42f0da4b@bitwarden.com","status":204},"timestamp":1767217020546} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test05b927c5-10e6-4a4e-8cb4-375f42f0da4b@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020546} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test05b927c5-10e6-4a4e-8cb4-375f42f0da4b@bitwarden.com","status":400},"timestamp":1767217020546} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test05b927c5-10e6-4a4e-8cb4-375f42f0da4b@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020547} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test282e9e9e-b455-4e02-b81a-6fed3d66d471@bitwarden.com","status":204},"timestamp":1767217020547} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test282e9e9e-b455-4e02-b81a-6fed3d66d471@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020547} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test282e9e9e-b455-4e02-b81a-6fed3d66d471@bitwarden.com","status":400},"timestamp":1767217020548} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test282e9e9e-b455-4e02-b81a-6fed3d66d471@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020548} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test5a359b82-080e-4ae3-a374-b02e46221b6a@bitwarden.com","status":204},"timestamp":1767217020549} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test5a359b82-080e-4ae3-a374-b02e46221b6a@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020549} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test5a359b82-080e-4ae3-a374-b02e46221b6a@bitwarden.com","status":400},"timestamp":1767217020549} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test5a359b82-080e-4ae3-a374-b02e46221b6a@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020550} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test56ca8f0c-ff9b-49a2-a781-0d4bb08717f2@bitwarden.com","status":204},"timestamp":1767217020551} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test56ca8f0c-ff9b-49a2-a781-0d4bb08717f2@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020551} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test56ca8f0c-ff9b-49a2-a781-0d4bb08717f2@bitwarden.com","status":400},"timestamp":1767217020552} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test56ca8f0c-ff9b-49a2-a781-0d4bb08717f2@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020552} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test2abb5376-14ae-421d-8b54-1c5ec7951ad6@bitwarden.com","status":204},"timestamp":1767217020553} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test2abb5376-14ae-421d-8b54-1c5ec7951ad6@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020553} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test2abb5376-14ae-421d-8b54-1c5ec7951ad6@bitwarden.com","status":400},"timestamp":1767217020553} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test2abb5376-14ae-421d-8b54-1c5ec7951ad6@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020553} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test684997a1-5339-4944-b639-8894ba27de74@bitwarden.com","status":204},"timestamp":1767217020554} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test684997a1-5339-4944-b639-8894ba27de74@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020554} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test684997a1-5339-4944-b639-8894ba27de74@bitwarden.com","status":400},"timestamp":1767217020555} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test684997a1-5339-4944-b639-8894ba27de74@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020555} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testc4fad0d2-047e-4496-ba98-3dc8b5735be9@bitwarden.com","status":204},"timestamp":1767217020556} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testc4fad0d2-047e-4496-ba98-3dc8b5735be9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020556} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testc4fad0d2-047e-4496-ba98-3dc8b5735be9@bitwarden.com","status":400},"timestamp":1767217020556} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testc4fad0d2-047e-4496-ba98-3dc8b5735be9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020556} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testb832b0aa-4fcd-4eab-baec-909d63e3a0c4@bitwarden.com","status":204},"timestamp":1767217020557} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testb832b0aa-4fcd-4eab-baec-909d63e3a0c4@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020557} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testb832b0aa-4fcd-4eab-baec-909d63e3a0c4@bitwarden.com","status":400},"timestamp":1767217020558} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testb832b0aa-4fcd-4eab-baec-909d63e3a0c4@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020558} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test92c7eb22-6169-407b-9fe3-f53f250e6e75@bitwarden.com","status":204},"timestamp":1767217020559} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test92c7eb22-6169-407b-9fe3-f53f250e6e75@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020559} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test92c7eb22-6169-407b-9fe3-f53f250e6e75@bitwarden.com","status":400},"timestamp":1767217020559} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test92c7eb22-6169-407b-9fe3-f53f250e6e75@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020559} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test940fc891-93bb-4551-874c-553fbbe3a9c9@bitwarden.com","status":204},"timestamp":1767217020560} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test940fc891-93bb-4551-874c-553fbbe3a9c9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020560} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test940fc891-93bb-4551-874c-553fbbe3a9c9@bitwarden.com","status":400},"timestamp":1767217020561} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test940fc891-93bb-4551-874c-553fbbe3a9c9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020561} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testf1524f7f-d5cc-411f-90be-2e683820045c@bitwarden.com","status":204},"timestamp":1767217020562} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testf1524f7f-d5cc-411f-90be-2e683820045c@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020562} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testf1524f7f-d5cc-411f-90be-2e683820045c@bitwarden.com","status":400},"timestamp":1767217020562} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testf1524f7f-d5cc-411f-90be-2e683820045c@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020563} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test47bf8e0e-aa62-4cae-950e-1c6369c3cbe0@bitwarden.com","status":204},"timestamp":1767217020563} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test47bf8e0e-aa62-4cae-950e-1c6369c3cbe0@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020563} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test47bf8e0e-aa62-4cae-950e-1c6369c3cbe0@bitwarden.com","status":400},"timestamp":1767217020564} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test47bf8e0e-aa62-4cae-950e-1c6369c3cbe0@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020564} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testbfe7fe00-309b-40cb-bff9-f1c9eeb4527c@bitwarden.com","status":204},"timestamp":1767217020565} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testbfe7fe00-309b-40cb-bff9-f1c9eeb4527c@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020565} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testbfe7fe00-309b-40cb-bff9-f1c9eeb4527c@bitwarden.com","status":400},"timestamp":1767217020565} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testbfe7fe00-309b-40cb-bff9-f1c9eeb4527c@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020565} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test6096f30c-f92f-4fa8-90db-d5eecb490054@bitwarden.com","status":204},"timestamp":1767217020566} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test6096f30c-f92f-4fa8-90db-d5eecb490054@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020566} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test6096f30c-f92f-4fa8-90db-d5eecb490054@bitwarden.com","status":400},"timestamp":1767217020567} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test6096f30c-f92f-4fa8-90db-d5eecb490054@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020567} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testa829ff2a-5db9-43cb-917f-3606575d08b6@bitwarden.com","status":204},"timestamp":1767217020567} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testa829ff2a-5db9-43cb-917f-3606575d08b6@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020568} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testa829ff2a-5db9-43cb-917f-3606575d08b6@bitwarden.com","status":400},"timestamp":1767217020568} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testa829ff2a-5db9-43cb-917f-3606575d08b6@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020568} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test47bca56c-71f3-40f5-a357-a0c05b22c1d2@bitwarden.com","status":204},"timestamp":1767217020569} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test47bca56c-71f3-40f5-a357-a0c05b22c1d2@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020569} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test47bca56c-71f3-40f5-a357-a0c05b22c1d2@bitwarden.com","status":400},"timestamp":1767217020569} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test47bca56c-71f3-40f5-a357-a0c05b22c1d2@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020569} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test0d3eb1e0-2db7-49cf-ae69-02c6dbfa8da9@bitwarden.com","status":204},"timestamp":1767217020570} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test0d3eb1e0-2db7-49cf-ae69-02c6dbfa8da9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020570} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test0d3eb1e0-2db7-49cf-ae69-02c6dbfa8da9@bitwarden.com","status":400},"timestamp":1767217020571} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test0d3eb1e0-2db7-49cf-ae69-02c6dbfa8da9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020571} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test9d0f3ec8-7e02-4cb7-955a-0b075127ae29@bitwarden.com","status":204},"timestamp":1767217020572} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test9d0f3ec8-7e02-4cb7-955a-0b075127ae29@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020572} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test9d0f3ec8-7e02-4cb7-955a-0b075127ae29@bitwarden.com","status":400},"timestamp":1767217020572} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test9d0f3ec8-7e02-4cb7-955a-0b075127ae29@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020572} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test5db9fa5c-7ad6-4d2d-98b8-2b42d8cae01e@bitwarden.com","status":204},"timestamp":1767217020573} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test5db9fa5c-7ad6-4d2d-98b8-2b42d8cae01e@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020573} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test5db9fa5c-7ad6-4d2d-98b8-2b42d8cae01e@bitwarden.com","status":400},"timestamp":1767217020573} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test5db9fa5c-7ad6-4d2d-98b8-2b42d8cae01e@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020573} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testbd7b099e-1ac0-4c26-971f-26aa00766b2d@bitwarden.com","status":204},"timestamp":1767217020574} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testbd7b099e-1ac0-4c26-971f-26aa00766b2d@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020574} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testbd7b099e-1ac0-4c26-971f-26aa00766b2d@bitwarden.com","status":400},"timestamp":1767217020575} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testbd7b099e-1ac0-4c26-971f-26aa00766b2d@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020575} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test5c60f7d9-e411-47ea-bd92-227d4cd1a528@bitwarden.com","status":204},"timestamp":1767217020576} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test5c60f7d9-e411-47ea-bd92-227d4cd1a528@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020576} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test5c60f7d9-e411-47ea-bd92-227d4cd1a528@bitwarden.com","status":400},"timestamp":1767217020576} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test5c60f7d9-e411-47ea-bd92-227d4cd1a528@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020576} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testb2bf83b6-70ce-41c4-9d78-68c213ba5e79@bitwarden.com","status":204},"timestamp":1767217020577} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testb2bf83b6-70ce-41c4-9d78-68c213ba5e79@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020577} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testb2bf83b6-70ce-41c4-9d78-68c213ba5e79@bitwarden.com","status":400},"timestamp":1767217020577} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testb2bf83b6-70ce-41c4-9d78-68c213ba5e79@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020577} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testd59854e0-071e-4b92-87d3-b1361ddf4f19@bitwarden.com","status":204},"timestamp":1767217020578} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testd59854e0-071e-4b92-87d3-b1361ddf4f19@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020578} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testd59854e0-071e-4b92-87d3-b1361ddf4f19@bitwarden.com","status":400},"timestamp":1767217020578} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testd59854e0-071e-4b92-87d3-b1361ddf4f19@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020578} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test1e570338-9121-4290-87cc-b9b9c997b23a@bitwarden.com","status":204},"timestamp":1767217020579} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test1e570338-9121-4290-87cc-b9b9c997b23a@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217020579} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test1e570338-9121-4290-87cc-b9b9c997b23a@bitwarden.com","status":400},"timestamp":1767217020580} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test1e570338-9121-4290-87cc-b9b9c997b23a@bitwarden.com","actual":400,"expected":200},"timestamp":1767217020580} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test7200c4de-c3f4-40d5-8f96-350a3bd9670b@bitwarden.com","status":204},"timestamp":1767217103057} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test7200c4de-c3f4-40d5-8f96-350a3bd9670b@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103062} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test7200c4de-c3f4-40d5-8f96-350a3bd9670b@bitwarden.com","status":400},"timestamp":1767217103091} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test7200c4de-c3f4-40d5-8f96-350a3bd9670b@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103091} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test22cf0a3f-aebd-4051-9798-7c50006fe0e1@bitwarden.com","status":204},"timestamp":1767217103123} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test22cf0a3f-aebd-4051-9798-7c50006fe0e1@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103123} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test22cf0a3f-aebd-4051-9798-7c50006fe0e1@bitwarden.com","status":400},"timestamp":1767217103124} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test22cf0a3f-aebd-4051-9798-7c50006fe0e1@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103124} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test6f3b2ee8-6e14-4c72-8f7e-849f03b74a3e@bitwarden.com","status":204},"timestamp":1767217103125} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test6f3b2ee8-6e14-4c72-8f7e-849f03b74a3e@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103126} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test6f3b2ee8-6e14-4c72-8f7e-849f03b74a3e@bitwarden.com","status":400},"timestamp":1767217103126} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test6f3b2ee8-6e14-4c72-8f7e-849f03b74a3e@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103126} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test71e2b905-de2e-4aa6-974a-5400475517b9@bitwarden.com","status":204},"timestamp":1767217103127} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test71e2b905-de2e-4aa6-974a-5400475517b9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103127} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test71e2b905-de2e-4aa6-974a-5400475517b9@bitwarden.com","status":400},"timestamp":1767217103128} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test71e2b905-de2e-4aa6-974a-5400475517b9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103128} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test4b3133c1-7c1c-48da-a0c1-db54592de41e@bitwarden.com","status":204},"timestamp":1767217103129} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test4b3133c1-7c1c-48da-a0c1-db54592de41e@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103129} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test4b3133c1-7c1c-48da-a0c1-db54592de41e@bitwarden.com","status":400},"timestamp":1767217103130} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test4b3133c1-7c1c-48da-a0c1-db54592de41e@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103130} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testf8e99a1f-35f6-41fa-bf6e-c3fc9a2d89aa@bitwarden.com","status":204},"timestamp":1767217103131} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testf8e99a1f-35f6-41fa-bf6e-c3fc9a2d89aa@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103131} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testf8e99a1f-35f6-41fa-bf6e-c3fc9a2d89aa@bitwarden.com","status":400},"timestamp":1767217103131} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testf8e99a1f-35f6-41fa-bf6e-c3fc9a2d89aa@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103132} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test2a91889e-2dbd-4400-91d2-a7a1277b1976@bitwarden.com","status":204},"timestamp":1767217103133} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test2a91889e-2dbd-4400-91d2-a7a1277b1976@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103133} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test2a91889e-2dbd-4400-91d2-a7a1277b1976@bitwarden.com","status":400},"timestamp":1767217103133} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test2a91889e-2dbd-4400-91d2-a7a1277b1976@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103133} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test115626a7-035b-47c2-926d-51728656bfbb@bitwarden.com","status":204},"timestamp":1767217103134} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test115626a7-035b-47c2-926d-51728656bfbb@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103134} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test115626a7-035b-47c2-926d-51728656bfbb@bitwarden.com","status":400},"timestamp":1767217103135} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test115626a7-035b-47c2-926d-51728656bfbb@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103135} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testd5c06a44-76ed-4f30-8647-344f9abca932@bitwarden.com","status":204},"timestamp":1767217103135} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testd5c06a44-76ed-4f30-8647-344f9abca932@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103135} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testd5c06a44-76ed-4f30-8647-344f9abca932@bitwarden.com","status":400},"timestamp":1767217103136} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testd5c06a44-76ed-4f30-8647-344f9abca932@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103136} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test1eef218b-d7b4-4cd9-a85d-25bf167a90a4@bitwarden.com","status":204},"timestamp":1767217103137} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test1eef218b-d7b4-4cd9-a85d-25bf167a90a4@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103137} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test1eef218b-d7b4-4cd9-a85d-25bf167a90a4@bitwarden.com","status":400},"timestamp":1767217103137} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test1eef218b-d7b4-4cd9-a85d-25bf167a90a4@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103137} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testb091c01c-44d9-459b-8049-c1facc30d939@bitwarden.com","status":204},"timestamp":1767217103138} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testb091c01c-44d9-459b-8049-c1facc30d939@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103138} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testb091c01c-44d9-459b-8049-c1facc30d939@bitwarden.com","status":400},"timestamp":1767217103139} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testb091c01c-44d9-459b-8049-c1facc30d939@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103139} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test50bd241c-49f3-4ca8-b38a-31865ce78ca2@bitwarden.com","status":204},"timestamp":1767217103140} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test50bd241c-49f3-4ca8-b38a-31865ce78ca2@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103140} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test50bd241c-49f3-4ca8-b38a-31865ce78ca2@bitwarden.com","status":400},"timestamp":1767217103140} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test50bd241c-49f3-4ca8-b38a-31865ce78ca2@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103140} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-teste5d41d19-617c-4250-801f-2d5a794f80f2@bitwarden.com","status":204},"timestamp":1767217103141} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-teste5d41d19-617c-4250-801f-2d5a794f80f2@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103141} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-teste5d41d19-617c-4250-801f-2d5a794f80f2@bitwarden.com","status":400},"timestamp":1767217103141} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-teste5d41d19-617c-4250-801f-2d5a794f80f2@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103141} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test2ff5b61a-725f-4cfa-add9-647c15abd0b1@bitwarden.com","status":204},"timestamp":1767217103142} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test2ff5b61a-725f-4cfa-add9-647c15abd0b1@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103142} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test2ff5b61a-725f-4cfa-add9-647c15abd0b1@bitwarden.com","status":400},"timestamp":1767217103143} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test2ff5b61a-725f-4cfa-add9-647c15abd0b1@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103143} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test43bfa58f-6639-4130-9f9e-7810a7a19a33@bitwarden.com","status":204},"timestamp":1767217103144} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test43bfa58f-6639-4130-9f9e-7810a7a19a33@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103144} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test43bfa58f-6639-4130-9f9e-7810a7a19a33@bitwarden.com","status":400},"timestamp":1767217103144} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test43bfa58f-6639-4130-9f9e-7810a7a19a33@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103144} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test54110a4c-6317-49f3-aaeb-159a9f278187@bitwarden.com","status":204},"timestamp":1767217103145} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test54110a4c-6317-49f3-aaeb-159a9f278187@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103145} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test54110a4c-6317-49f3-aaeb-159a9f278187@bitwarden.com","status":400},"timestamp":1767217103145} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test54110a4c-6317-49f3-aaeb-159a9f278187@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103145} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test978a4a77-75c1-4538-b7fd-d9799598044b@bitwarden.com","status":204},"timestamp":1767217103146} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test978a4a77-75c1-4538-b7fd-d9799598044b@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103146} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test978a4a77-75c1-4538-b7fd-d9799598044b@bitwarden.com","status":400},"timestamp":1767217103147} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test978a4a77-75c1-4538-b7fd-d9799598044b@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103147} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testc8c69ee4-d9bd-4279-9429-66af89027f68@bitwarden.com","status":204},"timestamp":1767217103148} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testc8c69ee4-d9bd-4279-9429-66af89027f68@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103148} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testc8c69ee4-d9bd-4279-9429-66af89027f68@bitwarden.com","status":400},"timestamp":1767217103148} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testc8c69ee4-d9bd-4279-9429-66af89027f68@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103148} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test354e62ae-dd73-4aaf-ac17-54e59c5a57ed@bitwarden.com","status":204},"timestamp":1767217103149} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test354e62ae-dd73-4aaf-ac17-54e59c5a57ed@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103149} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test354e62ae-dd73-4aaf-ac17-54e59c5a57ed@bitwarden.com","status":400},"timestamp":1767217103149} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test354e62ae-dd73-4aaf-ac17-54e59c5a57ed@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103149} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testecb04cb7-f79b-4f08-8858-055c67794c91@bitwarden.com","status":204},"timestamp":1767217103150} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testecb04cb7-f79b-4f08-8858-055c67794c91@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103150} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testecb04cb7-f79b-4f08-8858-055c67794c91@bitwarden.com","status":400},"timestamp":1767217103151} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testecb04cb7-f79b-4f08-8858-055c67794c91@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103151} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test39bc2bf4-0357-4335-ac7b-788f5252d6ab@bitwarden.com","status":204},"timestamp":1767217103151} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test39bc2bf4-0357-4335-ac7b-788f5252d6ab@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103151} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test39bc2bf4-0357-4335-ac7b-788f5252d6ab@bitwarden.com","status":400},"timestamp":1767217103152} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test39bc2bf4-0357-4335-ac7b-788f5252d6ab@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103152} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test1d40b477-ad56-4022-a521-c2392ead447d@bitwarden.com","status":204},"timestamp":1767217103153} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test1d40b477-ad56-4022-a521-c2392ead447d@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103153} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test1d40b477-ad56-4022-a521-c2392ead447d@bitwarden.com","status":400},"timestamp":1767217103153} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test1d40b477-ad56-4022-a521-c2392ead447d@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103153} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testad7b05b3-e46f-43ef-bfae-e1a8684f64fb@bitwarden.com","status":204},"timestamp":1767217103154} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testad7b05b3-e46f-43ef-bfae-e1a8684f64fb@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103154} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testad7b05b3-e46f-43ef-bfae-e1a8684f64fb@bitwarden.com","status":400},"timestamp":1767217103155} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testad7b05b3-e46f-43ef-bfae-e1a8684f64fb@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103155} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testf6aae59a-4b31-4def-858c-6365785110da@bitwarden.com","status":204},"timestamp":1767217103155} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testf6aae59a-4b31-4def-858c-6365785110da@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103155} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testf6aae59a-4b31-4def-858c-6365785110da@bitwarden.com","status":400},"timestamp":1767217103156} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testf6aae59a-4b31-4def-858c-6365785110da@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103156} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test81720b75-4dfb-4af2-aced-5aa02eab6af7@bitwarden.com","status":204},"timestamp":1767217103157} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test81720b75-4dfb-4af2-aced-5aa02eab6af7@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103157} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test81720b75-4dfb-4af2-aced-5aa02eab6af7@bitwarden.com","status":400},"timestamp":1767217103157} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test81720b75-4dfb-4af2-aced-5aa02eab6af7@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103157} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test921afa7a-0e2b-42d9-9976-b63e440d3bee@bitwarden.com","status":204},"timestamp":1767217103158} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test921afa7a-0e2b-42d9-9976-b63e440d3bee@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103158} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test921afa7a-0e2b-42d9-9976-b63e440d3bee@bitwarden.com","status":400},"timestamp":1767217103158} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test921afa7a-0e2b-42d9-9976-b63e440d3bee@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103158} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test5ec86a7b-9f74-4aa5-aa7e-53101210f238@bitwarden.com","status":204},"timestamp":1767217103159} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test5ec86a7b-9f74-4aa5-aa7e-53101210f238@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103159} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test5ec86a7b-9f74-4aa5-aa7e-53101210f238@bitwarden.com","status":400},"timestamp":1767217103160} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test5ec86a7b-9f74-4aa5-aa7e-53101210f238@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103160} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testc92383a7-25ed-412b-bd6a-d67ac186ed6a@bitwarden.com","status":204},"timestamp":1767217103160} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testc92383a7-25ed-412b-bd6a-d67ac186ed6a@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103161} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testc92383a7-25ed-412b-bd6a-d67ac186ed6a@bitwarden.com","status":400},"timestamp":1767217103161} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testc92383a7-25ed-412b-bd6a-d67ac186ed6a@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103161} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test5eb8a550-5643-4d1f-bc62-a199361cd71d@bitwarden.com","status":204},"timestamp":1767217103162} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test5eb8a550-5643-4d1f-bc62-a199361cd71d@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103162} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test5eb8a550-5643-4d1f-bc62-a199361cd71d@bitwarden.com","status":400},"timestamp":1767217103162} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test5eb8a550-5643-4d1f-bc62-a199361cd71d@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103162} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testc945b848-4cad-4df2-9c6e-75a053ffcd07@bitwarden.com","status":204},"timestamp":1767217103163} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testc945b848-4cad-4df2-9c6e-75a053ffcd07@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103163} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testc945b848-4cad-4df2-9c6e-75a053ffcd07@bitwarden.com","status":400},"timestamp":1767217103164} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testc945b848-4cad-4df2-9c6e-75a053ffcd07@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103164} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testb059029d-47c0-4a44-ab13-94fbb856f9c6@bitwarden.com","status":204},"timestamp":1767217103164} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testb059029d-47c0-4a44-ab13-94fbb856f9c6@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103164} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testb059029d-47c0-4a44-ab13-94fbb856f9c6@bitwarden.com","status":400},"timestamp":1767217103165} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testb059029d-47c0-4a44-ab13-94fbb856f9c6@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103165} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testba148834-d085-40a5-866e-c75ab6701336@bitwarden.com","status":204},"timestamp":1767217103166} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testba148834-d085-40a5-866e-c75ab6701336@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103166} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testba148834-d085-40a5-866e-c75ab6701336@bitwarden.com","status":400},"timestamp":1767217103166} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testba148834-d085-40a5-866e-c75ab6701336@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103166} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testbbc13657-891c-4eb3-9cb0-df592f1851f9@bitwarden.com","status":204},"timestamp":1767217103167} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testbbc13657-891c-4eb3-9cb0-df592f1851f9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103167} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testbbc13657-891c-4eb3-9cb0-df592f1851f9@bitwarden.com","status":400},"timestamp":1767217103167} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testbbc13657-891c-4eb3-9cb0-df592f1851f9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103167} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test0d54c6cf-99e0-4e35-a40d-2cffce8546b0@bitwarden.com","status":204},"timestamp":1767217103168} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test0d54c6cf-99e0-4e35-a40d-2cffce8546b0@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103168} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test0d54c6cf-99e0-4e35-a40d-2cffce8546b0@bitwarden.com","status":400},"timestamp":1767217103169} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test0d54c6cf-99e0-4e35-a40d-2cffce8546b0@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103169} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test232750b1-74c6-4b7f-a62e-58187503e18a@bitwarden.com","status":204},"timestamp":1767217103170} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test232750b1-74c6-4b7f-a62e-58187503e18a@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103170} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test232750b1-74c6-4b7f-a62e-58187503e18a@bitwarden.com","status":400},"timestamp":1767217103170} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test232750b1-74c6-4b7f-a62e-58187503e18a@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103170} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-teste5efeaef-c249-4bc3-8c03-9710d951c387@bitwarden.com","status":204},"timestamp":1767217103171} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-teste5efeaef-c249-4bc3-8c03-9710d951c387@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103171} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-teste5efeaef-c249-4bc3-8c03-9710d951c387@bitwarden.com","status":400},"timestamp":1767217103171} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-teste5efeaef-c249-4bc3-8c03-9710d951c387@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103171} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test182de32c-2248-4162-bc0c-93a2ebd69d15@bitwarden.com","status":204},"timestamp":1767217103172} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test182de32c-2248-4162-bc0c-93a2ebd69d15@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103172} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test182de32c-2248-4162-bc0c-93a2ebd69d15@bitwarden.com","status":400},"timestamp":1767217103173} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test182de32c-2248-4162-bc0c-93a2ebd69d15@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103173} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test6bb810ef-4c54-4536-9e4c-34c7e1568e53@bitwarden.com","status":204},"timestamp":1767217103173} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test6bb810ef-4c54-4536-9e4c-34c7e1568e53@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103173} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test6bb810ef-4c54-4536-9e4c-34c7e1568e53@bitwarden.com","status":400},"timestamp":1767217103174} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test6bb810ef-4c54-4536-9e4c-34c7e1568e53@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103174} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test153442a5-d749-44a5-b3c2-271d20e3d742@bitwarden.com","status":204},"timestamp":1767217103175} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test153442a5-d749-44a5-b3c2-271d20e3d742@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103175} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test153442a5-d749-44a5-b3c2-271d20e3d742@bitwarden.com","status":400},"timestamp":1767217103175} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test153442a5-d749-44a5-b3c2-271d20e3d742@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103175} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test77a5db79-1f93-4cd2-a31f-e71a9c0ff6fb@bitwarden.com","status":204},"timestamp":1767217103176} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test77a5db79-1f93-4cd2-a31f-e71a9c0ff6fb@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103176} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test77a5db79-1f93-4cd2-a31f-e71a9c0ff6fb@bitwarden.com","status":400},"timestamp":1767217103177} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test77a5db79-1f93-4cd2-a31f-e71a9c0ff6fb@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103177} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test565494b8-b4c5-42bc-8864-c70f0478841b@bitwarden.com","status":204},"timestamp":1767217103178} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test565494b8-b4c5-42bc-8864-c70f0478841b@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103178} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test565494b8-b4c5-42bc-8864-c70f0478841b@bitwarden.com","status":400},"timestamp":1767217103178} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test565494b8-b4c5-42bc-8864-c70f0478841b@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103178} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test44316529-92ac-4e0b-8636-2da6c6a9a4dc@bitwarden.com","status":204},"timestamp":1767217103179} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test44316529-92ac-4e0b-8636-2da6c6a9a4dc@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103179} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test44316529-92ac-4e0b-8636-2da6c6a9a4dc@bitwarden.com","status":400},"timestamp":1767217103179} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test44316529-92ac-4e0b-8636-2da6c6a9a4dc@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103179} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testdf718129-6d96-47b5-9654-241e7e4e744a@bitwarden.com","status":204},"timestamp":1767217103180} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testdf718129-6d96-47b5-9654-241e7e4e744a@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103180} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testdf718129-6d96-47b5-9654-241e7e4e744a@bitwarden.com","status":400},"timestamp":1767217103181} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testdf718129-6d96-47b5-9654-241e7e4e744a@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103181} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test07c96543-cc34-46d3-acbb-12c3a0722c6b@bitwarden.com","status":204},"timestamp":1767217103181} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test07c96543-cc34-46d3-acbb-12c3a0722c6b@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103182} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test07c96543-cc34-46d3-acbb-12c3a0722c6b@bitwarden.com","status":400},"timestamp":1767217103182} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test07c96543-cc34-46d3-acbb-12c3a0722c6b@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103182} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test5d724177-636a-48f6-afad-d93c99d61a64@bitwarden.com","status":204},"timestamp":1767217103183} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test5d724177-636a-48f6-afad-d93c99d61a64@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103183} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test5d724177-636a-48f6-afad-d93c99d61a64@bitwarden.com","status":400},"timestamp":1767217103183} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test5d724177-636a-48f6-afad-d93c99d61a64@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103183} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testcdba648a-3094-48d9-91b0-4a21bc5836b7@bitwarden.com","status":204},"timestamp":1767217103184} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testcdba648a-3094-48d9-91b0-4a21bc5836b7@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103184} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testcdba648a-3094-48d9-91b0-4a21bc5836b7@bitwarden.com","status":400},"timestamp":1767217103184} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testcdba648a-3094-48d9-91b0-4a21bc5836b7@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103185} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testbae00768-696a-4d71-bfcd-5dc6cbaee70c@bitwarden.com","status":204},"timestamp":1767217103185} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testbae00768-696a-4d71-bfcd-5dc6cbaee70c@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103185} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testbae00768-696a-4d71-bfcd-5dc6cbaee70c@bitwarden.com","status":400},"timestamp":1767217103186} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testbae00768-696a-4d71-bfcd-5dc6cbaee70c@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103186} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test684b9e50-4425-459a-8923-5bde8186cec9@bitwarden.com","status":204},"timestamp":1767217103187} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test684b9e50-4425-459a-8923-5bde8186cec9@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103187} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test684b9e50-4425-459a-8923-5bde8186cec9@bitwarden.com","status":400},"timestamp":1767217103187} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test684b9e50-4425-459a-8923-5bde8186cec9@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103187} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testcfbcfd1e-a767-4269-bee6-34f71af4b0a1@bitwarden.com","status":204},"timestamp":1767217103188} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testcfbcfd1e-a767-4269-bee6-34f71af4b0a1@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103188} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testcfbcfd1e-a767-4269-bee6-34f71af4b0a1@bitwarden.com","status":400},"timestamp":1767217103188} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testcfbcfd1e-a767-4269-bee6-34f71af4b0a1@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103188} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test619d38cd-1900-4600-a268-f87cbbbefdc5@bitwarden.com","status":204},"timestamp":1767217103189} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test619d38cd-1900-4600-a268-f87cbbbefdc5@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103189} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test619d38cd-1900-4600-a268-f87cbbbefdc5@bitwarden.com","status":400},"timestamp":1767217103190} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test619d38cd-1900-4600-a268-f87cbbbefdc5@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103190} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testd58d7931-7c74-4557-9957-ba8d56f6dbcf@bitwarden.com","status":204},"timestamp":1767217103190} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testd58d7931-7c74-4557-9957-ba8d56f6dbcf@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103190} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testd58d7931-7c74-4557-9957-ba8d56f6dbcf@bitwarden.com","status":400},"timestamp":1767217103191} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testd58d7931-7c74-4557-9957-ba8d56f6dbcf@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103191} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testa2b761a9-ed49-447c-bd63-b31c6285c565@bitwarden.com","status":204},"timestamp":1767217103192} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testa2b761a9-ed49-447c-bd63-b31c6285c565@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103192} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testa2b761a9-ed49-447c-bd63-b31c6285c565@bitwarden.com","status":400},"timestamp":1767217103192} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testa2b761a9-ed49-447c-bd63-b31c6285c565@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103192} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testd6b5aac7-5432-4f8e-8cfb-b3fa49d968d6@bitwarden.com","status":204},"timestamp":1767217103193} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testd6b5aac7-5432-4f8e-8cfb-b3fa49d968d6@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217103193} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testd6b5aac7-5432-4f8e-8cfb-b3fa49d968d6@bitwarden.com","status":400},"timestamp":1767217103194} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testd6b5aac7-5432-4f8e-8cfb-b3fa49d968d6@bitwarden.com","actual":400,"expected":200},"timestamp":1767217103194} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test286a3058-9578-4f8a-812b-fabafd97faa0@bitwarden.com","status":204},"timestamp":1767217229982} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test286a3058-9578-4f8a-812b-fabafd97faa0@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217229988} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test286a3058-9578-4f8a-812b-fabafd97faa0@bitwarden.com","status":400},"timestamp":1767217230012} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test286a3058-9578-4f8a-812b-fabafd97faa0@bitwarden.com","actual":400,"expected":200},"timestamp":1767217230012} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testd61adf5e-8f98-48d9-b625-18cd639065fd@bitwarden.com","status":204},"timestamp":1767217240393} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testd61adf5e-8f98-48d9-b625-18cd639065fd@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217240403} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testd61adf5e-8f98-48d9-b625-18cd639065fd@bitwarden.com","status":400},"timestamp":1767217240500} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testd61adf5e-8f98-48d9-b625-18cd639065fd@bitwarden.com","actual":400,"expected":200},"timestamp":1767217240501} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test68356f6b-6d24-4160-a256-04dfe65e2682@bitwarden.com","status":204},"timestamp":1767217297031} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test68356f6b-6d24-4160-a256-04dfe65e2682@bitwarden.com","hasUnlock":false,"hasAuth":false,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217303691} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test68356f6b-6d24-4160-a256-04dfe65e2682@bitwarden.com","status":400},"timestamp":1767217303806} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test68356f6b-6d24-4160-a256-04dfe65e2682@bitwarden.com","actual":400,"expected":200},"timestamp":1767217328723} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testdd047e03-bfb1-42b6-9d8a-4e12f68d09c0@bitwarden.com","status":204},"timestamp":1767217411260} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testdd047e03-bfb1-42b6-9d8a-4e12f68d09c0@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217411267} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testdd047e03-bfb1-42b6-9d8a-4e12f68d09c0@bitwarden.com","status":400},"timestamp":1767217411294} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testdd047e03-bfb1-42b6-9d8a-4e12f68d09c0@bitwarden.com","actual":400,"expected":200},"timestamp":1767217411294} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testb4cc71c9-625c-4935-ad34-3f44f2f48086@bitwarden.com","status":204},"timestamp":1767217431762} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testb4cc71c9-625c-4935-ad34-3f44f2f48086@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217434718} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testb4cc71c9-625c-4935-ad34-3f44f2f48086@bitwarden.com","status":400},"timestamp":1767217434856} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testb4cc71c9-625c-4935-ad34-3f44f2f48086@bitwarden.com","actual":400,"expected":200},"timestamp":1767217436616} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testfad6d4db-1542-4f80-b7eb-962224760601@bitwarden.com","status":204},"timestamp":1767217567241} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testfad6d4db-1542-4f80-b7eb-962224760601@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217569544} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testfad6d4db-1542-4f80-b7eb-962224760601@bitwarden.com","status":400},"timestamp":1767217569650} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testfad6d4db-1542-4f80-b7eb-962224760601@bitwarden.com","actual":400,"expected":200},"timestamp":1767217570091} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testae200d65-2cb9-4e7e-9072-e85b568ebbeb@bitwarden.com","status":204},"timestamp":1767217718921} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testae200d65-2cb9-4e7e-9072-e85b568ebbeb@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767217719883} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testae200d65-2cb9-4e7e-9072-e85b568ebbeb@bitwarden.com","status":400},"timestamp":1767217720008} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testae200d65-2cb9-4e7e-9072-e85b568ebbeb@bitwarden.com","actual":400,"expected":200},"timestamp":1767217720452} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test37b691b1-46a1-465f-90be-bf31882069be@bitwarden.com","status":204},"timestamp":1767218009023} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test37b691b1-46a1-465f-90be-bf31882069be@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767218009755} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test37b691b1-46a1-465f-90be-bf31882069be@bitwarden.com","status":400},"timestamp":1767218009829} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test37b691b1-46a1-465f-90be-bf31882069be@bitwarden.com","actual":400,"expected":200},"timestamp":1767218009829} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test76740011-3f81-4f3e-a0b4-01cf23e17a78@bitwarden.com","status":204},"timestamp":1767218084895} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test76740011-3f81-4f3e-a0b4-01cf23e17a78@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767218084901} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test76740011-3f81-4f3e-a0b4-01cf23e17a78@bitwarden.com","status":400},"timestamp":1767218084928} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test76740011-3f81-4f3e-a0b4-01cf23e17a78@bitwarden.com","actual":400,"expected":200},"timestamp":1767218084929} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-testb44f7181-a39d-4720-85e6-cdbed603f14b@bitwarden.com","status":204},"timestamp":1767218107041} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-testb44f7181-a39d-4720-85e6-cdbed603f14b@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767218107047} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-testb44f7181-a39d-4720-85e6-cdbed603f14b@bitwarden.com","status":400},"timestamp":1767218107073} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-testb44f7181-a39d-4720-85e6-cdbed603f14b@bitwarden.com","actual":400,"expected":200},"timestamp":1767218107074} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test5bc32e3a-df8e-407b-9574-cbe0588aaa95@bitwarden.com","status":204},"timestamp":1767218247090} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test5bc32e3a-df8e-407b-9574-cbe0588aaa95@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767218247096} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"A","location":"RegisterFinishRequestModel.Validate","message":"validate_enter","data":{"hasUnlock":false,"hasAuth":false},"timestamp":1767218247119} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"A","location":"RegisterFinishRequestModel.Validate","message":"token_flags","data":{"hasEmailVerification":true,"hasOrgInvite":false,"hasOrgSponsoredFreeFamilyPlan":false,"hasEmergencyAccessInvite":false,"hasProviderInvite":false,"tokenCount":1},"timestamp":1767218247121} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"A","location":"RegisterFinishRequestModel.Validate","message":"token_type_resolved","data":{"tokenType":"EmailVerification"},"timestamp":1767218247122} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"C","location":"RegisterFinishRequestModel.Validate","message":"kdf_source_root_fields","data":{"Kdf":"PBKDF2_SHA256","KdfIterations":600000,"KdfMemory":null,"KdfParallelism":null},"timestamp":1767218247122} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"C","location":"RegisterFinishRequestModel.Validate","message":"kdf_validation_results","data":{"count":0,"errors":[]},"timestamp":1767218247123} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"D","location":"AccountsController.PostRegisterFinish","message":"register_finish_enter","data":{"hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokenFlags":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false}},"timestamp":1767218247125} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"D","location":"AccountsController.PostRegisterFinish","message":"register_finish_token_type","data":{"tokenType":"EmailVerification"},"timestamp":1767218247127} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"D","location":"AccountsController.PostRegisterFinish","message":"register_finish_kdf_preview","data":{"source":"root","kdf":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null},"timestamp":1767218247127} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"D","location":"AccountsController.PostRegisterFinish","message":"register_finish_process_result","data":{"succeeded":true,"errorCount":0},"timestamp":1767218247278} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test5bc32e3a-df8e-407b-9574-cbe0588aaa95@bitwarden.com","status":200},"timestamp":1767218247281} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test5bc32e3a-df8e-407b-9574-cbe0588aaa95@bitwarden.com","actual":200,"expected":200},"timestamp":1767218247282} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"send_verification_status","data":{"email":"integration-test23dce254-28d2-465f-9ec3-9321a1b9ac1b@bitwarden.com","status":204},"timestamp":1767218248131} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_request","data":{"email":"integration-test23dce254-28d2-465f-9ec3-9321a1b9ac1b@bitwarden.com","hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokens":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false},"kdf":{"type":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null}},"timestamp":1767218248131} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"A","location":"RegisterFinishRequestModel.Validate","message":"validate_enter","data":{"hasUnlock":false,"hasAuth":false},"timestamp":1767218248132} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"A","location":"RegisterFinishRequestModel.Validate","message":"token_flags","data":{"hasEmailVerification":true,"hasOrgInvite":false,"hasOrgSponsoredFreeFamilyPlan":false,"hasEmergencyAccessInvite":false,"hasProviderInvite":false,"tokenCount":1},"timestamp":1767218248132} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"A","location":"RegisterFinishRequestModel.Validate","message":"token_type_resolved","data":{"tokenType":"EmailVerification"},"timestamp":1767218248132} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"C","location":"RegisterFinishRequestModel.Validate","message":"kdf_source_root_fields","data":{"Kdf":"PBKDF2_SHA256","KdfIterations":600000,"KdfMemory":null,"KdfParallelism":null},"timestamp":1767218248132} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"C","location":"RegisterFinishRequestModel.Validate","message":"kdf_validation_results","data":{"count":0,"errors":[]},"timestamp":1767218248132} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"D","location":"AccountsController.PostRegisterFinish","message":"register_finish_enter","data":{"hasUnlock":false,"hasAuth":false,"hasSymKey":true,"rootKdfPresent":true,"tokenFlags":{"ev":true,"oi":false,"fam":false,"ea":false,"prov":false}},"timestamp":1767218248132} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"D","location":"AccountsController.PostRegisterFinish","message":"register_finish_token_type","data":{"tokenType":"EmailVerification"},"timestamp":1767218248132} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"D","location":"AccountsController.PostRegisterFinish","message":"register_finish_kdf_preview","data":{"source":"root","kdf":"PBKDF2_SHA256","iters":600000,"mem":null,"par":null},"timestamp":1767218248133} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"D","location":"AccountsController.PostRegisterFinish","message":"register_finish_process_result","data":{"succeeded":true,"errorCount":0},"timestamp":1767218248169} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"register_finish_status","data":{"email":"integration-test23dce254-28d2-465f-9ec3-9321a1b9ac1b@bitwarden.com","status":200},"timestamp":1767218248170} -{"sessionId":"debug-session","runId":"pre-fix","hypothesisId":"factory","location":"IdentityApplicationFactory","message":"assert_register_finish_status","data":{"email":"integration-test23dce254-28d2-465f-9ec3-9321a1b9ac1b@bitwarden.com","actual":200,"expected":200},"timestamp":1767218248170} From fc507a4c60411993d3fb1f3407c81c9ea5006dfb Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Wed, 31 Dec 2025 17:19:08 -0500 Subject: [PATCH 23/44] comment(register): [PM-27084] Account Register Uses New Data Types - Fixed error in register finish model validation. --- .../Accounts/RegisterFinishRequestModel.cs | 2 +- src/Core/Utilities/KdfSettingsValidator.cs | 27 ------------------- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index b6b5737e85c7..2738a3249647 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -199,7 +199,7 @@ public IEnumerable Validate(ValidationContext validationContex IEnumerable kdfValidationResults; if (MasterPasswordUnlock != null && MasterPasswordAuthentication != null) { - kdfValidationResults = KdfSettingsValidator.Validate(MasterPasswordUnlock?.ToData() ?? throw new InvalidOperationException("Error Here")); + kdfValidationResults = KdfSettingsValidator.Validate(MasterPasswordUnlock.ToData().Kdf); } else { diff --git a/src/Core/Utilities/KdfSettingsValidator.cs b/src/Core/Utilities/KdfSettingsValidator.cs index 07ce73907d3c..e5690ad469e7 100644 --- a/src/Core/Utilities/KdfSettingsValidator.cs +++ b/src/Core/Utilities/KdfSettingsValidator.cs @@ -37,33 +37,6 @@ public static IEnumerable Validate(KdfType kdfType, int kdfIte } } - public static IEnumerable Validate(MasterPasswordUnlockData masterPasswordUnlockData) - { - switch (masterPasswordUnlockData.Kdf.KdfType) - { - case KdfType.PBKDF2_SHA256: - if (!AuthConstants.PBKDF2_ITERATIONS.InsideRange(masterPasswordUnlockData.Kdf.Iterations)) - { - yield return new ValidationResult($"KDF iterations must be between {AuthConstants.PBKDF2_ITERATIONS.Min} and {AuthConstants.PBKDF2_ITERATIONS.Max}."); - } - break; - case KdfType.Argon2id: - if (!AuthConstants.ARGON2_ITERATIONS.InsideRange(masterPasswordUnlockData.Kdf.Iterations)) - { - yield return new ValidationResult($"Argon2 iterations must be between {AuthConstants.ARGON2_ITERATIONS.Min} and {AuthConstants.ARGON2_ITERATIONS.Max}."); - } - else if (!masterPasswordUnlockData.Kdf.Memory.HasValue || !AuthConstants.ARGON2_MEMORY.InsideRange(masterPasswordUnlockData.Kdf.Memory.Value)) - { - yield return new ValidationResult($"Argon2 memory must be between {AuthConstants.ARGON2_MEMORY.Min}mb and {AuthConstants.ARGON2_MEMORY.Max}mb."); - } - else if (!masterPasswordUnlockData.Kdf.Parallelism.HasValue || !AuthConstants.ARGON2_PARALLELISM.InsideRange(masterPasswordUnlockData.Kdf.Parallelism.Value)) - { - yield return new ValidationResult($"Argon2 parallelism must be between {AuthConstants.ARGON2_PARALLELISM.Min} and {AuthConstants.ARGON2_PARALLELISM.Max}."); - } - break; - } - } - public static IEnumerable Validate(KdfSettings settings) { return Validate(settings.KdfType, settings.Iterations, settings.Memory, settings.Parallelism); From 05d8cc5058bcc0da9dc1fd2349adff8c83a53bf9 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Wed, 31 Dec 2025 17:38:21 -0500 Subject: [PATCH 24/44] test(register): [PM-27084] Account Register Uses New Data Types - Fixed tests. --- .../Factories/IdentityApplicationFactory.cs | 42 ++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs index 3df80d267c1c..3f768c62bd4a 100644 --- a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs +++ b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Net.Http.Json; +using System.Text; using System.Text.Json; using Bit.Core; using Bit.Core.Auth.Models.Api.Request.Accounts; @@ -26,6 +27,7 @@ public class IdentityApplicationFactory : WebApplicationFactoryBase public const string DefaultDeviceIdentifier = "92b9d953-b9b6-4eaf-9d3e-11d57144dfeb"; public const string DefaultUserEmail = "DefaultEmail@bitwarden.com"; public const string DefaultUserPasswordHash = "default_password_hash"; + private const string DefaultEncryptedString = "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98sp4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg="; /// /// A dictionary to store registration tokens for email verification. We cannot substitute the IMailService more than once, so @@ -239,14 +241,13 @@ public async Task RegisterNewIdentityFactoryUserAsync( { var unlock = requestModel.MasterPasswordUnlock; // PM-28143 - Once UserSymmetricKey is removed and UnlockData is required, delete the fallback to UserSymmetricKey below. - var masterKeyWrappedUserKey = !string.IsNullOrWhiteSpace(unlock.MasterKeyWrappedUserKey) - ? unlock.MasterKeyWrappedUserKey - : (string.IsNullOrWhiteSpace(requestModel.UserSymmetricKey) ? "user_symmetric_key" : requestModel.UserSymmetricKey); + // Always force a valid encrypted string for tests to avoid model validation failures. + var masterKeyWrappedUserKey = DefaultEncryptedString; requestModel.MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel { Kdf = alignedKdf, MasterKeyWrappedUserKey = masterKeyWrappedUserKey, - Salt = unlock.Salt + Salt = string.IsNullOrWhiteSpace(unlock.Salt) ? requestModel.Email : unlock.Salt }; } @@ -279,7 +280,11 @@ public async Task RegisterNewIdentityFactoryUserAsync( requestModel.EmailVerificationToken = RegistrationTokens[requestModel.Email]; var postRegisterFinishHttpContext = await PostRegisterFinishAsync(requestModel); - Assert.Equal(StatusCodes.Status200OK, postRegisterFinishHttpContext.Response.StatusCode); + if (postRegisterFinishHttpContext.Response.StatusCode != StatusCodes.Status200OK) + { + var body = await ReadResponseBodyAsync(postRegisterFinishHttpContext); + Assert.Fail($"register/finish failed (status {postRegisterFinishHttpContext.Response.StatusCode}). Body: {body}"); + } var database = GetDatabaseContext(); var user = await database.Users @@ -290,4 +295,31 @@ public async Task RegisterNewIdentityFactoryUserAsync( return user; } + private static async Task ReadResponseBodyAsync(HttpContext ctx) + { + try + { + if (ctx?.Response?.Body == null) + { + return ""; + } + var stream = ctx.Response.Body; + if (stream.CanSeek) + { + stream.Seek(0, SeekOrigin.Begin); + } + using var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: false, leaveOpen: true); + var text = await reader.ReadToEndAsync(); + if (stream.CanSeek) + { + stream.Seek(0, SeekOrigin.Begin); + } + return string.IsNullOrWhiteSpace(text) ? "" : text; + } + catch (Exception ex) + { + return $""; + } + } + } From e531ab1aab1b31bedda2fbe593d6b69a9640e70d Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Wed, 31 Dec 2025 18:19:50 -0500 Subject: [PATCH 25/44] test(register): [PM-27084] Account Register Uses New Data Types - Fixed accounts controller tests. --- test/Api.IntegrationTest/Controllers/AccountsControllerTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Api.IntegrationTest/Controllers/AccountsControllerTest.cs b/test/Api.IntegrationTest/Controllers/AccountsControllerTest.cs index f0312bca780d..70e40817da8c 100644 --- a/test/Api.IntegrationTest/Controllers/AccountsControllerTest.cs +++ b/test/Api.IntegrationTest/Controllers/AccountsControllerTest.cs @@ -358,7 +358,7 @@ public async Task PostKdf_InvalidKdf_BadRequest(KdfType kdf, int kdfIterations, Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var content = await response.Content.ReadAsStringAsync(); - Assert.Contains("KDF settings are invalid", content); + Assert.Contains("The model state is invalid", content); } [Fact] From 28640d0963bca1b14a5a931d3b095183b2059c03 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Wed, 31 Dec 2025 21:34:24 -0500 Subject: [PATCH 26/44] fix(register): [PM-27084] Account Register Uses New Data Types - Addressed concerns from reviewer. --- .../Accounts/RegisterFinishRequestModel.cs | 33 ++----------------- .../{Data => Api/Request}/KdfRequestModel.cs | 3 +- ...sterPasswordUnlockAndAuthenticationData.cs | 9 +---- 3 files changed, 6 insertions(+), 39 deletions(-) rename src/Core/KeyManagement/Models/{Data => Api/Request}/KdfRequestModel.cs (89%) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 2738a3249647..30dc8a141add 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -69,9 +69,9 @@ public User ToUser() Email = Email, MasterPasswordHint = MasterPasswordHint, Kdf = MasterPasswordUnlock?.Kdf.KdfType ?? Kdf - ?? throw new BadRequestException("KdfType couldn't be found on either the MasterPasswordUnlockData or the Kdf property passed in."), + ?? throw new BadRequestException("KdfType couldn't be found on either the MasterPasswordUnlock or the Kdf property passed in."), KdfIterations = MasterPasswordUnlock?.Kdf.Iterations ?? KdfIterations - ?? throw new BadRequestException("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in."), + ?? throw new BadRequestException("KdfIterations couldn't be found on either the MasterPasswordUnlock or the KdfIterations property passed in."), // KdfMemory and KdfParallelism are optional (only used for Argon2id) KdfMemory = MasterPasswordUnlock?.Kdf.Memory ?? KdfMemory, KdfParallelism = MasterPasswordUnlock?.Kdf.Parallelism ?? KdfParallelism, @@ -123,33 +123,6 @@ public IEnumerable Validate(ValidationContext validationContex ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash); // 1. Access Token Presence Verification Check - - // Ensure exactly one registration token type is provided - var hasEmailVerification = !string.IsNullOrWhiteSpace(EmailVerificationToken); - var hasOrgInvite = !string.IsNullOrEmpty(OrgInviteToken) - && OrganizationUserId.HasValue - && OrganizationUserId.Value != Guid.Empty; - var hasOrgSponsoredFreeFamilyPlan = !string.IsNullOrWhiteSpace(OrgSponsoredFreeFamilyPlanToken); - var hasEmergencyAccessInvite = !string.IsNullOrWhiteSpace(AcceptEmergencyAccessInviteToken) - && AcceptEmergencyAccessId.HasValue - && AcceptEmergencyAccessId.Value != Guid.Empty; - var hasProviderInvite = !string.IsNullOrWhiteSpace(ProviderInviteToken) - && ProviderUserId.HasValue - && ProviderUserId.Value != Guid.Empty; - var tokenCount = (hasEmailVerification ? 1 : 0) - + (hasOrgInvite ? 1 : 0) - + (hasOrgSponsoredFreeFamilyPlan ? 1 : 0) - + (hasEmergencyAccessInvite ? 1 : 0) - + (hasProviderInvite ? 1 : 0); - if (tokenCount == 0) - { - throw new BadRequestException("Invalid registration finish request"); - } - if (tokenCount > 1) - { - throw new BadRequestException("Multiple registration token types provided."); - } - switch (GetTokenType()) { case RegisterFinishTokenType.EmailVerification: @@ -222,7 +195,7 @@ private static void ThrowIfExistsAndHashIsNotEqual( { if (authenticationData.MasterPasswordAuthenticationHash != hash) { - throw new BadRequestException("Master password hash and hash are not equal."); + throw new BadRequestException("AuthenticationData MasterPasswordHash and root level MasterPasswordHash provided and are not equal. Only provide one."); } } } diff --git a/src/Core/KeyManagement/Models/Data/KdfRequestModel.cs b/src/Core/KeyManagement/Models/Api/Request/KdfRequestModel.cs similarity index 89% rename from src/Core/KeyManagement/Models/Data/KdfRequestModel.cs rename to src/Core/KeyManagement/Models/Api/Request/KdfRequestModel.cs index ea84fa12d275..edcd7f760f3c 100644 --- a/src/Core/KeyManagement/Models/Data/KdfRequestModel.cs +++ b/src/Core/KeyManagement/Models/Api/Request/KdfRequestModel.cs @@ -1,8 +1,9 @@ using System.ComponentModel.DataAnnotations; using Bit.Core.Enums; +using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Utilities; -namespace Bit.Core.KeyManagement.Models.Data; +namespace Bit.Core.KeyManagement.Models.Api.Request; public class KdfRequestModel : IValidatableObject { diff --git a/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockAndAuthenticationData.cs b/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockAndAuthenticationData.cs index e60cb3bb952c..b79ce8bce10c 100644 --- a/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockAndAuthenticationData.cs +++ b/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockAndAuthenticationData.cs @@ -1,15 +1,8 @@ -#nullable enable -using Bit.Core.Entities; +using Bit.Core.Entities; using Bit.Core.Enums; namespace Bit.Core.KeyManagement.Models.Data; -/// -/// Probably shouldn't be used as we want to make sure that the unlock data and authentication data -/// can use separate kdf settings. -/// -/// Should be cleaned up in the near future. -/// public class MasterPasswordUnlockAndAuthenticationData { public KdfType KdfType { get; set; } From 260b289b640712f18693141cb9762b8f8581c218 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Fri, 2 Jan 2026 10:03:50 -0500 Subject: [PATCH 27/44] fix(register): [PM-27084] Account Register Uses New Data Types - Fixed up tests a little more. --- .../Controllers/AccountsController.cs | 23 ++++++++----------- .../Controllers/AccountsControllerTests.cs | 7 +++--- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/Identity/Controllers/AccountsController.cs b/src/Identity/Controllers/AccountsController.cs index aba344aeda1e..f80274d65dcc 100644 --- a/src/Identity/Controllers/AccountsController.cs +++ b/src/Identity/Controllers/AccountsController.cs @@ -1,7 +1,4 @@ -// FIXME: Update this file to be null safe and then delete the line below -#nullable disable - -using System.Text; +using System.Text; using Bit.Core; using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models.Api.Request.Accounts; @@ -41,7 +38,7 @@ public class AccountsController : Controller private readonly IFeatureService _featureService; private readonly IDataProtectorTokenFactory _registrationEmailVerificationTokenDataFactory; - private readonly byte[] _defaultKdfHmacKey = null; + private readonly byte[]? _defaultKdfHmacKey = null; private static readonly List _defaultKdfResults = [ // The first result (index 0) should always return the "normal" default. @@ -147,7 +144,7 @@ public async Task PostRegisterFinish([FromBody] Reg User user = model.ToUser(); // Users will either have an emailed token or an email verification token - not both. - IdentityResult identityResult = null; + IdentityResult? identityResult = null; // PM-28143 - Just use the MasterPasswordAuthenticationData.MasterPasswordAuthenticationHash string masterPasswordHash = model.MasterPasswordAuthentication?.MasterPasswordAuthenticationHash @@ -159,14 +156,14 @@ public async Task PostRegisterFinish([FromBody] Reg identityResult = await _registerUserCommand.RegisterUserViaEmailVerificationToken( user, masterPasswordHash, - model.EmailVerificationToken); + model.EmailVerificationToken!); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.OrganizationInvite: identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken( user, masterPasswordHash, - model.OrgInviteToken, + model.OrgInviteToken!, model.OrganizationUserId); return ProcessRegistrationResult(identityResult, user); @@ -174,23 +171,23 @@ public async Task PostRegisterFinish([FromBody] Reg identityResult = await _registerUserCommand.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken( user, masterPasswordHash, - model.OrgSponsoredFreeFamilyPlanToken); + model.OrgSponsoredFreeFamilyPlanToken!); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.EmergencyAccessInvite: identityResult = await _registerUserCommand.RegisterUserViaAcceptEmergencyAccessInviteToken( user, masterPasswordHash, - model.AcceptEmergencyAccessInviteToken, - (Guid)model.AcceptEmergencyAccessId); + model.AcceptEmergencyAccessInviteToken!, + (Guid)model.AcceptEmergencyAccessId!); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.ProviderInvite: identityResult = await _registerUserCommand.RegisterUserViaProviderInviteToken( user, masterPasswordHash, - model.ProviderInviteToken, - (Guid)model.ProviderUserId); + model.ProviderInviteToken!, + (Guid)model.ProviderUserId!); return ProcessRegistrationResult(identityResult, user); default: diff --git a/test/Identity.Test/Controllers/AccountsControllerTests.cs b/test/Identity.Test/Controllers/AccountsControllerTests.cs index 86d407256e0d..90ef917cf62b 100644 --- a/test/Identity.Test/Controllers/AccountsControllerTests.cs +++ b/test/Identity.Test/Controllers/AccountsControllerTests.cs @@ -11,7 +11,6 @@ using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.KeyManagement.Models.Api.Request; -using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Models.Data; using Bit.Core.Repositories; using Bit.Core.Services; @@ -963,7 +962,7 @@ public async Task PostRegisterFinish_WhenKdfMissingInAllSources_ShouldReturnBadR // Act & Assert var ex = await Assert.ThrowsAsync(() => _sut.PostRegisterFinish(model)); - Assert.Equal("KdfType couldn't be found on either the MasterPasswordUnlockData or the Kdf property passed in.", ex.Message); + Assert.Equal("KdfType couldn't be found on either the MasterPasswordUnlock or the Kdf property passed in.", ex.Message); } [Theory, BitAutoData] @@ -1001,7 +1000,7 @@ public async Task PostRegisterFinish_WhenKdfIterationsMissingInAllSources_Should // Act & Assert var ex = await Assert.ThrowsAsync(() => _sut.PostRegisterFinish(model)); - Assert.Equal("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in.", ex.Message); + Assert.Equal("KdfIterations couldn't be found on either the MasterPasswordUnlock or the KdfIterations property passed in.", ex.Message); } [Theory, BitAutoData] @@ -1192,7 +1191,7 @@ public void RegisterFinishRequestModel_Validate_Throws_WhenAuthHashAndRootHashMi // Act & Assert var ex = Assert.Throws(() => model.Validate(ctx).ToList()); - Assert.Equal("Master password hash and hash are not equal.", ex.Message); + Assert.Equal("AuthenticationData MasterPasswordHash and root level MasterPasswordHash provided and are not equal. Only provide one.", ex.Message); } private void SetDefaultKdfHmacKey(byte[]? newKey) From 06bf7b82cc5fb8617e30bcdf9bbb5e2398e7bcf0 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Fri, 2 Jan 2026 11:54:14 -0500 Subject: [PATCH 28/44] fix(register): [PM-27084] Account Register Uses New Data Types - Converted throws to validation results and updated tests. --- .../Accounts/RegisterFinishRequestModel.cs | 91 ++++++++++++------- .../Controllers/AccountsController.cs | 9 +- .../Controllers/AccountsControllerTests.cs | 15 ++- 3 files changed, 75 insertions(+), 40 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 30dc8a141add..430ef0430783 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -119,83 +119,110 @@ public RegisterFinishTokenType GetTokenType() public IEnumerable Validate(ValidationContext validationContext) { - // PM-28143 - Remove this check - ThrowIfExistsAndHashIsNotEqual(MasterPasswordAuthentication, MasterPasswordHash); + // 1. Authentication data containing hash and hash at root level check + if (MasterPasswordAuthentication != null && MasterPasswordHash != null) + { + if (MasterPasswordAuthentication.MasterPasswordAuthenticationHash != MasterPasswordHash) + { + yield return new ValidationResult( + $"{nameof(MasterPasswordAuthentication.MasterPasswordAuthenticationHash)} and root level {nameof(MasterPasswordHash)} provided and are not equal. Only provide one.", + [nameof(MasterPasswordAuthentication.MasterPasswordAuthenticationHash), nameof(MasterPasswordHash)]); + } + } + - // 1. Access Token Presence Verification Check + // 1. Access token presence verification check switch (GetTokenType()) { case RegisterFinishTokenType.EmailVerification: if (string.IsNullOrEmpty(EmailVerificationToken)) { - throw new BadRequestException("Email verification token absent when processing register/finish."); + yield return new ValidationResult( + $"{nameof(EmailVerificationToken)} absent when processing register/finish.", + [nameof(EmailVerificationToken)]); } break; case RegisterFinishTokenType.OrganizationInvite: if (string.IsNullOrEmpty(OrgInviteToken)) { - throw new BadRequestException("Organization invite token absent when processing register/finish."); + yield return new ValidationResult( + $"{nameof(OrgInviteToken)} absent when processing register/finish.", + [nameof(OrgInviteToken)]); } break; case RegisterFinishTokenType.OrgSponsoredFreeFamilyPlan: if (string.IsNullOrEmpty(OrgSponsoredFreeFamilyPlanToken)) { - throw new BadRequestException("Organization sponsored free family plan token absent when processing register/finish."); + yield return new ValidationResult( + $"{nameof(OrgSponsoredFreeFamilyPlanToken)} absent when processing register/finish.", + [nameof(OrgSponsoredFreeFamilyPlanToken)]); } break; case RegisterFinishTokenType.EmergencyAccessInvite: if (string.IsNullOrEmpty(AcceptEmergencyAccessInviteToken)) { - throw new BadRequestException("Accept emergency access invite token absent when processing register/finish."); + yield return new ValidationResult( + $"{nameof(AcceptEmergencyAccessInviteToken)} absent when processing register/finish.", + [nameof(AcceptEmergencyAccessInviteToken)]); } if (!AcceptEmergencyAccessId.HasValue || AcceptEmergencyAccessId.Value == Guid.Empty) { - throw new BadRequestException("Accept emergency access id absent when processing register/finish."); + yield return new ValidationResult( + $"{nameof(AcceptEmergencyAccessId)} absent when processing register/finish.", + [nameof(AcceptEmergencyAccessId)]); } break; case RegisterFinishTokenType.ProviderInvite: if (string.IsNullOrEmpty(ProviderInviteToken)) { - throw new BadRequestException("Provider invite token absent when processing register/finish."); + yield return new ValidationResult( + $"{nameof(ProviderInviteToken)} absent when processing register/finish.", + [nameof(ProviderInviteToken)]); } if (!ProviderUserId.HasValue || ProviderUserId.Value == Guid.Empty) { - throw new BadRequestException("Provider user id absent when processing register/finish."); + yield return new ValidationResult( + $"{nameof(ProviderUserId)} absent when processing register/finish.", + [nameof(ProviderUserId)]); } break; default: - throw new BadRequestException("Invalid registration finish request"); + yield return new ValidationResult("Invalid registration finish request"); + break; } // 2. Validate kdf settings. - - IEnumerable kdfValidationResults; if (MasterPasswordUnlock != null && MasterPasswordAuthentication != null) { - kdfValidationResults = KdfSettingsValidator.Validate(MasterPasswordUnlock.ToData().Kdf); + foreach (var validationResult in KdfSettingsValidator.Validate(MasterPasswordUnlock.ToData().Kdf)) + { + yield return validationResult; + } } else { - kdfValidationResults = KdfSettingsValidator.Validate( - Kdf ?? throw new BadRequestException($"{nameof(Kdf)} not found on RequestModel"), - KdfIterations ?? throw new BadRequestException($"{nameof(KdfIterations)} not found on RequestModel"), - KdfMemory, - KdfParallelism); - } - - return kdfValidationResults; - } + var hasMissingRequiredKdfInputs = false; + if (Kdf == null) + { + yield return new ValidationResult($"{nameof(Kdf)} not found on RequestModel", [nameof(Kdf)]); + hasMissingRequiredKdfInputs = true; + } + if (KdfIterations == null) + { + yield return new ValidationResult($"{nameof(KdfIterations)} not found on RequestModel", [nameof(KdfIterations)]); + hasMissingRequiredKdfInputs = true; + } - // PM-28143 - Remove function - private static void ThrowIfExistsAndHashIsNotEqual( - MasterPasswordAuthenticationDataRequestModel? authenticationData, - string? hash) - { - if (authenticationData != null && hash != null) - { - if (authenticationData.MasterPasswordAuthenticationHash != hash) + if (!hasMissingRequiredKdfInputs) { - throw new BadRequestException("AuthenticationData MasterPasswordHash and root level MasterPasswordHash provided and are not equal. Only provide one."); + foreach (var validationResult in KdfSettingsValidator.Validate( + Kdf!.Value, + KdfIterations!.Value, + KdfMemory, + KdfParallelism)) + { + yield return validationResult; + } } } } diff --git a/src/Identity/Controllers/AccountsController.cs b/src/Identity/Controllers/AccountsController.cs index f80274d65dcc..2e42b690dd07 100644 --- a/src/Identity/Controllers/AccountsController.cs +++ b/src/Identity/Controllers/AccountsController.cs @@ -202,11 +202,10 @@ private RegisterFinishResponseModel ProcessRegistrationResult(IdentityResult res return new RegisterFinishResponseModel(); } - if (result.Errors != null) - foreach (var error in result.Errors.Where(e => e.Code != "DuplicateUserName")) - { - ModelState.AddModelError(string.Empty, error.Description); - } + foreach (var error in result.Errors.Where(e => e.Code != "DuplicateUserName")) + { + ModelState.AddModelError(string.Empty, error.Description); + } throw new BadRequestException(ModelState); } diff --git a/test/Identity.Test/Controllers/AccountsControllerTests.cs b/test/Identity.Test/Controllers/AccountsControllerTests.cs index 90ef917cf62b..6a619842d11f 100644 --- a/test/Identity.Test/Controllers/AccountsControllerTests.cs +++ b/test/Identity.Test/Controllers/AccountsControllerTests.cs @@ -1187,11 +1187,20 @@ public void RegisterFinishRequestModel_Validate_Throws_WhenAuthHashAndRootHashMi } }; + // Provide a minimal valid token type to satisfy model-level token validation + model.EmailVerificationToken = "test-token"; + var ctx = new ValidationContext(model); - // Act & Assert - var ex = Assert.Throws(() => model.Validate(ctx).ToList()); - Assert.Equal("AuthenticationData MasterPasswordHash and root level MasterPasswordHash provided and are not equal. Only provide one.", ex.Message); + // Act + var results = model.Validate(ctx).ToList(); + + // Assert: validation result exists with expected message and member names + var mismatchResult = Assert.Single(results.Where(r => + r.ErrorMessage == + "MasterPasswordAuthenticationHash and root level MasterPasswordHash provided and are not equal. Only provide one.")); + Assert.Contains("MasterPasswordAuthenticationHash", mismatchResult.MemberNames); + Assert.Contains("MasterPasswordHash", mismatchResult.MemberNames); } private void SetDefaultKdfHmacKey(byte[]? newKey) From c255e397c0c1bc8d501fddc1d27b8005a449b54b Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Fri, 2 Jan 2026 12:21:04 -0500 Subject: [PATCH 29/44] fix(register): [PM-27084] Account Register Uses New Data Types - Added more validation around the master password kdf. --- .../Accounts/RegisterFinishRequestModel.cs | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 430ef0430783..720306a1d110 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -192,14 +192,23 @@ public IEnumerable Validate(ValidationContext validationContex } // 2. Validate kdf settings. - if (MasterPasswordUnlock != null && MasterPasswordAuthentication != null) + if (MasterPasswordUnlock != null) { foreach (var validationResult in KdfSettingsValidator.Validate(MasterPasswordUnlock.ToData().Kdf)) { yield return validationResult; } } - else + + if (MasterPasswordAuthentication != null) + { + foreach (var validationResult in KdfSettingsValidator.Validate(MasterPasswordAuthentication.ToData().Kdf)) + { + yield return validationResult; + } + } + + if (MasterPasswordUnlock == null && MasterPasswordAuthentication == null) { var hasMissingRequiredKdfInputs = false; if (Kdf == null) @@ -225,5 +234,15 @@ public IEnumerable Validate(ValidationContext validationContex } } } + else if (MasterPasswordUnlock == null && MasterPasswordAuthentication != null) + { + // Authentication provided but Unlock missing + yield return new ValidationResult($"{nameof(MasterPasswordUnlock)} not found on RequestModel", [nameof(MasterPasswordUnlock)]); + } + else if (MasterPasswordUnlock != null && MasterPasswordAuthentication == null) + { + // Unlock provided but Authentication missing + yield return new ValidationResult($"{nameof(MasterPasswordAuthentication)} not found on RequestModel", [nameof(MasterPasswordAuthentication)]); + } } } From 3aa0e4c7028edd48475ca7e040c0562601aeb3f4 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Fri, 2 Jan 2026 12:46:18 -0500 Subject: [PATCH 30/44] fix(register): [PM-27084] Account Register Uses New Data Types - Shuffled around validation a little. In a great place now. --- .../Accounts/RegisterFinishRequestModel.cs | 130 ++++++++++-------- 1 file changed, 74 insertions(+), 56 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 720306a1d110..f3f9a26adba1 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -130,9 +130,81 @@ public IEnumerable Validate(ValidationContext validationContex } } + // 2. Validate kdf settings. + if (MasterPasswordUnlock != null) + { + foreach (var validationResult in KdfSettingsValidator.Validate(MasterPasswordUnlock.ToData().Kdf)) + { + yield return validationResult; + } + } + + if (MasterPasswordAuthentication != null) + { + foreach (var validationResult in KdfSettingsValidator.Validate(MasterPasswordAuthentication.ToData().Kdf)) + { + yield return validationResult; + } + } + + // 3. Validate root kdf values if kdf values are not in the unlock and authentication. + if (MasterPasswordUnlock == null && MasterPasswordAuthentication == null) + { + var hasMissingRequiredKdfInputs = false; + if (Kdf == null) + { + yield return new ValidationResult($"{nameof(Kdf)} not found on RequestModel", [nameof(Kdf)]); + hasMissingRequiredKdfInputs = true; + } + if (KdfIterations == null) + { + yield return new ValidationResult($"{nameof(KdfIterations)} not found on RequestModel", [nameof(KdfIterations)]); + hasMissingRequiredKdfInputs = true; + } + + if (!hasMissingRequiredKdfInputs) + { + foreach (var validationResult in KdfSettingsValidator.Validate( + Kdf!.Value, + KdfIterations!.Value, + KdfMemory, + KdfParallelism)) + { + yield return validationResult; + } + } + } + else if (MasterPasswordUnlock == null && MasterPasswordAuthentication != null) + { + // Authentication provided but Unlock missing + yield return new ValidationResult($"{nameof(MasterPasswordUnlock)} not found on RequestModel", [nameof(MasterPasswordUnlock)]); + } + else if (MasterPasswordUnlock != null && MasterPasswordAuthentication == null) + { + // Unlock provided but Authentication missing + yield return new ValidationResult($"{nameof(MasterPasswordAuthentication)} not found on RequestModel", [nameof(MasterPasswordAuthentication)]); + } + + // 3. Lastly, validate access token type and presence. Must be done last because of yield break. + RegisterFinishTokenType tokenType; + var tokenTypeResolved = true; + try + { + tokenType = GetTokenType(); + } + catch (InvalidOperationException) + { + tokenTypeResolved = false; + tokenType = default; + } - // 1. Access token presence verification check - switch (GetTokenType()) + if (!tokenTypeResolved) + { + yield return new ValidationResult("No valid registration token provided"); + yield break; + } + + switch (tokenType) { case RegisterFinishTokenType.EmailVerification: if (string.IsNullOrEmpty(EmailVerificationToken)) @@ -190,59 +262,5 @@ public IEnumerable Validate(ValidationContext validationContex yield return new ValidationResult("Invalid registration finish request"); break; } - - // 2. Validate kdf settings. - if (MasterPasswordUnlock != null) - { - foreach (var validationResult in KdfSettingsValidator.Validate(MasterPasswordUnlock.ToData().Kdf)) - { - yield return validationResult; - } - } - - if (MasterPasswordAuthentication != null) - { - foreach (var validationResult in KdfSettingsValidator.Validate(MasterPasswordAuthentication.ToData().Kdf)) - { - yield return validationResult; - } - } - - if (MasterPasswordUnlock == null && MasterPasswordAuthentication == null) - { - var hasMissingRequiredKdfInputs = false; - if (Kdf == null) - { - yield return new ValidationResult($"{nameof(Kdf)} not found on RequestModel", [nameof(Kdf)]); - hasMissingRequiredKdfInputs = true; - } - if (KdfIterations == null) - { - yield return new ValidationResult($"{nameof(KdfIterations)} not found on RequestModel", [nameof(KdfIterations)]); - hasMissingRequiredKdfInputs = true; - } - - if (!hasMissingRequiredKdfInputs) - { - foreach (var validationResult in KdfSettingsValidator.Validate( - Kdf!.Value, - KdfIterations!.Value, - KdfMemory, - KdfParallelism)) - { - yield return validationResult; - } - } - } - else if (MasterPasswordUnlock == null && MasterPasswordAuthentication != null) - { - // Authentication provided but Unlock missing - yield return new ValidationResult($"{nameof(MasterPasswordUnlock)} not found on RequestModel", [nameof(MasterPasswordUnlock)]); - } - else if (MasterPasswordUnlock != null && MasterPasswordAuthentication == null) - { - // Unlock provided but Authentication missing - yield return new ValidationResult($"{nameof(MasterPasswordAuthentication)} not found on RequestModel", [nameof(MasterPasswordAuthentication)]); - } } } From 2111df73ba696226d6339a87c970ab2b385ec58b Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Fri, 2 Jan 2026 12:48:38 -0500 Subject: [PATCH 31/44] fix(register): [PM-27084] Account Register Uses New Data Types - Removed unused import. --- .../Factories/IdentityApplicationFactory.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs index 3f768c62bd4a..266bdba82390 100644 --- a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs +++ b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs @@ -10,7 +10,6 @@ using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.KeyManagement.Models.Api.Request; -using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Services; using Bit.Identity; using Bit.Test.Common.Helpers; From 65c0ac9fd4192d8e59958630e947a7a91875f007 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Fri, 2 Jan 2026 13:10:46 -0500 Subject: [PATCH 32/44] fix(register): [PM-27084] Account Register Uses New Data Types - Removed unused import. --- test/Api.IntegrationTest/Controllers/AccountsControllerTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/Api.IntegrationTest/Controllers/AccountsControllerTest.cs b/test/Api.IntegrationTest/Controllers/AccountsControllerTest.cs index 70e40817da8c..4e19c64326ee 100644 --- a/test/Api.IntegrationTest/Controllers/AccountsControllerTest.cs +++ b/test/Api.IntegrationTest/Controllers/AccountsControllerTest.cs @@ -7,7 +7,6 @@ using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.KeyManagement.Models.Api.Request; -using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Platform.Push; using Bit.Core.Repositories; using Bit.Core.Services; From 9e43ca24422f5e9e34d85b6d05f04228aa57bd95 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Fri, 9 Jan 2026 09:27:12 -0500 Subject: [PATCH 33/44] test(register): [PM-27084] Account Register Uses New Data Types - Added validation tests and ToUser no longer throws bad request. --- .../Accounts/RegisterFinishRequestModel.cs | 9 +- .../RegisterFinishRequestModelTests.cs | 177 ++++++++++++++++++ .../Controllers/AccountsControllerTests.cs | 113 ----------- 3 files changed, 180 insertions(+), 119 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index f3f9a26adba1..418efe5997f9 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -1,6 +1,5 @@ using Bit.Core.Entities; using Bit.Core.Enums; -using Bit.Core.Exceptions; using Bit.Core.KeyManagement.Models.Api.Request; using Bit.Core.Utilities; @@ -68,16 +67,14 @@ public User ToUser() { Email = Email, MasterPasswordHint = MasterPasswordHint, - Kdf = MasterPasswordUnlock?.Kdf.KdfType ?? Kdf - ?? throw new BadRequestException("KdfType couldn't be found on either the MasterPasswordUnlock or the Kdf property passed in."), - KdfIterations = MasterPasswordUnlock?.Kdf.Iterations ?? KdfIterations - ?? throw new BadRequestException("KdfIterations couldn't be found on either the MasterPasswordUnlock or the KdfIterations property passed in."), + Kdf = (KdfType)(MasterPasswordUnlock?.Kdf.KdfType ?? Kdf)!, + KdfIterations = (int)(MasterPasswordUnlock?.Kdf.Iterations ?? KdfIterations)!, // KdfMemory and KdfParallelism are optional (only used for Argon2id) KdfMemory = MasterPasswordUnlock?.Kdf.Memory ?? KdfMemory, KdfParallelism = MasterPasswordUnlock?.Kdf.Parallelism ?? KdfParallelism, // PM-28827 To be added when MasterPasswordSalt is added to the user column // MasterPasswordSalt = MasterPasswordUnlock?.Salt ?? Email.ToLower().Trim(), - Key = MasterPasswordUnlock?.MasterKeyWrappedUserKey ?? UserSymmetricKey ?? throw new BadRequestException("MasterKeyWrappedUserKey couldn't be found on either the MasterPasswordUnlockData or the UserSymmetricKey property passed in."), + Key = MasterPasswordUnlock?.MasterKeyWrappedUserKey ?? UserSymmetricKey }; UserAsymmetricKeys.ToUser(user); diff --git a/test/Core.Test/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModelTests.cs b/test/Core.Test/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModelTests.cs index 588ca878fc0c..16ba8dbf161e 100644 --- a/test/Core.Test/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModelTests.cs +++ b/test/Core.Test/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModelTests.cs @@ -1,5 +1,6 @@ using Bit.Core.Auth.Models.Api.Request.Accounts; using Bit.Core.Enums; +using Bit.Core.KeyManagement.Models.Api.Request; using Bit.Test.Common.AutoFixture.Attributes; using Xunit; @@ -7,6 +8,17 @@ namespace Bit.Core.Test.Auth.Models.Api.Request.Accounts; public class RegisterFinishRequestModelTests { + private static List Validate(RegisterFinishRequestModel model) + { + var results = new List(); + System.ComponentModel.DataAnnotations.Validator.TryValidateObject( + model, + new System.ComponentModel.DataAnnotations.ValidationContext(model), + results, + true); + return results; + } + [Theory] [BitAutoData] public void GetTokenType_Returns_EmailVerification(string email, string masterPasswordHash, @@ -170,4 +182,169 @@ public void ToUser_Returns_User(string email, string masterPasswordHash, string Assert.Equal(userAsymmetricKeys.PublicKey, result.PublicKey); Assert.Equal(userAsymmetricKeys.EncryptedPrivateKey, result.PrivateKey); } + + [Fact] + public void Validate_WhenBothAuthAndRootHashProvidedButNotEqual_ReturnsMismatchError() + { + var model = new RegisterFinishRequestModel + { + Email = "user@example.com", + MasterPasswordHash = "root-hash", + UserAsymmetricKeys = new KeysRequestModel { PublicKey = "pk", EncryptedPrivateKey = "sk" }, + // Provide both unlock and authentication with valid KDF so only the mismatch rule fires + MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel + { + Kdf = new KdfRequestModel { KdfType = KdfType.PBKDF2_SHA256, Iterations = AuthConstants.PBKDF2_ITERATIONS.Default }, + MasterKeyWrappedUserKey = "wrapped", + Salt = "salt" + }, + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel + { + Kdf = new KdfRequestModel { KdfType = KdfType.PBKDF2_SHA256, Iterations = AuthConstants.PBKDF2_ITERATIONS.Default }, + MasterPasswordAuthenticationHash = "auth-hash", // different than root + Salt = "salt" + }, + // Provide any valid token so we don't fail token validation + EmailVerificationToken = "token" + }; + + var results = Validate(model); + + Assert.Contains(results, r => + r.ErrorMessage == $"{nameof(MasterPasswordAuthenticationDataRequestModel.MasterPasswordAuthenticationHash)} and root level {nameof(RegisterFinishRequestModel.MasterPasswordHash)} provided and are not equal. Only provide one."); + } + + [Fact] + public void Validate_WhenAuthProvidedButUnlockMissing_ReturnsUnlockMissingError() + { + var model = new RegisterFinishRequestModel + { + Email = "user@example.com", + UserAsymmetricKeys = new KeysRequestModel { PublicKey = "pk", EncryptedPrivateKey = "sk" }, + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel + { + Kdf = new KdfRequestModel { KdfType = KdfType.PBKDF2_SHA256, Iterations = AuthConstants.PBKDF2_ITERATIONS.Default }, + MasterPasswordAuthenticationHash = "auth-hash", + Salt = "salt" + }, + EmailVerificationToken = "token" + }; + + var results = Validate(model); + + Assert.Contains(results, r => r.ErrorMessage == "MasterPasswordUnlock not found on RequestModel"); + } + + [Fact] + public void Validate_WhenUnlockProvidedButAuthMissing_ReturnsAuthMissingError() + { + var model = new RegisterFinishRequestModel + { + Email = "user@example.com", + UserAsymmetricKeys = new KeysRequestModel { PublicKey = "pk", EncryptedPrivateKey = "sk" }, + MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel + { + Kdf = new KdfRequestModel { KdfType = KdfType.PBKDF2_SHA256, Iterations = AuthConstants.PBKDF2_ITERATIONS.Default }, + MasterKeyWrappedUserKey = "wrapped", + Salt = "salt" + }, + EmailVerificationToken = "token" + }; + + var results = Validate(model); + + Assert.Contains(results, r => r.ErrorMessage == "MasterPasswordAuthentication not found on RequestModel"); + } + + [Fact] + public void Validate_WhenNeitherAuthNorUnlock_AndRootKdfMissing_ReturnsBothRootKdfErrors() + { + var model = new RegisterFinishRequestModel + { + Email = "user@example.com", + UserAsymmetricKeys = new KeysRequestModel { PublicKey = "pk", EncryptedPrivateKey = "sk" }, + // No MasterPasswordUnlock, no MasterPasswordAuthentication + // No root Kdf and KdfIterations to trigger both errors + EmailVerificationToken = "token" + }; + + var results = Validate(model); + + Assert.Contains(results, r => r.ErrorMessage == $"{nameof(RegisterFinishRequestModel.Kdf)} not found on RequestModel"); + Assert.Contains(results, r => r.ErrorMessage == $"{nameof(RegisterFinishRequestModel.KdfIterations)} not found on RequestModel"); + } + + [Fact] + public void Validate_WhenNeitherAuthNorUnlock_AndValidRootKdf_IsValid() + { + var model = new RegisterFinishRequestModel + { + Email = "user@example.com", + UserAsymmetricKeys = new KeysRequestModel { PublicKey = "pk", EncryptedPrivateKey = "sk" }, + Kdf = KdfType.PBKDF2_SHA256, + KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default, + // Memory and Parallelism irrelevant for PBKDF2 + EmailVerificationToken = "token" + }; + + var results = Validate(model); + + Assert.DoesNotContain(results, r => r.ErrorMessage?.Contains("Kdf") == true); + Assert.Empty(results.Where(r => r.ErrorMessage == "No valid registration token provided")); + } + + [Fact] + public void Validate_WhenAllFieldsValidWithSubModels_IsValid() + { + var model = new RegisterFinishRequestModel + { + Email = "user@example.com", + UserAsymmetricKeys = new KeysRequestModel { PublicKey = "pk", EncryptedPrivateKey = "sk" }, + MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel + { + Kdf = new KdfRequestModel { KdfType = KdfType.PBKDF2_SHA256, Iterations = AuthConstants.PBKDF2_ITERATIONS.Default }, + MasterKeyWrappedUserKey = "wrapped", + Salt = "salt" + }, + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel + { + Kdf = new KdfRequestModel { KdfType = KdfType.PBKDF2_SHA256, Iterations = AuthConstants.PBKDF2_ITERATIONS.Default }, + MasterPasswordAuthenticationHash = "auth-hash", + Salt = "salt" + }, + EmailVerificationToken = "token" + }; + + var results = Validate(model); + + Assert.Empty(results); + } + + [Fact] + public void Validate_WhenNoValidRegistrationTokenProvided_ReturnsTokenErrorOnly() + { + var model = new RegisterFinishRequestModel + { + Email = "user@example.com", + UserAsymmetricKeys = new KeysRequestModel { PublicKey = "pk", EncryptedPrivateKey = "sk" }, + MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel + { + Kdf = new KdfRequestModel { KdfType = KdfType.PBKDF2_SHA256, Iterations = AuthConstants.PBKDF2_ITERATIONS.Default }, + MasterKeyWrappedUserKey = "wrapped", + Salt = "salt" + }, + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel + { + Kdf = new KdfRequestModel { KdfType = KdfType.PBKDF2_SHA256, Iterations = AuthConstants.PBKDF2_ITERATIONS.Default }, + MasterPasswordAuthenticationHash = "auth-hash", + Salt = "salt" + } + // No token fields set + }; + + var results = Validate(model); + + Assert.Single(results); + Assert.Equal("No valid registration token provided", results[0].ErrorMessage); + } } diff --git a/test/Identity.Test/Controllers/AccountsControllerTests.cs b/test/Identity.Test/Controllers/AccountsControllerTests.cs index 6a619842d11f..86e461d1551c 100644 --- a/test/Identity.Test/Controllers/AccountsControllerTests.cs +++ b/test/Identity.Test/Controllers/AccountsControllerTests.cs @@ -927,119 +927,6 @@ await _registerUserCommand.Received(1).RegisterUserViaEmailVerificationToken( emailVerificationToken); } - [Theory, BitAutoData] - public async Task PostRegisterFinish_WhenKdfMissingInAllSources_ShouldReturnBadRequest( - string email, - string emailVerificationToken, - string masterPasswordHash, - string masterKeyWrappedUserKey, - int iterations, - string publicKey, - string encryptedPrivateKey) - { - // Arrange: No KDF at root, and no unlock-data present - var model = new RegisterFinishRequestModel - { - Email = email, - EmailVerificationToken = emailVerificationToken, - MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel - { - // present but ToUser does not source KDF from here - Kdf = new KdfRequestModel { KdfType = KdfType.Argon2id, Iterations = iterations }, - MasterPasswordAuthenticationHash = masterPasswordHash, - Salt = email - }, - MasterPasswordUnlock = null, - Kdf = null, - KdfIterations = iterations, - UserSymmetricKey = masterKeyWrappedUserKey, - UserAsymmetricKeys = new KeysRequestModel - { - PublicKey = publicKey, - EncryptedPrivateKey = encryptedPrivateKey - } - }; - - // Act & Assert - var ex = await Assert.ThrowsAsync(() => _sut.PostRegisterFinish(model)); - Assert.Equal("KdfType couldn't be found on either the MasterPasswordUnlock or the Kdf property passed in.", ex.Message); - } - - [Theory, BitAutoData] - public async Task PostRegisterFinish_WhenKdfIterationsMissingInAllSources_ShouldReturnBadRequest( - string email, - string emailVerificationToken, - string masterPasswordHash, - string masterKeyWrappedUserKey, - KdfType kdfType, - string publicKey, - string encryptedPrivateKey) - { - // Arrange: No KdfIterations at root, and no unlock-data present - var model = new RegisterFinishRequestModel - { - Email = email, - EmailVerificationToken = emailVerificationToken, - MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel - { - // present but ToUser does not source iterations from here - Kdf = new KdfRequestModel { KdfType = kdfType, Iterations = AuthConstants.PBKDF2_ITERATIONS.Default }, - MasterPasswordAuthenticationHash = masterPasswordHash, - Salt = email - }, - MasterPasswordUnlock = null, - Kdf = kdfType, - KdfIterations = null, - UserSymmetricKey = masterKeyWrappedUserKey, - UserAsymmetricKeys = new KeysRequestModel - { - PublicKey = publicKey, - EncryptedPrivateKey = encryptedPrivateKey - } - }; - - // Act & Assert - var ex = await Assert.ThrowsAsync(() => _sut.PostRegisterFinish(model)); - Assert.Equal("KdfIterations couldn't be found on either the MasterPasswordUnlock or the KdfIterations property passed in.", ex.Message); - } - - [Theory, BitAutoData] - public async Task PostRegisterFinish_WhenKeyMissingInAllSources_ShouldReturnBadRequest( - string email, - string emailVerificationToken, - string masterPasswordHash, - int iterations, - KdfType kdfType, - string publicKey, - string encryptedPrivateKey) - { - // Arrange: No key at root, and no unlock-data present - var model = new RegisterFinishRequestModel - { - Email = email, - EmailVerificationToken = emailVerificationToken, - MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel - { - Kdf = new KdfRequestModel { KdfType = kdfType, Iterations = iterations }, - MasterPasswordAuthenticationHash = masterPasswordHash, - Salt = email - }, - MasterPasswordUnlock = null, - Kdf = kdfType, - KdfIterations = iterations, - UserSymmetricKey = null, - UserAsymmetricKeys = new KeysRequestModel - { - PublicKey = publicKey, - EncryptedPrivateKey = encryptedPrivateKey - } - }; - - // Act & Assert - var ex = await Assert.ThrowsAsync(() => _sut.PostRegisterFinish(model)); - Assert.Equal("MasterKeyWrappedUserKey couldn't be found on either the MasterPasswordUnlockData or the UserSymmetricKey property passed in.", ex.Message); - } - [Theory, BitAutoData] public void RegisterFinishRequestModel_Validate_Throws_WhenUnlockAndAuthDataMismatch( string email, From 93c9631a75d9844a18af023eff4aa4bceee745f4 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Mon, 12 Jan 2026 15:11:58 -0500 Subject: [PATCH 34/44] test(register): [PM-27084] Account Register Uses New Data Types - Addressed feedback and added tests. --- .../Accounts/RegisterFinishRequestModel.cs | 6 ++++++ src/Identity/Controllers/AccountsController.cs | 14 +++++++------- .../Accounts/RegisterFinishRequestModelTests.cs | 16 +++++++++++----- .../Factories/IdentityApplicationFactory.cs | 1 - 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index 418efe5997f9..cb66540a6bb4 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -125,6 +125,12 @@ public IEnumerable Validate(ValidationContext validationContex $"{nameof(MasterPasswordAuthentication.MasterPasswordAuthenticationHash)} and root level {nameof(MasterPasswordHash)} provided and are not equal. Only provide one.", [nameof(MasterPasswordAuthentication.MasterPasswordAuthenticationHash), nameof(MasterPasswordHash)]); } + } // 1.5 if there is no master password hash that is unacceptable even though they are both optional in the model + else if (MasterPasswordAuthentication == null && MasterPasswordHash == null) + { + yield return new ValidationResult( + $"{nameof(MasterPasswordAuthentication.MasterPasswordAuthenticationHash)} and {nameof(MasterPasswordHash)} not found on request, one needs to be defined.", + [nameof(MasterPasswordAuthentication.MasterPasswordAuthenticationHash), nameof(MasterPasswordHash)]); } // 2. Validate kdf settings. diff --git a/src/Identity/Controllers/AccountsController.cs b/src/Identity/Controllers/AccountsController.cs index 2e42b690dd07..e9807fb1fc4f 100644 --- a/src/Identity/Controllers/AccountsController.cs +++ b/src/Identity/Controllers/AccountsController.cs @@ -147,22 +147,22 @@ public async Task PostRegisterFinish([FromBody] Reg IdentityResult? identityResult = null; // PM-28143 - Just use the MasterPasswordAuthenticationData.MasterPasswordAuthenticationHash - string masterPasswordHash = model.MasterPasswordAuthentication?.MasterPasswordAuthenticationHash - ?? model.MasterPasswordHash ?? throw new BadRequestException("MasterPasswordHash couldn't be found on either the MasterPasswordAuthenticationData or the MasterPasswordHash property passed in."); + string masterPasswordAuthenticationHash = model.MasterPasswordAuthentication?.MasterPasswordAuthenticationHash + ?? model.MasterPasswordHash!; switch (model.GetTokenType()) { case RegisterFinishTokenType.EmailVerification: identityResult = await _registerUserCommand.RegisterUserViaEmailVerificationToken( user, - masterPasswordHash, + masterPasswordAuthenticationHash, model.EmailVerificationToken!); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.OrganizationInvite: identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken( user, - masterPasswordHash, + masterPasswordAuthenticationHash, model.OrgInviteToken!, model.OrganizationUserId); return ProcessRegistrationResult(identityResult, user); @@ -170,14 +170,14 @@ public async Task PostRegisterFinish([FromBody] Reg case RegisterFinishTokenType.OrgSponsoredFreeFamilyPlan: identityResult = await _registerUserCommand.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken( user, - masterPasswordHash, + masterPasswordAuthenticationHash, model.OrgSponsoredFreeFamilyPlanToken!); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.EmergencyAccessInvite: identityResult = await _registerUserCommand.RegisterUserViaAcceptEmergencyAccessInviteToken( user, - masterPasswordHash, + masterPasswordAuthenticationHash, model.AcceptEmergencyAccessInviteToken!, (Guid)model.AcceptEmergencyAccessId!); return ProcessRegistrationResult(identityResult, user); @@ -185,7 +185,7 @@ public async Task PostRegisterFinish([FromBody] Reg case RegisterFinishTokenType.ProviderInvite: identityResult = await _registerUserCommand.RegisterUserViaProviderInviteToken( user, - masterPasswordHash, + masterPasswordAuthenticationHash, model.ProviderInviteToken!, (Guid)model.ProviderUserId!); return ProcessRegistrationResult(identityResult, user); diff --git a/test/Core.Test/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModelTests.cs b/test/Core.Test/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModelTests.cs index 16ba8dbf161e..3c099ce9626d 100644 --- a/test/Core.Test/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModelTests.cs +++ b/test/Core.Test/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModelTests.cs @@ -275,22 +275,28 @@ public void Validate_WhenNeitherAuthNorUnlock_AndRootKdfMissing_ReturnsBothRootK } [Fact] - public void Validate_WhenNeitherAuthNorUnlock_AndValidRootKdf_IsValid() + public void Validate_WhenAuthAndRootHashBothMissing_ReturnsMissingHashErrorOnly() { var model = new RegisterFinishRequestModel { Email = "user@example.com", UserAsymmetricKeys = new KeysRequestModel { PublicKey = "pk", EncryptedPrivateKey = "sk" }, + // Both MasterPasswordAuthentication and MasterPasswordHash are missing + MasterPasswordAuthentication = null, + MasterPasswordHash = null, + // Provide valid root KDF to avoid root KDF errors Kdf = KdfType.PBKDF2_SHA256, KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default, - // Memory and Parallelism irrelevant for PBKDF2 - EmailVerificationToken = "token" + EmailVerificationToken = "token" // avoid token error }; var results = Validate(model); - Assert.DoesNotContain(results, r => r.ErrorMessage?.Contains("Kdf") == true); - Assert.Empty(results.Where(r => r.ErrorMessage == "No valid registration token provided")); + // Only the new missing hash error should be present + Assert.Single(results); + Assert.Equal($"{nameof(MasterPasswordAuthenticationDataRequestModel.MasterPasswordAuthenticationHash)} and {nameof(RegisterFinishRequestModel.MasterPasswordHash)} not found on request, one needs to be defined.", results[0].ErrorMessage); + Assert.Contains(nameof(MasterPasswordAuthenticationDataRequestModel.MasterPasswordAuthenticationHash), results[0].MemberNames); + Assert.Contains(nameof(RegisterFinishRequestModel.MasterPasswordHash), results[0].MemberNames); } [Fact] diff --git a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs index 266bdba82390..55cb6da74758 100644 --- a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs +++ b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs @@ -239,7 +239,6 @@ public async Task RegisterNewIdentityFactoryUserAsync( if (requestModel.MasterPasswordUnlock != null) { var unlock = requestModel.MasterPasswordUnlock; - // PM-28143 - Once UserSymmetricKey is removed and UnlockData is required, delete the fallback to UserSymmetricKey below. // Always force a valid encrypted string for tests to avoid model validation failures. var masterKeyWrappedUserKey = DefaultEncryptedString; requestModel.MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel From f92d7d2bff6d49c94457e2525587b1aef0714f1e Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Mon, 12 Jan 2026 16:02:20 -0500 Subject: [PATCH 35/44] test(register): [PM-27084] Account Register Uses New Data Types - Addressed more feedback. No longer overriding the master password hash. --- .../Factories/IdentityApplicationFactory.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs index bee585881092..e190dda42715 100644 --- a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs +++ b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs @@ -240,11 +240,10 @@ public async Task RegisterNewIdentityFactoryUserAsync( { var unlock = requestModel.MasterPasswordUnlock; // Always force a valid encrypted string for tests to avoid model validation failures. - var masterKeyWrappedUserKey = DefaultEncryptedString; requestModel.MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel { Kdf = alignedKdf, - MasterKeyWrappedUserKey = masterKeyWrappedUserKey, + MasterKeyWrappedUserKey = unlock.MasterKeyWrappedUserKey, Salt = string.IsNullOrWhiteSpace(unlock.Salt) ? requestModel.Email : unlock.Salt }; } From 6301dbfc5e106f14525a243be123bfa87c17d566 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Thu, 15 Jan 2026 13:56:14 -0500 Subject: [PATCH 36/44] test(register): [PM-27084] Account Register Uses New Data Types - Fixed tests. --- .../Auth/AutoFixture/RegisterFinishRequestModelFixtures.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/Core.Test/Auth/AutoFixture/RegisterFinishRequestModelFixtures.cs b/test/Core.Test/Auth/AutoFixture/RegisterFinishRequestModelFixtures.cs index a751a16f31f7..22fca7ab5909 100644 --- a/test/Core.Test/Auth/AutoFixture/RegisterFinishRequestModelFixtures.cs +++ b/test/Core.Test/Auth/AutoFixture/RegisterFinishRequestModelFixtures.cs @@ -29,7 +29,9 @@ public void Customize(IFixture fixture) .With(o => o.OrgInviteToken, OrgInviteToken) .With(o => o.OrgSponsoredFreeFamilyPlanToken, OrgSponsoredFreeFamilyPlanToken) .With(o => o.AcceptEmergencyAccessInviteToken, AcceptEmergencyAccessInviteToken) - .With(o => o.ProviderInviteToken, ProviderInviteToken)); + .With(o => o.ProviderInviteToken, ProviderInviteToken) + .Without(o => o.MasterPasswordAuthentication) + .Without(o => o.MasterPasswordUnlock)); } } From e14f6a442151305460fbbc1779e7c5917380c7d4 Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Thu, 22 Jan 2026 16:40:04 -0500 Subject: [PATCH 37/44] fix(register): [PM-27084] Account Register Uses New Data Types - Added constant for feature flag. --- src/Core/Constants.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index 9ffe199f1d04..4d7bc416c511 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -165,6 +165,7 @@ public static class FeatureFlagKeys public const string MarketingInitiatedPremiumFlow = "pm-26140-marketing-initiated-premium-flow"; public const string PrefetchPasswordPrelogin = "pm-23801-prefetch-password-prelogin"; public const string PM27086_UpdateAuthenticationApisForInputPassword = "pm-27086-update-authentication-apis-for-input-password"; + public const string PM27044_UpdateRegistrationApis = "pm-27044-update-registration-apis"; /* Autofill Team */ public const string SSHAgent = "ssh-agent"; From c0266493797e8c5d010e4ebe68a5a018ca9308a2 Mon Sep 17 00:00:00 2001 From: Eli Grubb Date: Tue, 16 Dec 2025 17:49:38 -0700 Subject: [PATCH 38/44] [PM-27278] add AccountKeysRequestModel to RegisterFinishRequestModel for account encryption v2 support --- .../Accounts/RegisterFinishRequestModel.cs | 101 ++++- .../Registration/IRegisterUserCommand.cs | 21 +- .../Implementations/RegisterUserCommand.cs | 21 +- src/Core/Constants.cs | 1 + .../Data/MasterPasswordAuthenticationData.cs | 19 +- .../Models/Data/MasterPasswordUnlockData.cs | 19 +- .../Data/PublicKeyEncryptionKeyPairData.cs | 17 + .../Models/Data/RegisterFinishData.cs | 31 ++ .../Models/Data/SecurityStateData.cs | 16 + .../Models/Data/SignatureKeyPairData.cs | 17 + .../Models/Data/UserAccountKeysData.cs | 20 +- src/Core/Repositories/IUserRepository.cs | 2 + src/Core/Services/IUserService.cs | 3 +- .../Services/Implementations/UserService.cs | 18 +- .../Controllers/AccountsController.cs | 17 +- .../Repositories/UserRepository.cs | 26 ++ .../Repositories/UserRepository.cs | 24 + .../User_SetRegisterFinishUserData.sql | 30 ++ .../SignatureKeyPairRequestModelFixtures.cs | 19 + .../RegisterFinishRequestModelTests.cs | 138 +++++- .../Registration/RegisterUserCommandTests.cs | 139 +++--- .../Controllers/AccountsControllerTests.cs | 412 ++++++++++++------ .../Factories/IdentityApplicationFactory.cs | 47 ++ 23 files changed, 895 insertions(+), 263 deletions(-) create mode 100644 src/Core/KeyManagement/Models/Data/RegisterFinishData.cs create mode 100644 src/Sql/dbo/KeyManagement/Stored Procedures/User_SetRegisterFinishUserData.sql create mode 100644 test/Common/AutoFixture/SignatureKeyPairRequestModelFixtures.cs diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index cb66540a6bb4..a8e65709e394 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -1,6 +1,8 @@ using Bit.Core.Entities; using Bit.Core.Enums; +using Bit.Core.Exceptions; using Bit.Core.KeyManagement.Models.Api.Request; +using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Utilities; namespace Bit.Core.Auth.Models.Api.Request.Accounts; @@ -38,7 +40,12 @@ public class RegisterFinishRequestModel : IValidatableObject // in the MasterPasswordAuthenticationData. public string? UserSymmetricKey { get; set; } - public required KeysRequestModel UserAsymmetricKeys { get; set; } + // TODO Remove property below, deprecated due to new AccountKeys property + // https://bitwarden.atlassian.net/browse/PM-27326 + // Will throw error if both UserAsymmetricKeys and AccountKeys do not exist. + public KeysRequestModel? UserAsymmetricKeys { get; set; } + + public AccountKeysRequestModel? AccountKeys { get; set; } // PM-28143 - Remove line below (made optional during migration to MasterPasswordUnlockData) public KdfType? Kdf { get; set; } @@ -61,25 +68,84 @@ public class RegisterFinishRequestModel : IValidatableObject public Guid? ProviderUserId { get; set; } - public User ToUser() + public User ToUser(bool IsV2Encryption) { - var user = new User + // TODO remove IsV2Encryption bool and simplify logic below after a compatibility period - once V2 accounts are supported + // https://bitwarden.atlassian.net/browse/PM-27326 + if (!IsV2Encryption) + { + var user = new User + { + Email = Email, + MasterPasswordHint = MasterPasswordHint, + Kdf = (KdfType)(MasterPasswordUnlock?.Kdf.KdfType ?? Kdf)!, + KdfIterations = (int)(MasterPasswordUnlock?.Kdf.Iterations ?? KdfIterations)!, + // KdfMemory and KdfParallelism are optional (only used for Argon2id) + KdfMemory = MasterPasswordUnlock?.Kdf.Memory ?? KdfMemory, + KdfParallelism = MasterPasswordUnlock?.Kdf.Parallelism ?? KdfParallelism, + // PM-28827 To be added when MasterPasswordSalt is added to the user column + // MasterPasswordSalt = MasterPasswordUnlock?.Salt ?? Email.ToLower().Trim(), + Key = MasterPasswordUnlock?.MasterKeyWrappedUserKey ?? UserSymmetricKey + }; + + user = UserAsymmetricKeys?.ToUser(user) ?? throw new Exception("User's public and private account keys couldn't be found in either AccountKeys or UserAsymmetricKeys"); + + return user; + } + return new User { Email = Email, MasterPasswordHint = MasterPasswordHint, - Kdf = (KdfType)(MasterPasswordUnlock?.Kdf.KdfType ?? Kdf)!, - KdfIterations = (int)(MasterPasswordUnlock?.Kdf.Iterations ?? KdfIterations)!, - // KdfMemory and KdfParallelism are optional (only used for Argon2id) - KdfMemory = MasterPasswordUnlock?.Kdf.Memory ?? KdfMemory, - KdfParallelism = MasterPasswordUnlock?.Kdf.Parallelism ?? KdfParallelism, - // PM-28827 To be added when MasterPasswordSalt is added to the user column - // MasterPasswordSalt = MasterPasswordUnlock?.Salt ?? Email.ToLower().Trim(), - Key = MasterPasswordUnlock?.MasterKeyWrappedUserKey ?? UserSymmetricKey }; + } - UserAsymmetricKeys.ToUser(user); - - return user; + public RegisterFinishData ToData() + { + // TODO clean up flow once old fields are deprecated + // https://bitwarden.atlassian.net/browse/PM-27326 + return new RegisterFinishData + { + MasterPasswordUnlockData = MasterPasswordUnlock?.ToData() ?? + new MasterPasswordUnlockData + { + Kdf = new KdfSettings + { + KdfType = Kdf ?? throw new Exception("KdfType couldn't be found on either the MasterPasswordUnlockData or the Kdf property passed in."), + Iterations = KdfIterations ?? throw new Exception("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in."), + // KdfMemory and KdfParallelism are optional (only used for Argon2id) + Memory = KdfMemory, + Parallelism = KdfParallelism, + }, + MasterKeyWrappedUserKey = UserSymmetricKey ?? throw new Exception("MasterKeyWrappedUserKey couldn't be found on either the MasterPasswordUnlockData or the UserSymmetricKey property passed in."), + // PM-28827 To be added when MasterPasswordSalt is added to the user column + Salt = Email.ToLowerInvariant().Trim(), + }, + UserAccountKeysData = AccountKeys?.ToAccountKeysData() ?? + new UserAccountKeysData + { + PublicKeyEncryptionKeyPairData = new PublicKeyEncryptionKeyPairData + ( + UserAsymmetricKeys?.EncryptedPrivateKey ?? + throw new Exception("WrappedPrivateKey couldn't be found in either AccountKeys or UserAsymmetricKeys."), + UserAsymmetricKeys?.PublicKey ?? + throw new Exception("PublicKey couldn't be found in either AccountKeys or UserAsymmetricKeys") + ), + }, + MasterPasswordAuthenticationData = MasterPasswordAuthentication?.ToData() ?? + new MasterPasswordAuthenticationData + { + Kdf = new KdfSettings + { + KdfType = Kdf ?? throw new Exception("KdfType couldn't be found on either the MasterPasswordUnlockData or the Kdf property passed in."), + Iterations = KdfIterations ?? throw new Exception("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in."), + // KdfMemory and KdfParallelism are optional (only used for Argon2id) + Memory = KdfMemory, + Parallelism = KdfParallelism, + }, + MasterPasswordAuthenticationHash = MasterPasswordHash ?? throw new BadRequestException("MasterPasswordHash couldn't be found on either the MasterPasswordAuthenticationData or the MasterPasswordHash property passed in."), + Salt = Email.ToLowerInvariant().Trim(), + } + }; } public RegisterFinishTokenType GetTokenType() @@ -188,6 +254,13 @@ public IEnumerable Validate(ValidationContext validationContex yield return new ValidationResult($"{nameof(MasterPasswordAuthentication)} not found on RequestModel", [nameof(MasterPasswordAuthentication)]); } + if (AccountKeys == null && UserAsymmetricKeys == null) + { + yield return new ValidationResult( + $"{nameof(AccountKeys.PublicKeyEncryptionKeyPair.PublicKey)} and {nameof(AccountKeys.PublicKeyEncryptionKeyPair.WrappedPrivateKey)} not found in RequestModel", + [nameof(AccountKeys.PublicKeyEncryptionKeyPair.PublicKey), nameof(AccountKeys.PublicKeyEncryptionKeyPair.WrappedPrivateKey)]); + } + // 3. Lastly, validate access token type and presence. Must be done last because of yield break. RegisterFinishTokenType tokenType; var tokenTypeResolved = true; diff --git a/src/Core/Auth/UserFeatures/Registration/IRegisterUserCommand.cs b/src/Core/Auth/UserFeatures/Registration/IRegisterUserCommand.cs index 97c2eabd3c8a..9f8ad616da1a 100644 --- a/src/Core/Auth/UserFeatures/Registration/IRegisterUserCommand.cs +++ b/src/Core/Auth/UserFeatures/Registration/IRegisterUserCommand.cs @@ -1,5 +1,6 @@ using Bit.Core.AdminConsole.Entities; using Bit.Core.Entities; +using Bit.Core.KeyManagement.Models.Data; using Microsoft.AspNetCore.Identity; namespace Bit.Core.Auth.UserFeatures.Registration; @@ -31,11 +32,11 @@ public interface IRegisterUserCommand /// If the organization has a 2FA required policy enabled, email verification will be enabled for the user. /// /// The to create - /// The hashed master password the user entered + /// Cryptographic data for finishing user registration /// The org invite token sent to the user via email /// The associated org user guid that was created at the time of invite /// - public Task RegisterUserViaOrganizationInviteToken(User user, string masterPasswordHash, string orgInviteToken, Guid? orgUserId); + public Task RegisterUserViaOrganizationInviteToken(User user, RegisterFinishData registerFinishData, string orgInviteToken, Guid? orgUserId); /// /// Creates a new user with a given master password hash, sends a welcome email, and raises the signup reference event. @@ -43,10 +44,10 @@ public interface IRegisterUserCommand /// An error will be thrown if the token is invalid or expired. /// /// The to create - /// The hashed master password the user entered + /// Cryptographic data for finishing user registration /// The email verification token sent to the user via email /// - public Task RegisterUserViaEmailVerificationToken(User user, string masterPasswordHash, string emailVerificationToken); + public Task RegisterUserViaEmailVerificationToken(User user, RegisterFinishData registerFinishData, string emailVerificationToken); /// /// Creates a new user with a given master password hash, sends a welcome email, and raises the signup reference event. @@ -54,10 +55,10 @@ public interface IRegisterUserCommand /// If the token is invalid or expired, an error will be thrown. /// /// The to create - /// The hashed master password the user entered + /// Cryptographic data for finishing user registration /// The org sponsored free family plan invite token sent to the user via email /// - public Task RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(User user, string masterPasswordHash, string orgSponsoredFreeFamilyPlanInviteToken); + public Task RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(User user, RegisterFinishData registerFinishData, string orgSponsoredFreeFamilyPlanInviteToken); /// /// Creates a new user with a given master password hash, sends a welcome email, and raises the signup reference event. @@ -65,11 +66,11 @@ public interface IRegisterUserCommand /// If the token is invalid or expired, an error will be thrown. /// /// The to create - /// The hashed master password the user entered + /// Cryptographic data for finishing user registration /// The emergency access invite token sent to the user via email /// The emergency access id (used to validate the token) /// - public Task RegisterUserViaAcceptEmergencyAccessInviteToken(User user, string masterPasswordHash, + public Task RegisterUserViaAcceptEmergencyAccessInviteToken(User user, RegisterFinishData registerFinishData, string acceptEmergencyAccessInviteToken, Guid acceptEmergencyAccessId); /// @@ -78,10 +79,10 @@ public Task RegisterUserViaAcceptEmergencyAccessInviteToken(User /// If the token is invalid or expired, an error will be thrown. /// /// The to create - /// The hashed master password the user entered + /// Cryptographic data for finishing user registration /// The provider invite token sent to the user via email /// The provider user id which is used to validate the invite token /// - public Task RegisterUserViaProviderInviteToken(User user, string masterPasswordHash, string providerInviteToken, Guid providerUserId); + public Task RegisterUserViaProviderInviteToken(User user, RegisterFinishData registerFinishData, string providerInviteToken, Guid providerUserId); } diff --git a/src/Core/Auth/UserFeatures/Registration/Implementations/RegisterUserCommand.cs b/src/Core/Auth/UserFeatures/Registration/Implementations/RegisterUserCommand.cs index ba63afb54c09..db47b469c3c5 100644 --- a/src/Core/Auth/UserFeatures/Registration/Implementations/RegisterUserCommand.cs +++ b/src/Core/Auth/UserFeatures/Registration/Implementations/RegisterUserCommand.cs @@ -8,6 +8,7 @@ using Bit.Core.Billing.Extensions; using Bit.Core.Entities; using Bit.Core.Exceptions; +using Bit.Core.KeyManagement.Models.Data; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces; using Bit.Core.Repositories; using Bit.Core.Services; @@ -111,7 +112,7 @@ public async Task RegisterSSOAutoProvisionedUserAsync(User user, return result; } - public async Task RegisterUserViaOrganizationInviteToken(User user, string masterPasswordHash, + public async Task RegisterUserViaOrganizationInviteToken(User user, RegisterFinishData registerFinishData, string orgInviteToken, Guid? orgUserId) { TryValidateOrgInviteToken(orgInviteToken, orgUserId, user); @@ -129,7 +130,7 @@ public async Task RegisterUserViaOrganizationInviteToken(User us user.EmailVerified = true; } - var result = await _userService.CreateUserAsync(user, masterPasswordHash); + var result = await _userService.CreateUserAsync(user, registerFinishData); var organization = await GetOrganizationUserOrganization(orgUserId ?? Guid.Empty, orgUser); if (result == IdentityResult.Success) { @@ -280,7 +281,7 @@ private async Task SendAppropriateWelcomeEmailAsync(User user, string initiation } } - public async Task RegisterUserViaEmailVerificationToken(User user, string masterPasswordHash, + public async Task RegisterUserViaEmailVerificationToken(User user, RegisterFinishData registerFinishData, string emailVerificationToken) { ValidateOpenRegistrationAllowed(); @@ -292,7 +293,7 @@ public async Task RegisterUserViaEmailVerificationToken(User use user.Name = tokenable.Name; user.ApiKey = CoreHelpers.SecureRandomString(30); // API key can't be null. - var result = await _userService.CreateUserAsync(user, masterPasswordHash); + var result = await _userService.CreateUserAsync(user, registerFinishData); if (result == IdentityResult.Success) { await SendWelcomeEmailAsync(user); @@ -301,7 +302,7 @@ public async Task RegisterUserViaEmailVerificationToken(User use return result; } - public async Task RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(User user, string masterPasswordHash, + public async Task RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(User user, RegisterFinishData registerFinishData, string orgSponsoredFreeFamilyPlanInviteToken) { ValidateOpenRegistrationAllowed(); @@ -311,7 +312,7 @@ public async Task RegisterUserViaOrganizationSponsoredFreeFamily user.EmailVerified = true; user.ApiKey = CoreHelpers.SecureRandomString(30); // API key can't be null. - var result = await _userService.CreateUserAsync(user, masterPasswordHash); + var result = await _userService.CreateUserAsync(user, registerFinishData); if (result == IdentityResult.Success) { await SendWelcomeEmailAsync(user); @@ -322,7 +323,7 @@ public async Task RegisterUserViaOrganizationSponsoredFreeFamily // TODO: in future, consider how we can consolidate base registration logic to reduce code duplication - public async Task RegisterUserViaAcceptEmergencyAccessInviteToken(User user, string masterPasswordHash, + public async Task RegisterUserViaAcceptEmergencyAccessInviteToken(User user, RegisterFinishData registerFinishData, string acceptEmergencyAccessInviteToken, Guid acceptEmergencyAccessId) { ValidateOpenRegistrationAllowed(); @@ -332,7 +333,7 @@ public async Task RegisterUserViaAcceptEmergencyAccessInviteToke user.EmailVerified = true; user.ApiKey = CoreHelpers.SecureRandomString(30); // API key can't be null. - var result = await _userService.CreateUserAsync(user, masterPasswordHash); + var result = await _userService.CreateUserAsync(user, registerFinishData); if (result == IdentityResult.Success) { await SendWelcomeEmailAsync(user); @@ -341,7 +342,7 @@ public async Task RegisterUserViaAcceptEmergencyAccessInviteToke return result; } - public async Task RegisterUserViaProviderInviteToken(User user, string masterPasswordHash, + public async Task RegisterUserViaProviderInviteToken(User user, RegisterFinishData registerFinishData, string providerInviteToken, Guid providerUserId) { ValidateOpenRegistrationAllowed(); @@ -351,7 +352,7 @@ public async Task RegisterUserViaProviderInviteToken(User user, user.EmailVerified = true; user.ApiKey = CoreHelpers.SecureRandomString(30); // API key can't be null. - var result = await _userService.CreateUserAsync(user, masterPasswordHash); + var result = await _userService.CreateUserAsync(user, registerFinishData); if (result == IdentityResult.Success) { await SendWelcomeEmailAsync(user); diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index d707af6ca8f4..d7b5d3945c3a 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -208,6 +208,7 @@ public static class FeatureFlagKeys public const string EnableAccountEncryptionV2KeyConnectorRegistration = "enable-account-encryption-v2-key-connector-registration"; public const string SdkKeyRotation = "pm-30144-sdk-key-rotation"; public const string EnableAccountEncryptionV2JitPasswordRegistration = "enable-account-encryption-v2-jit-password-registration"; + public const string EnableAccountEncryptionV2PasswordRegistration = "pm-27278-v2-password-registration"; /* Mobile Team */ public const string AndroidImportLoginsFlow = "import-logins-flow"; diff --git a/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs b/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs index 6e53dfa744a4..8e3899eeb609 100644 --- a/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs +++ b/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs @@ -1,4 +1,4 @@ -using Bit.Core.Entities; +using Bit.Core.Entities; using Bit.Core.Exceptions; using Bit.Core.KeyManagement.Models.Api.Request; @@ -21,4 +21,21 @@ public void ValidateSaltUnchangedForUser(User user) throw new BadRequestException("Invalid master password salt."); } } + + public override bool Equals(object? obj) + { + if (obj is not MasterPasswordAuthenticationData other) + { + return false; + } + + return Kdf.Equals(other.Kdf) && + MasterPasswordAuthenticationHash == other.MasterPasswordAuthenticationHash && + Salt == other.Salt; + } + + public override int GetHashCode() + { + return HashCode.Combine(Kdf, MasterPasswordAuthenticationHash, Salt); + } } diff --git a/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs b/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs index f8139cba99ff..d63bbe9dd80d 100644 --- a/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs +++ b/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs @@ -1,4 +1,4 @@ -using Bit.Core.Entities; +using Bit.Core.Entities; using Bit.Core.Exceptions; using Bit.Core.KeyManagement.Models.Api.Request; @@ -21,4 +21,21 @@ public void ValidateSaltUnchangedForUser(User user) throw new BadRequestException("Invalid master password salt."); } } + + public override bool Equals(object? obj) + { + if (obj is not MasterPasswordUnlockData other) + { + return false; + } + + return Kdf.Equals(other.Kdf) && + MasterKeyWrappedUserKey == other.MasterKeyWrappedUserKey && + Salt == other.Salt; + } + + public override int GetHashCode() + { + return HashCode.Combine(Kdf, MasterKeyWrappedUserKey, Salt); + } } diff --git a/src/Core/KeyManagement/Models/Data/PublicKeyEncryptionKeyPairData.cs b/src/Core/KeyManagement/Models/Data/PublicKeyEncryptionKeyPairData.cs index fb8b09d39095..13f3c69f5417 100644 --- a/src/Core/KeyManagement/Models/Data/PublicKeyEncryptionKeyPairData.cs +++ b/src/Core/KeyManagement/Models/Data/PublicKeyEncryptionKeyPairData.cs @@ -17,4 +17,21 @@ public PublicKeyEncryptionKeyPairData(string wrappedPrivateKey, string publicKey PublicKey = publicKey ?? throw new ArgumentNullException(nameof(publicKey)); SignedPublicKey = signedPublicKey; } + + public override bool Equals(object? obj) + { + if (obj is not PublicKeyEncryptionKeyPairData other) + { + return false; + } + + return WrappedPrivateKey == other.WrappedPrivateKey && + SignedPublicKey == other.SignedPublicKey && + PublicKey == other.PublicKey; + } + + public override int GetHashCode() + { + return HashCode.Combine(WrappedPrivateKey, SignedPublicKey, PublicKey); + } } diff --git a/src/Core/KeyManagement/Models/Data/RegisterFinishData.cs b/src/Core/KeyManagement/Models/Data/RegisterFinishData.cs new file mode 100644 index 000000000000..c1c28f21c33b --- /dev/null +++ b/src/Core/KeyManagement/Models/Data/RegisterFinishData.cs @@ -0,0 +1,31 @@ +namespace Bit.Core.KeyManagement.Models.Data; + +public class RegisterFinishData +{ + public required MasterPasswordUnlockData MasterPasswordUnlockData { get; set; } + public required UserAccountKeysData UserAccountKeysData { get; set; } + public required MasterPasswordAuthenticationData MasterPasswordAuthenticationData {get; set; } + + public bool IsV2Encryption() + { + return UserAccountKeysData.IsV2Encryption(); + } + + public override bool Equals(object? obj) + { + if (obj is not RegisterFinishData other) + { + return false; + } + + return MasterPasswordUnlockData.Equals(other.MasterPasswordUnlockData) && + MasterPasswordAuthenticationData.Equals(other.MasterPasswordAuthenticationData) && + UserAccountKeysData.Equals(other.UserAccountKeysData) && + IsV2Encryption() == other.IsV2Encryption(); + } + + public override int GetHashCode() + { + return HashCode.Combine(MasterPasswordUnlockData, UserAccountKeysData, MasterPasswordAuthenticationData); + } +} \ No newline at end of file diff --git a/src/Core/KeyManagement/Models/Data/SecurityStateData.cs b/src/Core/KeyManagement/Models/Data/SecurityStateData.cs index c9a4610387a1..a37663ba070c 100644 --- a/src/Core/KeyManagement/Models/Data/SecurityStateData.cs +++ b/src/Core/KeyManagement/Models/Data/SecurityStateData.cs @@ -7,4 +7,20 @@ public class SecurityStateData // The security version is included in the security state, but needs COSE parsing, // so this is a separate copy that can be used directly. public required int SecurityVersion { get; set; } + + public override bool Equals(object? obj) + { + if (obj is not SecurityStateData other) + { + return false; + } + + return SecurityState == other.SecurityState && + SecurityVersion == other.SecurityVersion; + } + + public override int GetHashCode() + { + return HashCode.Combine(SecurityState, SecurityVersion); + } } diff --git a/src/Core/KeyManagement/Models/Data/SignatureKeyPairData.cs b/src/Core/KeyManagement/Models/Data/SignatureKeyPairData.cs index 32ae3eef8fdd..66f977a46d71 100644 --- a/src/Core/KeyManagement/Models/Data/SignatureKeyPairData.cs +++ b/src/Core/KeyManagement/Models/Data/SignatureKeyPairData.cs @@ -18,4 +18,21 @@ public SignatureKeyPairData(SignatureAlgorithm signatureAlgorithm, string wrappe WrappedSigningKey = wrappedSigningKey ?? throw new ArgumentNullException(nameof(wrappedSigningKey)); VerifyingKey = verifyingKey ?? throw new ArgumentNullException(nameof(verifyingKey)); } + + public override bool Equals(object? obj) + { + if (obj is not SignatureKeyPairData other) + { + return false; + } + + return SignatureAlgorithm == other.SignatureAlgorithm && + WrappedSigningKey == other.WrappedSigningKey && + VerifyingKey == other.VerifyingKey; + } + + public override int GetHashCode() + { + return HashCode.Combine(SignatureAlgorithm, WrappedSigningKey, VerifyingKey); + } } diff --git a/src/Core/KeyManagement/Models/Data/UserAccountKeysData.cs b/src/Core/KeyManagement/Models/Data/UserAccountKeysData.cs index 3d552a10de07..cf687f469577 100644 --- a/src/Core/KeyManagement/Models/Data/UserAccountKeysData.cs +++ b/src/Core/KeyManagement/Models/Data/UserAccountKeysData.cs @@ -1,4 +1,4 @@ -namespace Bit.Core.KeyManagement.Models.Data; +namespace Bit.Core.KeyManagement.Models.Data; /// /// Represents an expanded account cryptographic state for a user. Expanded here means @@ -31,4 +31,22 @@ public bool IsV2Encryption() throw new InvalidOperationException("Invalid account cryptographic state: V2 encryption fields must be either all present or all absent."); } } + + public override bool Equals(object? obj) + { + if (obj is not UserAccountKeysData other) + { + return false; + } + + return PublicKeyEncryptionKeyPairData.Equals(other.PublicKeyEncryptionKeyPairData) && + Equals(SignatureKeyPairData, other.SignatureKeyPairData) && + Equals(SecurityStateData, other.SecurityStateData) && + IsV2Encryption() == other.IsV2Encryption(); + } + + public override int GetHashCode() + { + return HashCode.Combine(PublicKeyEncryptionKeyPairData, SignatureKeyPairData, SecurityStateData); + } } diff --git a/src/Core/Repositories/IUserRepository.cs b/src/Core/Repositories/IUserRepository.cs index c5f78d76fc86..950df1187e89 100644 --- a/src/Core/Repositories/IUserRepository.cs +++ b/src/Core/Repositories/IUserRepository.cs @@ -92,6 +92,8 @@ UpdateUserData SetMasterPassword(Guid userId, MasterPasswordUnlockData masterPas /// Actions to update user data. /// On success Task UpdateUserDataAsync(IEnumerable updateUserDataActions); + + UpdateUserData SetRegisterFinishUserData(Guid userId, RegisterFinishData registerFinishData); } public delegate Task UpdateUserData(Microsoft.Data.SqlClient.SqlConnection? connection = null, diff --git a/src/Core/Services/IUserService.cs b/src/Core/Services/IUserService.cs index a531883db114..3ca4d0caf2cf 100644 --- a/src/Core/Services/IUserService.cs +++ b/src/Core/Services/IUserService.cs @@ -7,6 +7,7 @@ using Bit.Core.Billing.Models.Business; using Bit.Core.Entities; using Bit.Core.Enums; +using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Models.Business; using Fido2NetLib; using Microsoft.AspNetCore.Identity; @@ -22,7 +23,7 @@ public interface IUserService Task GetAccountRevisionDateByIdAsync(Guid userId); Task SaveUserAsync(User user, bool push = false); Task CreateUserAsync(User user); - Task CreateUserAsync(User user, string masterPasswordHash); + Task CreateUserAsync(User user, RegisterFinishData registerFinishData); Task SendMasterPasswordHintAsync(string email); Task StartWebAuthnRegistrationAsync(User user); Task DeleteWebAuthnKeyAsync(User user, int id); diff --git a/src/Core/Services/Implementations/UserService.cs b/src/Core/Services/Implementations/UserService.cs index 5f87ee85d2c8..046a66c3f33b 100644 --- a/src/Core/Services/Implementations/UserService.cs +++ b/src/Core/Services/Implementations/UserService.cs @@ -27,6 +27,7 @@ using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; +using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Models.Business; using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces; @@ -323,9 +324,22 @@ public async Task CreateUserAsync(User user) return await CreateAsync(user); } - public async Task CreateUserAsync(User user, string masterPasswordHash) + public async Task CreateUserAsync(User user, RegisterFinishData registerFinishData) { - return await CreateAsync(user, masterPasswordHash); + // TODO remove logic below after a compatibility period - once V2 accounts are fully supported + // https://bitwarden.atlassian.net/browse/PM-27326 + if (!registerFinishData.IsV2Encryption()) + { + return await CreateAsync(user, registerFinishData.MasterPasswordAuthenticationData.MasterPasswordAuthenticationHash); + } + + var result = await CreateAsync(user, registerFinishData.MasterPasswordAuthenticationData.MasterPasswordAuthenticationHash); + if (result.Succeeded) + { + var setRegisterFinishUserDataTask = _userRepository.SetRegisterFinishUserData(user.Id, registerFinishData); + await _userRepository.SetV2AccountCryptographicStateAsync(user.Id, registerFinishData.UserAccountKeysData, [setRegisterFinishUserDataTask]); + } + return result; } public async Task SendMasterPasswordHintAsync(string email) diff --git a/src/Identity/Controllers/AccountsController.cs b/src/Identity/Controllers/AccountsController.cs index e9807fb1fc4f..e2f748ca3792 100644 --- a/src/Identity/Controllers/AccountsController.cs +++ b/src/Identity/Controllers/AccountsController.cs @@ -141,28 +141,25 @@ public async Task PostRegisterVerificationEmailClicked([FromBody] [HttpPost("register/finish")] public async Task PostRegisterFinish([FromBody] RegisterFinishRequestModel model) { - User user = model.ToUser(); + var registerFinishData = model.ToData(); + var user = model.ToUser(registerFinishData.IsV2Encryption()); // Users will either have an emailed token or an email verification token - not both. IdentityResult? identityResult = null; - // PM-28143 - Just use the MasterPasswordAuthenticationData.MasterPasswordAuthenticationHash - string masterPasswordAuthenticationHash = model.MasterPasswordAuthentication?.MasterPasswordAuthenticationHash - ?? model.MasterPasswordHash!; - switch (model.GetTokenType()) { case RegisterFinishTokenType.EmailVerification: identityResult = await _registerUserCommand.RegisterUserViaEmailVerificationToken( user, - masterPasswordAuthenticationHash, + registerFinishData, model.EmailVerificationToken!); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.OrganizationInvite: identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken( user, - masterPasswordAuthenticationHash, + registerFinishData, model.OrgInviteToken!, model.OrganizationUserId); return ProcessRegistrationResult(identityResult, user); @@ -170,14 +167,14 @@ public async Task PostRegisterFinish([FromBody] Reg case RegisterFinishTokenType.OrgSponsoredFreeFamilyPlan: identityResult = await _registerUserCommand.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken( user, - masterPasswordAuthenticationHash, + registerFinishData, model.OrgSponsoredFreeFamilyPlanToken!); return ProcessRegistrationResult(identityResult, user); case RegisterFinishTokenType.EmergencyAccessInvite: identityResult = await _registerUserCommand.RegisterUserViaAcceptEmergencyAccessInviteToken( user, - masterPasswordAuthenticationHash, + registerFinishData, model.AcceptEmergencyAccessInviteToken!, (Guid)model.AcceptEmergencyAccessId!); return ProcessRegistrationResult(identityResult, user); @@ -185,7 +182,7 @@ public async Task PostRegisterFinish([FromBody] Reg case RegisterFinishTokenType.ProviderInvite: identityResult = await _registerUserCommand.RegisterUserViaProviderInviteToken( user, - masterPasswordAuthenticationHash, + registerFinishData, model.ProviderInviteToken!, (Guid)model.ProviderUserId!); return ProcessRegistrationResult(identityResult, user); diff --git a/src/Infrastructure.Dapper/Repositories/UserRepository.cs b/src/Infrastructure.Dapper/Repositories/UserRepository.cs index 920145f2f259..5e1a4859021b 100644 --- a/src/Infrastructure.Dapper/Repositories/UserRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/UserRepository.cs @@ -477,6 +477,32 @@ public async Task UpdateUserDataAsync(IEnumerable updateUserData } } + public UpdateUserData SetRegisterFinishUserData(Guid userId, RegisterFinishData registerFinishData) + { + return async (connection, transaction) => + { + var timestamp = DateTime.UtcNow; + + await connection!.ExecuteAsync( + "[dbo].[User_SetRegisterFinishUserData]", + new + { + Id = userId, + Kdf = registerFinishData.MasterPasswordUnlockData.Kdf.KdfType, + KdfIterations = registerFinishData.MasterPasswordUnlockData.Kdf.Iterations, + KdfMemory = registerFinishData.MasterPasswordUnlockData.Kdf.Memory, + KdfParallelism = registerFinishData.MasterPasswordUnlockData.Kdf.Parallelism, + Key = registerFinishData.MasterPasswordUnlockData.MasterKeyWrappedUserKey, + PublicKey = registerFinishData.UserAccountKeysData.PublicKeyEncryptionKeyPairData.PublicKey, + PrivateKey = registerFinishData.UserAccountKeysData.PublicKeyEncryptionKeyPairData.WrappedPrivateKey, + RevisionDate = timestamp, + AccountRevisionDate = timestamp, + }, + transaction: transaction, + commandType: CommandType.StoredProcedure); + }; + } + private async Task ProtectDataAndSaveAsync(User user, Func saveTask) { if (user == null) diff --git a/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs b/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs index 24c88d592cfc..17c19aa38a9d 100644 --- a/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs @@ -555,6 +555,30 @@ public async Task UpdateUserDataAsync(IEnumerable updateUserData await transaction.CommitAsync(); } + public UpdateUserData SetRegisterFinishUserData(Guid userId, RegisterFinishData registerFinishData) + { + return async (_, _) => + { + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); + + var userEntity = await dbContext.Users.FindAsync(userId) ?? throw new ArgumentException("User not found", nameof(userId)); + var timestamp = DateTime.UtcNow; + + userEntity.Kdf = registerFinishData.MasterPasswordUnlockData.Kdf.KdfType; + userEntity.KdfIterations = registerFinishData.MasterPasswordUnlockData.Kdf.Iterations; + userEntity.KdfMemory = registerFinishData.MasterPasswordUnlockData.Kdf.Memory; + userEntity.KdfParallelism = registerFinishData.MasterPasswordUnlockData.Kdf.Parallelism; + userEntity.Key = registerFinishData.MasterPasswordUnlockData.MasterKeyWrappedUserKey; + userEntity.PublicKey = registerFinishData.UserAccountKeysData.PublicKeyEncryptionKeyPairData.PublicKey; + userEntity.PrivateKey = registerFinishData.UserAccountKeysData.PublicKeyEncryptionKeyPairData.WrappedPrivateKey; + userEntity.RevisionDate = timestamp; + userEntity.AccountRevisionDate = timestamp; + + await dbContext.SaveChangesAsync(); + }; + } + private static void MigrateDefaultUserCollectionsToShared(DatabaseContext dbContext, IEnumerable userIds) { var defaultCollections = (from c in dbContext.Collections diff --git a/src/Sql/dbo/KeyManagement/Stored Procedures/User_SetRegisterFinishUserData.sql b/src/Sql/dbo/KeyManagement/Stored Procedures/User_SetRegisterFinishUserData.sql new file mode 100644 index 000000000000..648e9a66bbf0 --- /dev/null +++ b/src/Sql/dbo/KeyManagement/Stored Procedures/User_SetRegisterFinishUserData.sql @@ -0,0 +1,30 @@ +CREATE PROCEDURE [dbo].[User_SetRegisterFinishUserData] + @Id UNIQUEIDENTIFIER, + @Kdf TINYINT, + @KdfIterations INT, + @KdfMemory INT, + @KdfParallelism INT, + @Key VARCHAR(MAX), + @PublicKey VARCHAR(MAX), + @PrivateKey VARCHAR(MAX), + @RevisionDate DATETIME2(7), + @AccountRevisionDate DATETIME2(7) +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[User] + SET + [Key] = @Key, + [Kdf] = @Kdf, + [KdfIterations] = @KdfIterations, + [KdfMemory] = @KdfMemory, + [KdfParallelism] = @KdfParallelism, + [PublicKey] = @PublicKey, + [PrivateKey] = @PrivateKey, + [RevisionDate] = @RevisionDate, + [AccountRevisionDate] = @AccountRevisionDate + WHERE + [Id] = @Id +END diff --git a/test/Common/AutoFixture/SignatureKeyPairRequestModelFixtures.cs b/test/Common/AutoFixture/SignatureKeyPairRequestModelFixtures.cs new file mode 100644 index 000000000000..a58528707e18 --- /dev/null +++ b/test/Common/AutoFixture/SignatureKeyPairRequestModelFixtures.cs @@ -0,0 +1,19 @@ +using AutoFixture; +using Bit.Core.KeyManagement.Models.Api.Request; +using Bit.Test.Common.AutoFixture.Attributes; + +namespace Bit.Test.Common.AutoFixture; + +internal class SignatureKeyPairRequestModelCustomization : ICustomization +{ + public void Customize(IFixture fixture) + { + fixture.Customize(composer => composer + .With(o => o.SignatureAlgorithm, "ed25519")); + } +} + +public class SignatureKeyPairRequestModelCustomizeAttribute : BitCustomizeAttribute +{ + public override ICustomization GetCustomization() => new SignatureKeyPairRequestModelCustomization(); +} \ No newline at end of file diff --git a/test/Core.Test/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModelTests.cs b/test/Core.Test/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModelTests.cs index 3c099ce9626d..b9532f989d50 100644 --- a/test/Core.Test/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModelTests.cs +++ b/test/Core.Test/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModelTests.cs @@ -1,6 +1,7 @@ using Bit.Core.Auth.Models.Api.Request.Accounts; using Bit.Core.Enums; using Bit.Core.KeyManagement.Models.Api.Request; +using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using Xunit; @@ -150,15 +151,89 @@ public void GetTokenType_Returns_Invalid(string email, string masterPasswordHash [Theory] [BitAutoData] - public void ToUser_Returns_User(string email, string masterPasswordHash, string masterPasswordHint, - string userSymmetricKey, KeysRequestModel userAsymmetricKeys, KdfType kdf, int kdfIterations, - int? kdfMemory, int? kdfParallelism) + [SignatureKeyPairRequestModelCustomize] + public void ToData_Returns_ToData(string email, string masterPasswordHint, KdfRequestModel kdfRequest, string masterPasswordAuthenticationHash, AccountKeysRequestModel accountKeysRequest, string userSymmetricKey, KeysRequestModel userAsymmetricKeys, KdfType kdf, int kdfIterations, int? kdfMemory, int? kdfParallelism) { // Arrange - var model = new RegisterFinishRequestModel + // V1 model and fields to be removed with + // https://bitwarden.atlassian.net/browse/PM-27326 + var legacyModel = new RegisterFinishRequestModel { Email = email, - MasterPasswordHash = masterPasswordHash, + MasterPasswordHash = masterPasswordAuthenticationHash, + MasterPasswordHint = masterPasswordHint, + UserSymmetricKey = userSymmetricKey, + UserAsymmetricKeys = userAsymmetricKeys, + Kdf = kdf, + KdfIterations = kdfIterations, + KdfMemory = kdfMemory, + KdfParallelism = kdfParallelism + }; + var newModel = new RegisterFinishRequestModel + { + Email = email, + MasterPasswordHint = masterPasswordHint, + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel + { + Kdf = kdfRequest, + MasterPasswordAuthenticationHash = masterPasswordAuthenticationHash, + Salt = email.ToLowerInvariant().Trim() + }, + MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel + { + Kdf = kdfRequest, + MasterKeyWrappedUserKey = userSymmetricKey, + Salt = email.ToLowerInvariant().Trim() + }, + AccountKeys = accountKeysRequest + }; + + // Act + var legacyData = legacyModel.ToData(); + var newData = newModel.ToData(); + + // Assert + Assert.False(legacyData.IsV2Encryption()); + Assert.Equal(legacyData.MasterPasswordUnlockData.Kdf.KdfType, kdf); + Assert.Equal(legacyData.MasterPasswordUnlockData.Kdf.Iterations, kdfIterations); + Assert.Equal(legacyData.MasterPasswordUnlockData.Kdf.Memory, kdfMemory); + Assert.Equal(legacyData.MasterPasswordUnlockData.Kdf.Parallelism, kdfParallelism); + Assert.Equal(legacyData.MasterPasswordUnlockData.MasterKeyWrappedUserKey, userSymmetricKey); + Assert.Equal(legacyData.MasterPasswordUnlockData.Salt, email.ToLowerInvariant().Trim()); + Assert.Equal(legacyData.UserAccountKeysData.PublicKeyEncryptionKeyPairData.PublicKey, userAsymmetricKeys.PublicKey); + Assert.Equal(legacyData.UserAccountKeysData.PublicKeyEncryptionKeyPairData.WrappedPrivateKey, userAsymmetricKeys.EncryptedPrivateKey); + Assert.Equal(legacyData.MasterPasswordAuthenticationData.Kdf.KdfType, kdf); + Assert.Equal(legacyData.MasterPasswordAuthenticationData.Kdf.Iterations, kdfIterations); + Assert.Equal(legacyData.MasterPasswordAuthenticationData.Kdf.Memory, kdfMemory); + Assert.Equal(legacyData.MasterPasswordAuthenticationData.Kdf.Parallelism, kdfParallelism); + Assert.Equal(legacyData.MasterPasswordAuthenticationData.MasterPasswordAuthenticationHash, masterPasswordAuthenticationHash); + Assert.Equal(legacyData.MasterPasswordAuthenticationData.Salt, email.ToLowerInvariant().Trim()); + + + Assert.True(newData.IsV2Encryption()); + Assert.Equal(newData.MasterPasswordUnlockData.Kdf, kdfRequest.ToData()); + Assert.Equal(newData.MasterPasswordUnlockData.MasterKeyWrappedUserKey, userSymmetricKey); + Assert.Equal(newData.MasterPasswordUnlockData.Salt, email.ToLowerInvariant()); + Assert.Equal(newData.MasterPasswordAuthenticationData.Kdf, kdfRequest.ToData()); + Assert.Equal(newData.MasterPasswordAuthenticationData.MasterPasswordAuthenticationHash, masterPasswordAuthenticationHash); + Assert.Equal(newData.MasterPasswordAuthenticationData.Salt, email.ToLowerInvariant().Trim()); + Assert.Equal(newData.UserAccountKeysData, accountKeysRequest.ToAccountKeysData()); + } + + [Theory] + [BitAutoData] + [SignatureKeyPairRequestModelCustomize] + public void ToUser_Returns_User(string email, string masterPasswordHint, AccountKeysRequestModel accountKeysRequest, + KdfRequestModel kdfRequest, string masterPasswordAuthenticationHash, string userSymmetricKey, + KeysRequestModel userAsymmetricKeys, KdfType kdf, int kdfIterations, int? kdfMemory, int? kdfParallelism) + { + // Arrange + // V1 model and fields to be removed with + // https://bitwarden.atlassian.net/browse/PM-27326 + var legacyModel = new RegisterFinishRequestModel + { + Email = email, + MasterPasswordHash = masterPasswordAuthenticationHash, MasterPasswordHint = masterPasswordHint, UserSymmetricKey = userSymmetricKey, UserAsymmetricKeys = userAsymmetricKeys, @@ -167,20 +242,53 @@ public void ToUser_Returns_User(string email, string masterPasswordHash, string KdfMemory = kdfMemory, KdfParallelism = kdfParallelism }; + var newModel = new RegisterFinishRequestModel + { + Email = email, + MasterPasswordHint = masterPasswordHint, + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel + { + Kdf = kdfRequest, + MasterPasswordAuthenticationHash = masterPasswordAuthenticationHash, + Salt = email.ToLowerInvariant().Trim() + }, + MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel + { + Kdf = kdfRequest, + MasterKeyWrappedUserKey = userSymmetricKey, + Salt = email.ToLowerInvariant().Trim() + }, + AccountKeys = accountKeysRequest + }; // Act - var result = model.ToUser(); + Assert.False(legacyModel.ToData().IsV2Encryption()); + var legacyResult = legacyModel.ToUser(false); + Assert.True(newModel.ToData().IsV2Encryption()); + var newResult = newModel.ToUser(true); // Assert - Assert.Equal(email, result.Email); - Assert.Equal(masterPasswordHint, result.MasterPasswordHint); - Assert.Equal(kdf, result.Kdf); - Assert.Equal(kdfIterations, result.KdfIterations); - Assert.Equal(kdfMemory, result.KdfMemory); - Assert.Equal(kdfParallelism, result.KdfParallelism); - Assert.Equal(userSymmetricKey, result.Key); - Assert.Equal(userAsymmetricKeys.PublicKey, result.PublicKey); - Assert.Equal(userAsymmetricKeys.EncryptedPrivateKey, result.PrivateKey); + Assert.Equal(email, legacyResult.Email); + Assert.Equal(masterPasswordHint, legacyResult.MasterPasswordHint); + Assert.Equal(kdf, legacyResult.Kdf); + Assert.Equal(kdfIterations, legacyResult.KdfIterations); + Assert.Equal(kdfMemory, legacyResult.KdfMemory); + Assert.Equal(kdfParallelism, legacyResult.KdfParallelism); + Assert.Equal(userSymmetricKey, legacyResult.Key); + Assert.Equal(userAsymmetricKeys.PublicKey, legacyResult.PublicKey); + Assert.Equal(userAsymmetricKeys.EncryptedPrivateKey, legacyResult.PrivateKey); + + // V2 expected fields + // all should be default/unset, with the exception of email and masterPasswordHint + Assert.Equal(email, newResult.Email); + Assert.Equal(masterPasswordHint, newResult.MasterPasswordHint); + Assert.Equal(KdfType.PBKDF2_SHA256, newResult.Kdf); + Assert.Equal(AuthConstants.PBKDF2_ITERATIONS.Default, newResult.KdfIterations); + Assert.Null(newResult.KdfMemory); + Assert.Null(newResult.KdfParallelism); + Assert.Null(newResult.Key); + Assert.Null(newResult.PublicKey); + Assert.Null(newResult.PrivateKey); } [Fact] diff --git a/test/Core.Test/Auth/UserFeatures/Registration/RegisterUserCommandTests.cs b/test/Core.Test/Auth/UserFeatures/Registration/RegisterUserCommandTests.cs index 29193bacbc5a..6728f6c42f63 100644 --- a/test/Core.Test/Auth/UserFeatures/Registration/RegisterUserCommandTests.cs +++ b/test/Core.Test/Auth/UserFeatures/Registration/RegisterUserCommandTests.cs @@ -9,6 +9,7 @@ using Bit.Core.Auth.UserFeatures.Registration.Implementations; using Bit.Core.Billing.Enums; using Bit.Core.Entities; +using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Exceptions; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces; using Bit.Core.Repositories; @@ -217,24 +218,24 @@ await sutProvider.GetDependency() [Theory] [BitAutoData] public async Task RegisterUserViaOrganizationInviteToken_NoOrgInviteOrOrgUserIdOrReferenceData_Succeeds( - SutProvider sutProvider, User user, string masterPasswordHash) + SutProvider sutProvider, User user, RegisterFinishData registerFinishData) { // Arrange user.ReferenceData = null; sutProvider.GetDependency() - .CreateUserAsync(user, masterPasswordHash) + .CreateUserAsync(user, registerFinishData) .Returns(IdentityResult.Success); // Act - var result = await sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, masterPasswordHash, null, null); + var result = await sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, registerFinishData, null, null); // Assert Assert.True(result.Succeeded); await sutProvider.GetDependency() .Received(1) - .CreateUserAsync(user, masterPasswordHash); + .CreateUserAsync(user, registerFinishData); } // Complex happy path test @@ -243,7 +244,7 @@ await sutProvider.GetDependency() [BitAutoData(true, "sampleInitiationPath")] [BitAutoData(true, "Secrets Manager trial")] public async Task RegisterUserViaOrganizationInviteToken_ComplexHappyPath_Succeeds(bool addUserReferenceData, string initiationPath, - SutProvider sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId, + SutProvider sutProvider, User user, RegisterFinishData registerFinishData, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId, [Policy(PolicyType.TwoFactorAuthentication, true)] PolicyStatus policy) { // Arrange @@ -275,13 +276,13 @@ public async Task RegisterUserViaOrganizationInviteToken_ComplexHappyPath_Succee .Returns(policy); sutProvider.GetDependency() - .CreateUserAsync(user, masterPasswordHash) + .CreateUserAsync(user, registerFinishData) .Returns(IdentityResult.Success); user.ReferenceData = addUserReferenceData ? $"{{\"initiationPath\":\"{initiationPath}\"}}" : null; // Act - var result = await sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, masterPasswordHash, orgInviteToken, orgUserId); + var result = await sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, registerFinishData, orgInviteToken, orgUserId); // Assert await sutProvider.GetDependency() @@ -313,7 +314,7 @@ await sutProvider.GetDependency() await sutProvider.GetDependency() .Received(1) - .CreateUserAsync(Arg.Is(u => u.EmailVerified == true && u.ApiKey != null), masterPasswordHash); + .CreateUserAsync(Arg.Is(u => u.EmailVerified == true && u.ApiKey != null), registerFinishData); if (addUserReferenceData) { @@ -347,7 +348,7 @@ await sutProvider.GetDependency() [BitAutoData("nullOrgInviteToken")] [BitAutoData("nullOrgUserId")] public async Task RegisterUserViaOrganizationInviteToken_MissingOrInvalidOrgInviteDataWithDisabledOpenRegistration_ThrowsBadRequestException(string scenario, - SutProvider sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid? orgUserId) + SutProvider sutProvider, User user, RegisterFinishData registerFinishData, OrganizationUser orgUser, string orgInviteToken, Guid? orgUserId) { // Arrange sutProvider.GetDependency() @@ -376,7 +377,7 @@ public async Task RegisterUserViaOrganizationInviteToken_MissingOrInvalidOrgInvi } // Act & Assert - var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, masterPasswordHash, orgInviteToken, orgUserId)); + var exception = await Assert.ThrowsAsync(() => sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, registerFinishData, orgInviteToken, orgUserId)); Assert.Equal("Open registration has been disabled by the system administrator.", exception.Message); } @@ -385,7 +386,7 @@ public async Task RegisterUserViaOrganizationInviteToken_MissingOrInvalidOrgInvi [BitAutoData("nullOrgInviteToken")] [BitAutoData("nullOrgUserId")] public async Task RegisterUserViaOrganizationInviteToken_MissingOrInvalidOrgInviteDataWithEnabledOpenRegistration_ThrowsBadRequestException(string scenario, - SutProvider sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid? orgUserId) + SutProvider sutProvider, User user, RegisterFinishData registerFinishData, OrganizationUser orgUser, string orgInviteToken, Guid? orgUserId) { // Arrange sutProvider.GetDependency() @@ -421,19 +422,19 @@ public async Task RegisterUserViaOrganizationInviteToken_MissingOrInvalidOrgInvi user.ReferenceData = null; sutProvider.GetDependency() - .CreateUserAsync(user, masterPasswordHash) + .CreateUserAsync(user, registerFinishData) .Returns(IdentityResult.Success); // Act var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, masterPasswordHash, orgInviteToken, orgUserId)); + sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, registerFinishData, orgInviteToken, orgUserId)); Assert.Equal(expectedErrorMessage, exception.Message); } [Theory] [BitAutoData] public async Task RegisterUserViaOrganizationInviteToken_BlockedDomainFromDifferentOrg_ThrowsBadRequestException( - SutProvider sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId, + SutProvider sutProvider, User user, RegisterFinishData registerFinishData, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId, [Policy(PolicyType.TwoFactorAuthentication, false)] PolicyStatus policy) { // Arrange @@ -472,14 +473,14 @@ public async Task RegisterUserViaOrganizationInviteToken_BlockedDomainFromDiffer // Act & Assert var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, masterPasswordHash, orgInviteToken, orgUserId)); + sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, registerFinishData, orgInviteToken, orgUserId)); Assert.Equal("This email address is claimed by an organization using Bitwarden.", exception.Message); } [Theory] [BitAutoData] public async Task RegisterUserViaOrganizationInviteToken_BlockedDomainFromSameOrg_Succeeds( - SutProvider sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId, + SutProvider sutProvider, User user, RegisterFinishData registerFinishData, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId, [Policy(PolicyType.TwoFactorAuthentication, false)] PolicyStatus policy) { // Arrange @@ -514,7 +515,7 @@ public async Task RegisterUserViaOrganizationInviteToken_BlockedDomainFromSameOr .Returns(false); sutProvider.GetDependency() - .CreateUserAsync(user, masterPasswordHash) + .CreateUserAsync(user, registerFinishData) .Returns(IdentityResult.Success); sutProvider.GetDependency() @@ -522,7 +523,7 @@ public async Task RegisterUserViaOrganizationInviteToken_BlockedDomainFromSameOr .Returns(policy); // Act - var result = await sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, masterPasswordHash, orgInviteToken, orgUserId); + var result = await sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, registerFinishData, orgInviteToken, orgUserId); // Assert Assert.True(result.Succeeded); @@ -534,7 +535,7 @@ await sutProvider.GetDependency() [Theory] [BitAutoData] public async Task RegisterUserViaOrganizationInviteToken_WithValidTokenButNullOrgUser_ThrowsBadRequestException( - SutProvider sutProvider, User user, string masterPasswordHash, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId) + SutProvider sutProvider, User user, RegisterFinishData registerFinishData, OrganizationUser orgUser, string orgInviteToken, Guid orgUserId) { // Arrange user.Email = "user@example.com"; @@ -558,7 +559,7 @@ public async Task RegisterUserViaOrganizationInviteToken_WithValidTokenButNullOr // Act & Assert var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, masterPasswordHash, orgInviteToken, orgUserId)); + sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, registerFinishData, orgInviteToken, orgUserId)); Assert.Equal("Invalid organization user invitation.", exception.Message); // Verify that GetByIdAsync was called @@ -569,7 +570,7 @@ await sutProvider.GetDependency() // Verify that user creation was never attempted await sutProvider.GetDependency() .DidNotReceive() - .CreateUserAsync(Arg.Any(), Arg.Any()); + .CreateUserAsync(Arg.Any(), Arg.Any()); } // ----------------------------------------------------------------------------------------------- @@ -578,7 +579,7 @@ await sutProvider.GetDependency() [Theory] [BitAutoData] - public async Task RegisterUserViaEmailVerificationToken_Succeeds(SutProvider sutProvider, User user, string masterPasswordHash, string emailVerificationToken, bool receiveMarketingMaterials) + public async Task RegisterUserViaEmailVerificationToken_Succeeds(SutProvider sutProvider, User user, RegisterFinishData registerFinishData, string emailVerificationToken, bool receiveMarketingMaterials) { // Arrange user.Email = $"test+{Guid.NewGuid()}@example.com"; @@ -596,18 +597,18 @@ public async Task RegisterUserViaEmailVerificationToken_Succeeds(SutProvider() - .CreateUserAsync(user, masterPasswordHash) + .CreateUserAsync(user, registerFinishData) .Returns(IdentityResult.Success); // Act - var result = await sutProvider.Sut.RegisterUserViaEmailVerificationToken(user, masterPasswordHash, emailVerificationToken); + var result = await sutProvider.Sut.RegisterUserViaEmailVerificationToken(user, registerFinishData, emailVerificationToken); // Assert Assert.True(result.Succeeded); await sutProvider.GetDependency() .Received(1) - .CreateUserAsync(Arg.Is(u => u.Name == user.Name && u.EmailVerified == true && u.ApiKey != null), masterPasswordHash); + .CreateUserAsync(Arg.Is(u => u.Name == user.Name && u.EmailVerified == true && u.ApiKey != null), registerFinishData); await sutProvider.GetDependency() .Received(1) @@ -616,7 +617,7 @@ await sutProvider.GetDependency() [Theory] [BitAutoData] - public async Task RegisterUserViaEmailVerificationToken_InvalidToken_ThrowsBadRequestException(SutProvider sutProvider, User user, string masterPasswordHash, string emailVerificationToken, bool receiveMarketingMaterials) + public async Task RegisterUserViaEmailVerificationToken_InvalidToken_ThrowsBadRequestException(SutProvider sutProvider, User user, RegisterFinishData registerFinishData, string emailVerificationToken, bool receiveMarketingMaterials) { // Arrange user.Email = $"test+{Guid.NewGuid()}@example.com"; @@ -634,21 +635,21 @@ public async Task RegisterUserViaEmailVerificationToken_InvalidToken_ThrowsBadRe }); // Act & Assert - var result = await Assert.ThrowsAsync(() => sutProvider.Sut.RegisterUserViaEmailVerificationToken(user, masterPasswordHash, emailVerificationToken)); + var result = await Assert.ThrowsAsync(() => sutProvider.Sut.RegisterUserViaEmailVerificationToken(user, registerFinishData, emailVerificationToken)); Assert.Equal("Invalid email verification token.", result.Message); } [Theory] [BitAutoData] - public async Task RegisterUserViaEmailVerificationToken_DisabledOpenRegistration_ThrowsBadRequestException(SutProvider sutProvider, User user, string masterPasswordHash, string emailVerificationToken) + public async Task RegisterUserViaEmailVerificationToken_DisabledOpenRegistration_ThrowsBadRequestException(SutProvider sutProvider, User user, RegisterFinishData registerFinishData, string emailVerificationToken) { // Arrange sutProvider.GetDependency() .DisableUserRegistration = true; // Act & Assert - var result = await Assert.ThrowsAsync(() => sutProvider.Sut.RegisterUserViaEmailVerificationToken(user, masterPasswordHash, emailVerificationToken)); + var result = await Assert.ThrowsAsync(() => sutProvider.Sut.RegisterUserViaEmailVerificationToken(user, registerFinishData, emailVerificationToken)); Assert.Equal("Open registration has been disabled by the system administrator.", result.Message); } @@ -659,7 +660,7 @@ public async Task RegisterUserViaEmailVerificationToken_DisabledOpenRegistration [Theory] [BitAutoData] - public async Task RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken_Succeeds(SutProvider sutProvider, User user, string masterPasswordHash, + public async Task RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken_Succeeds(SutProvider sutProvider, User user, RegisterFinishData registerFinishData, string orgSponsoredFreeFamilyPlanInviteToken) { // Arrange @@ -674,18 +675,18 @@ public async Task RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken_ .Returns((true, new OrganizationSponsorship())); sutProvider.GetDependency() - .CreateUserAsync(user, masterPasswordHash) + .CreateUserAsync(user, registerFinishData) .Returns(IdentityResult.Success); // Act - var result = await sutProvider.Sut.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(user, masterPasswordHash, orgSponsoredFreeFamilyPlanInviteToken); + var result = await sutProvider.Sut.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(user, registerFinishData, orgSponsoredFreeFamilyPlanInviteToken); // Assert Assert.True(result.Succeeded); await sutProvider.GetDependency() .Received(1) - .CreateUserAsync(Arg.Is(u => u.Name == user.Name && u.EmailVerified == true && u.ApiKey != null), masterPasswordHash); + .CreateUserAsync(Arg.Is(u => u.Name == user.Name && u.EmailVerified == true && u.ApiKey != null), registerFinishData); await sutProvider.GetDependency() .Received(1) @@ -695,7 +696,7 @@ await sutProvider.GetDependency() [Theory] [BitAutoData] public async Task RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken_InvalidToken_ThrowsBadRequestException(SutProvider sutProvider, User user, - string masterPasswordHash, string orgSponsoredFreeFamilyPlanInviteToken) + RegisterFinishData registerFinishData, string orgSponsoredFreeFamilyPlanInviteToken) { // Arrange user.Email = $"test+{Guid.NewGuid()}@example.com"; @@ -710,7 +711,7 @@ public async Task RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken_ // Act & Assert var result = await Assert.ThrowsAsync(() => - sutProvider.Sut.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(user, masterPasswordHash, orgSponsoredFreeFamilyPlanInviteToken)); + sutProvider.Sut.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(user, registerFinishData, orgSponsoredFreeFamilyPlanInviteToken)); Assert.Equal("Invalid org sponsored free family plan token.", result.Message); } @@ -718,7 +719,7 @@ public async Task RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken_ [Theory] [BitAutoData] public async Task RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken_DisabledOpenRegistration_ThrowsBadRequestException(SutProvider sutProvider, User user, - string masterPasswordHash, string orgSponsoredFreeFamilyPlanInviteToken) + RegisterFinishData registerFinishData, string orgSponsoredFreeFamilyPlanInviteToken) { // Arrange sutProvider.GetDependency() @@ -726,7 +727,7 @@ public async Task RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken_ // Act & Assert var result = await Assert.ThrowsAsync(() => - sutProvider.Sut.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(user, masterPasswordHash, orgSponsoredFreeFamilyPlanInviteToken)); + sutProvider.Sut.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(user, registerFinishData, orgSponsoredFreeFamilyPlanInviteToken)); Assert.Equal("Open registration has been disabled by the system administrator.", result.Message); } @@ -737,7 +738,7 @@ public async Task RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken_ [Theory] [BitAutoData] public async Task RegisterUserViaAcceptEmergencyAccessInviteToken_Succeeds( - SutProvider sutProvider, User user, string masterPasswordHash, + SutProvider sutProvider, User user, RegisterFinishData registerFinishData, EmergencyAccessEntity emergencyAccess, string acceptEmergencyAccessInviteToken, Guid acceptEmergencyAccessId) { // Arrange @@ -758,18 +759,18 @@ public async Task RegisterUserViaAcceptEmergencyAccessInviteToken_Succeeds( }); sutProvider.GetDependency() - .CreateUserAsync(user, masterPasswordHash) + .CreateUserAsync(user, registerFinishData) .Returns(IdentityResult.Success); // Act - var result = await sutProvider.Sut.RegisterUserViaAcceptEmergencyAccessInviteToken(user, masterPasswordHash, acceptEmergencyAccessInviteToken, acceptEmergencyAccessId); + var result = await sutProvider.Sut.RegisterUserViaAcceptEmergencyAccessInviteToken(user, registerFinishData, acceptEmergencyAccessInviteToken, acceptEmergencyAccessId); // Assert Assert.True(result.Succeeded); await sutProvider.GetDependency() .Received(1) - .CreateUserAsync(Arg.Is(u => u.Name == user.Name && u.EmailVerified == true && u.ApiKey != null), masterPasswordHash); + .CreateUserAsync(Arg.Is(u => u.Name == user.Name && u.EmailVerified == true && u.ApiKey != null), registerFinishData); await sutProvider.GetDependency() .Received(1) @@ -779,7 +780,7 @@ await sutProvider.GetDependency() [Theory] [BitAutoData] public async Task RegisterUserViaAcceptEmergencyAccessInviteToken_InvalidToken_ThrowsBadRequestException(SutProvider sutProvider, User user, - string masterPasswordHash, EmergencyAccessEntity emergencyAccess, string acceptEmergencyAccessInviteToken, Guid acceptEmergencyAccessId) + RegisterFinishData registerFinishData, EmergencyAccessEntity emergencyAccess, string acceptEmergencyAccessInviteToken, Guid acceptEmergencyAccessId) { // Arrange user.Email = $"test+{Guid.NewGuid()}@example.com"; @@ -800,7 +801,7 @@ public async Task RegisterUserViaAcceptEmergencyAccessInviteToken_InvalidToken_T // Act & Assert var result = await Assert.ThrowsAsync(() => - sutProvider.Sut.RegisterUserViaAcceptEmergencyAccessInviteToken(user, masterPasswordHash, acceptEmergencyAccessInviteToken, acceptEmergencyAccessId)); + sutProvider.Sut.RegisterUserViaAcceptEmergencyAccessInviteToken(user, registerFinishData, acceptEmergencyAccessInviteToken, acceptEmergencyAccessId)); Assert.Equal("Invalid accept emergency access invite token.", result.Message); } @@ -808,7 +809,7 @@ public async Task RegisterUserViaAcceptEmergencyAccessInviteToken_InvalidToken_T [Theory] [BitAutoData] public async Task RegisterUserViaAcceptEmergencyAccessInviteToken_DisabledOpenRegistration_ThrowsBadRequestException(SutProvider sutProvider, User user, - string masterPasswordHash, string acceptEmergencyAccessInviteToken, Guid acceptEmergencyAccessId) + RegisterFinishData registerFinishData, string acceptEmergencyAccessInviteToken, Guid acceptEmergencyAccessId) { // Arrange sutProvider.GetDependency() @@ -816,7 +817,7 @@ public async Task RegisterUserViaAcceptEmergencyAccessInviteToken_DisabledOpenRe // Act & Assert var result = await Assert.ThrowsAsync(() => - sutProvider.Sut.RegisterUserViaAcceptEmergencyAccessInviteToken(user, masterPasswordHash, acceptEmergencyAccessInviteToken, acceptEmergencyAccessId)); + sutProvider.Sut.RegisterUserViaAcceptEmergencyAccessInviteToken(user, registerFinishData, acceptEmergencyAccessInviteToken, acceptEmergencyAccessId)); Assert.Equal("Open registration has been disabled by the system administrator.", result.Message); } @@ -827,7 +828,7 @@ public async Task RegisterUserViaAcceptEmergencyAccessInviteToken_DisabledOpenRe [Theory] [BitAutoData] public async Task RegisterUserViaProviderInviteToken_Succeeds(SutProvider sutProvider, - User user, string masterPasswordHash, Guid providerUserId) + User user, RegisterFinishData registerFinishData, Guid providerUserId) { // Arrange user.Email = $"test+{Guid.NewGuid()}@example.com"; @@ -859,7 +860,7 @@ public async Task RegisterUserViaProviderInviteToken_Succeeds(SutProvider() - .CreateUserAsync(user, masterPasswordHash) + .CreateUserAsync(user, registerFinishData) .Returns(IdentityResult.Success); // Using sutProvider in the parameters of the function means that the constructor has already run for the @@ -867,14 +868,14 @@ public async Task RegisterUserViaProviderInviteToken_Succeeds(SutProvider() .Received(1) - .CreateUserAsync(Arg.Is(u => u.Name == user.Name && u.EmailVerified == true && u.ApiKey != null), masterPasswordHash); + .CreateUserAsync(Arg.Is(u => u.Name == user.Name && u.EmailVerified == true && u.ApiKey != null), registerFinishData); await sutProvider.GetDependency() .Received(1) @@ -884,7 +885,7 @@ await sutProvider.GetDependency() [Theory] [BitAutoData] public async Task RegisterUserViaProviderInviteToken_InvalidToken_ThrowsBadRequestException(SutProvider sutProvider, - User user, string masterPasswordHash, Guid providerUserId) + User user, RegisterFinishData registerFinishData, Guid providerUserId) { // Arrange user.Email = $"test+{Guid.NewGuid()}@example.com"; @@ -921,14 +922,14 @@ public async Task RegisterUserViaProviderInviteToken_InvalidToken_ThrowsBadReque // Act & Assert var result = await Assert.ThrowsAsync(() => - sutProvider.Sut.RegisterUserViaProviderInviteToken(user, masterPasswordHash, base64EncodedProviderInvToken, Guid.NewGuid())); + sutProvider.Sut.RegisterUserViaProviderInviteToken(user, registerFinishData, base64EncodedProviderInvToken, Guid.NewGuid())); Assert.Equal("Invalid provider invite token.", result.Message); } [Theory] [BitAutoData] public async Task RegisterUserViaProviderInviteToken_DisabledOpenRegistration_ThrowsBadRequestException(SutProvider sutProvider, - User user, string masterPasswordHash, Guid providerUserId) + User user, RegisterFinishData registerFinishData, Guid providerUserId) { // Arrange // Start with plaintext @@ -959,7 +960,7 @@ public async Task RegisterUserViaProviderInviteToken_DisabledOpenRegistration_Th // Act & Assert var result = await Assert.ThrowsAsync(() => - sutProvider.Sut.RegisterUserViaProviderInviteToken(user, masterPasswordHash, base64EncodedProviderInvToken, providerUserId)); + sutProvider.Sut.RegisterUserViaProviderInviteToken(user, registerFinishData, base64EncodedProviderInvToken, providerUserId)); Assert.Equal("Open registration has been disabled by the system administrator.", result.Message); } @@ -1065,7 +1066,7 @@ await sutProvider.GetDependency() [Theory] [BitAutoData] public async Task RegisterUserViaEmailVerificationToken_BlockedDomain_ThrowsBadRequestException( - SutProvider sutProvider, User user, string masterPasswordHash, + SutProvider sutProvider, User user, RegisterFinishData registerFinishData, string emailVerificationToken, bool receiveMarketingMaterials) { // Arrange @@ -1089,14 +1090,14 @@ public async Task RegisterUserViaEmailVerificationToken_BlockedDomain_ThrowsBadR // Act & Assert var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.RegisterUserViaEmailVerificationToken(user, masterPasswordHash, emailVerificationToken)); + sutProvider.Sut.RegisterUserViaEmailVerificationToken(user, registerFinishData, emailVerificationToken)); Assert.Equal("This email address is claimed by an organization using Bitwarden.", exception.Message); } [Theory] [BitAutoData] public async Task RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken_BlockedDomain_ThrowsBadRequestException( - SutProvider sutProvider, User user, string masterPasswordHash, + SutProvider sutProvider, User user, RegisterFinishData registerFinishData, string orgSponsoredFreeFamilyPlanInviteToken) { // Arrange @@ -1116,14 +1117,14 @@ public async Task RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken_ // Act & Assert var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(user, masterPasswordHash, orgSponsoredFreeFamilyPlanInviteToken)); + sutProvider.Sut.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(user, registerFinishData, orgSponsoredFreeFamilyPlanInviteToken)); Assert.Equal("This email address is claimed by an organization using Bitwarden.", exception.Message); } [Theory] [BitAutoData] public async Task RegisterUserViaAcceptEmergencyAccessInviteToken_BlockedDomain_ThrowsBadRequestException( - SutProvider sutProvider, User user, string masterPasswordHash, + SutProvider sutProvider, User user, RegisterFinishData registerFinishData, EmergencyAccessEntity emergencyAccess, string acceptEmergencyAccessInviteToken, Guid acceptEmergencyAccessId) { // Arrange @@ -1149,14 +1150,14 @@ public async Task RegisterUserViaAcceptEmergencyAccessInviteToken_BlockedDomain_ // Act & Assert var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.RegisterUserViaAcceptEmergencyAccessInviteToken(user, masterPasswordHash, acceptEmergencyAccessInviteToken, acceptEmergencyAccessId)); + sutProvider.Sut.RegisterUserViaAcceptEmergencyAccessInviteToken(user, registerFinishData, acceptEmergencyAccessInviteToken, acceptEmergencyAccessId)); Assert.Equal("This email address is claimed by an organization using Bitwarden.", exception.Message); } [Theory] [BitAutoData] public async Task RegisterUserViaProviderInviteToken_BlockedDomain_ThrowsBadRequestException( - SutProvider sutProvider, User user, string masterPasswordHash, Guid providerUserId) + SutProvider sutProvider, User user, RegisterFinishData registerFinishData, Guid providerUserId) { // Arrange user.Email = "user@blocked-domain.com"; @@ -1197,7 +1198,7 @@ public async Task RegisterUserViaProviderInviteToken_BlockedDomain_ThrowsBadRequ // Act & Assert var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.RegisterUserViaProviderInviteToken(user, masterPasswordHash, base64EncodedProviderInvToken, providerUserId)); + sutProvider.Sut.RegisterUserViaProviderInviteToken(user, registerFinishData, base64EncodedProviderInvToken, providerUserId)); Assert.Equal("This email address is claimed by an organization using Bitwarden.", exception.Message); } @@ -1226,7 +1227,7 @@ public async Task RegisterUser_InvalidEmailFormat_ThrowsBadRequestException( [Theory] [BitAutoData] public async Task RegisterUserViaEmailVerificationToken_InvalidEmailFormat_ThrowsBadRequestException( - SutProvider sutProvider, User user, string masterPasswordHash, + SutProvider sutProvider, User user, RegisterFinishData registerFinishData, string emailVerificationToken, bool receiveMarketingMaterials) { // Arrange @@ -1246,7 +1247,7 @@ public async Task RegisterUserViaEmailVerificationToken_InvalidEmailFormat_Throw // Act & Assert var exception = await Assert.ThrowsAsync(() => - sutProvider.Sut.RegisterUserViaEmailVerificationToken(user, masterPasswordHash, emailVerificationToken)); + sutProvider.Sut.RegisterUserViaEmailVerificationToken(user, registerFinishData, emailVerificationToken)); Assert.Equal("Invalid email address format.", exception.Message); } @@ -1256,7 +1257,7 @@ public async Task SendWelcomeEmail_OrganizationNull_SendsIndividualWelcomeEmail( User user, OrganizationUser orgUser, string orgInviteToken, - string masterPasswordHash, + RegisterFinishData registerFinishData, [Policy(PolicyType.TwoFactorAuthentication, false)] PolicyStatus policy, SutProvider sutProvider) { @@ -1265,7 +1266,7 @@ public async Task SendWelcomeEmail_OrganizationNull_SendsIndividualWelcomeEmail( orgUser.Email = user.Email; sutProvider.GetDependency() - .CreateUserAsync(user, masterPasswordHash) + .CreateUserAsync(user, registerFinishData) .Returns(IdentityResult.Success); sutProvider.GetDependency() @@ -1295,7 +1296,7 @@ public async Task SendWelcomeEmail_OrganizationNull_SendsIndividualWelcomeEmail( }); // Act - var result = await sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, masterPasswordHash, orgInviteToken, orgUser.Id); + var result = await sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, registerFinishData, orgInviteToken, orgUser.Id); // Assert await sutProvider.GetDependency() @@ -1342,7 +1343,7 @@ public async Task GetOrganizationWelcomeEmailDetailsAsync_HappyPath_ReturnsOrgan Organization organization, User user, OrganizationUser orgUser, - string masterPasswordHash, + RegisterFinishData registerFinishData, string orgInviteToken, [Policy(PolicyType.TwoFactorAuthentication, false)] PolicyStatus policy, SutProvider sutProvider) @@ -1353,7 +1354,7 @@ public async Task GetOrganizationWelcomeEmailDetailsAsync_HappyPath_ReturnsOrgan organization.PlanType = PlanType.EnterpriseAnnually; sutProvider.GetDependency() - .CreateUserAsync(user, masterPasswordHash) + .CreateUserAsync(user, registerFinishData) .Returns(IdentityResult.Success); sutProvider.GetDependency() @@ -1383,7 +1384,7 @@ public async Task GetOrganizationWelcomeEmailDetailsAsync_HappyPath_ReturnsOrgan }); // Act - var result = await sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, masterPasswordHash, orgInviteToken, orgUser.Id); + var result = await sutProvider.Sut.RegisterUserViaOrganizationInviteToken(user, registerFinishData, orgInviteToken, orgUser.Id); // Assert Assert.True(result.Succeeded); diff --git a/test/Identity.Test/Controllers/AccountsControllerTests.cs b/test/Identity.Test/Controllers/AccountsControllerTests.cs index 86e461d1551c..0eda69bf72ea 100644 --- a/test/Identity.Test/Controllers/AccountsControllerTests.cs +++ b/test/Identity.Test/Controllers/AccountsControllerTests.cs @@ -24,6 +24,7 @@ using Microsoft.Extensions.Logging; using NSubstitute; using NSubstitute.ReturnsExtensions; +using SignatureKeyPairRequestModelCustomizeAttribute = Bit.Test.Common.AutoFixture.SignatureKeyPairRequestModelCustomizeAttribute; using Xunit; namespace Bit.Identity.Test.Controllers; @@ -325,13 +326,13 @@ await _sendVerificationEmailForRegistrationCommand.Received(1) .Run(email, name, receiveMarketingEmails, null); // fromMarketing gets ignored and null gets passed } - [Theory, BitAutoData] + [Theory, BitAutoData, SignatureKeyPairRequestModelCustomize] public async Task PostRegisterFinish_WhenGivenOrgInvite_ShouldRegisterUser( string email, string masterPasswordHash, string orgInviteToken, Guid organizationUserId, string userSymmetricKey, - KeysRequestModel userAsymmetricKeys) + KeysRequestModel userAsymmetricKeys, AccountKeysRequestModel accountKeys) { // Arrange - var model = new RegisterFinishRequestModel + var legacyModel = new RegisterFinishRequestModel { Email = email, MasterPasswordHash = masterPasswordHash, @@ -343,34 +344,73 @@ public async Task PostRegisterFinish_WhenGivenOrgInvite_ShouldRegisterUser( UserAsymmetricKeys = userAsymmetricKeys }; - var user = model.ToUser(); + var kdfModel = new KdfRequestModel + { + KdfType = KdfType.Argon2id, + Iterations = AuthConstants.ARGON2_ITERATIONS.Default, + }; + + var newModel = new RegisterFinishRequestModel + { + Email = email, + OrgInviteToken = orgInviteToken, + OrganizationUserId = organizationUserId, + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel + { + MasterPasswordAuthenticationHash = masterPasswordHash, + Kdf = kdfModel, + Salt = email.ToLowerInvariant().Trim(), + }, + MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel + { + Kdf = kdfModel, + MasterKeyWrappedUserKey = userSymmetricKey, + Salt = email.ToLowerInvariant().Trim(), + }, + AccountKeys = accountKeys + }; + + var legacyUser = legacyModel.ToUser(false); + var legacyData = legacyModel.ToData(); - _registerUserCommand.RegisterUserViaOrganizationInviteToken(Arg.Any(), masterPasswordHash, orgInviteToken, organizationUserId) + var newUser = newModel.ToUser(true); + var newData = newModel.ToData(); + + _registerUserCommand.RegisterUserViaOrganizationInviteToken(Arg.Any(), legacyData, orgInviteToken, organizationUserId) + .Returns(Task.FromResult(IdentityResult.Success)); + _registerUserCommand.RegisterUserViaOrganizationInviteToken(Arg.Any(), newData, orgInviteToken, organizationUserId) .Returns(Task.FromResult(IdentityResult.Success)); // Act - var result = await _sut.PostRegisterFinish(model); + var legacyResult = await _sut.PostRegisterFinish(legacyModel); + var newResult = await _sut.PostRegisterFinish(newModel); // Assert - Assert.NotNull(result); + Assert.NotNull(legacyResult); + await _registerUserCommand.Received(1).RegisterUserViaOrganizationInviteToken(Arg.Is(u => + u.Email == legacyUser.Email && + u.MasterPasswordHint == legacyUser.MasterPasswordHint && + u.Kdf == legacyUser.Kdf && + u.KdfIterations == legacyUser.KdfIterations && + u.KdfMemory == legacyUser.KdfMemory && + u.KdfParallelism == legacyUser.KdfParallelism && + u.Key == legacyUser.Key + ), legacyData, orgInviteToken, organizationUserId); + + Assert.NotNull(newResult); await _registerUserCommand.Received(1).RegisterUserViaOrganizationInviteToken(Arg.Is(u => - u.Email == user.Email && - u.MasterPasswordHint == user.MasterPasswordHint && - u.Kdf == user.Kdf && - u.KdfIterations == user.KdfIterations && - u.KdfMemory == user.KdfMemory && - u.KdfParallelism == user.KdfParallelism && - u.Key == user.Key - ), masterPasswordHash, orgInviteToken, organizationUserId); + u.Email == newUser.Email && + u.MasterPasswordHint == newUser.MasterPasswordHint + ), newData, orgInviteToken, organizationUserId); } - [Theory, BitAutoData] + [Theory, BitAutoData, SignatureKeyPairRequestModelCustomize] public async Task PostRegisterFinish_OrgInviteDuplicateUser_ThrowsBadRequestException( string email, string masterPasswordHash, string orgInviteToken, Guid organizationUserId, string userSymmetricKey, - KeysRequestModel userAsymmetricKeys) + KeysRequestModel userAsymmetricKeys, AccountKeysRequestModel accountKeys) { // Arrange - var model = new RegisterFinishRequestModel + var legacyModel = new RegisterFinishRequestModel { Email = email, MasterPasswordHash = masterPasswordHash, @@ -382,14 +422,44 @@ public async Task PostRegisterFinish_OrgInviteDuplicateUser_ThrowsBadRequestExce UserAsymmetricKeys = userAsymmetricKeys }; - var user = model.ToUser(); + var kdfModel = new KdfRequestModel + { + KdfType = KdfType.Argon2id, + Iterations = AuthConstants.ARGON2_ITERATIONS.Default + }; + + var newModel = new RegisterFinishRequestModel + { + Email = email, + OrgInviteToken = orgInviteToken, + OrganizationUserId = organizationUserId, + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel + { + MasterPasswordAuthenticationHash = masterPasswordHash, + Kdf = kdfModel, + Salt = email.ToLowerInvariant().Trim(), + }, + MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel + { + Kdf = kdfModel, + MasterKeyWrappedUserKey = userSymmetricKey, + Salt = email.ToLowerInvariant().Trim(), + }, + AccountKeys = accountKeys, + }; + + var legacyUser = legacyModel.ToUser(false); + var legacyData = legacyModel.ToData(); + + var newUser = newModel.ToUser(true); + var newData = newModel.ToData(); // Duplicates throw 2 errors, one for the email and one for the username var duplicateUserNameErrorCode = "DuplicateUserName"; - var duplicateUserNameErrorDesc = $"Username '{user.Email}' is already taken."; + var duplicateUserNameErrorDesc = $"Username '{email}' is already taken."; var duplicateUserEmailErrorCode = "DuplicateEmail"; - var duplicateUserEmailErrorDesc = $"Email '{user.Email}' is already taken."; + var duplicateUserEmailErrorDesc = $"Email '{email}' is already taken."; var failedIdentityResult = IdentityResult.Failed( new IdentityError { Code = duplicateUserNameErrorCode, Description = duplicateUserNameErrorDesc }, @@ -397,35 +467,49 @@ public async Task PostRegisterFinish_OrgInviteDuplicateUser_ThrowsBadRequestExce ); _registerUserCommand.RegisterUserViaOrganizationInviteToken(Arg.Is(u => - u.Email == user.Email && - u.MasterPasswordHint == user.MasterPasswordHint && - u.Kdf == user.Kdf && - u.KdfIterations == user.KdfIterations && - u.KdfMemory == user.KdfMemory && - u.KdfParallelism == user.KdfParallelism && - u.Key == user.Key - ), masterPasswordHash, orgInviteToken, organizationUserId) + u.Email == legacyUser.Email && + u.MasterPasswordHint == legacyUser.MasterPasswordHint && + u.Kdf == legacyUser.Kdf && + u.KdfIterations == legacyUser.KdfIterations && + u.KdfMemory == legacyUser.KdfMemory && + u.KdfParallelism == legacyUser.KdfParallelism && + u.Key == legacyUser.Key + ), legacyData, orgInviteToken, organizationUserId) + .Returns(Task.FromResult(failedIdentityResult)); + + _registerUserCommand.RegisterUserViaOrganizationInviteToken(Arg.Is(u => + u.Email == newUser.Email && + u.MasterPasswordHint == newUser.MasterPasswordHint + ), newData, orgInviteToken, organizationUserId) .Returns(Task.FromResult(failedIdentityResult)); // Act - var exception = await Assert.ThrowsAsync(() => _sut.PostRegisterFinish(model)); + var legacyException = await Assert.ThrowsAsync(() => _sut.PostRegisterFinish(legacyModel)); + var newException = await Assert.ThrowsAsync(() => _sut.PostRegisterFinish(newModel)); // We filter out the duplicate username error // so we should only see the duplicate email error - Assert.Equal(1, exception.ModelState.ErrorCount); - exception.ModelState.TryGetValue(string.Empty, out var modelStateEntry); - Assert.NotNull(modelStateEntry); - var modelError = modelStateEntry.Errors.First(); - Assert.Equal(duplicateUserEmailErrorDesc, modelError.ErrorMessage); + Assert.Equal(2, legacyException.ModelState.ErrorCount); + legacyException.ModelState.TryGetValue(string.Empty, out var legacyModelStateEntry); + Assert.NotNull(legacyModelStateEntry); + var legacyModelError = legacyModelStateEntry.Errors.First(); + Assert.Equal(duplicateUserEmailErrorDesc, legacyModelError.ErrorMessage); + + // TODO PM-27326 decrease back to 1 once legacy testing is removed + Assert.Equal(2, newException.ModelState.ErrorCount); + newException.ModelState.TryGetValue(string.Empty, out var newModelStateEntry); + Assert.NotNull(newModelStateEntry); + var newModelError = newModelStateEntry.Errors.First(); + Assert.Equal(duplicateUserEmailErrorDesc, newModelError.ErrorMessage); } - [Theory, BitAutoData] + [Theory, BitAutoData, SignatureKeyPairRequestModelCustomize] public async Task PostRegisterFinish_WhenGivenEmailVerificationToken_ShouldRegisterUser( string email, string masterPasswordHash, string emailVerificationToken, string userSymmetricKey, - KeysRequestModel userAsymmetricKeys) + KeysRequestModel userAsymmetricKeys, AccountKeysRequestModel accountKeys) { // Arrange - var model = new RegisterFinishRequestModel + var legacyModel = new RegisterFinishRequestModel { Email = email, MasterPasswordHash = masterPasswordHash, @@ -436,34 +520,72 @@ public async Task PostRegisterFinish_WhenGivenEmailVerificationToken_ShouldRegis UserAsymmetricKeys = userAsymmetricKeys }; - var user = model.ToUser(); + var kdfModel = new KdfRequestModel + { + KdfType = KdfType.Argon2id, + Iterations = AuthConstants.ARGON2_ITERATIONS.Default + }; + + var newModel = new RegisterFinishRequestModel + { + Email = email, + EmailVerificationToken = emailVerificationToken, + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel + { + MasterPasswordAuthenticationHash = masterPasswordHash, + Kdf = kdfModel, + Salt = email.ToLowerInvariant().Trim(), + }, + MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel + { + Kdf = kdfModel, + MasterKeyWrappedUserKey = userSymmetricKey, + Salt = email.ToLowerInvariant().Trim(), + }, + AccountKeys = accountKeys, + }; + + var legacyUser = legacyModel.ToUser(false); + var legacyData = legacyModel.ToData(); + + var newUser = newModel.ToUser(true); + var newData = newModel.ToData(); - _registerUserCommand.RegisterUserViaEmailVerificationToken(Arg.Any(), masterPasswordHash, emailVerificationToken) + _registerUserCommand.RegisterUserViaEmailVerificationToken(Arg.Any(), legacyData, emailVerificationToken) + .Returns(Task.FromResult(IdentityResult.Success)); + _registerUserCommand.RegisterUserViaEmailVerificationToken(Arg.Any(), newData, emailVerificationToken) .Returns(Task.FromResult(IdentityResult.Success)); // Act - var result = await _sut.PostRegisterFinish(model); + var legacyResult = await _sut.PostRegisterFinish(legacyModel); + var newResult = await _sut.PostRegisterFinish(newModel); // Assert - Assert.NotNull(result); + Assert.NotNull(legacyResult); await _registerUserCommand.Received(1).RegisterUserViaEmailVerificationToken(Arg.Is(u => - u.Email == user.Email && - u.MasterPasswordHint == user.MasterPasswordHint && - u.Kdf == user.Kdf && - u.KdfIterations == user.KdfIterations && - u.KdfMemory == user.KdfMemory && - u.KdfParallelism == user.KdfParallelism && - u.Key == user.Key - ), masterPasswordHash, emailVerificationToken); + u.Email == legacyUser.Email && + u.MasterPasswordHint == legacyUser.MasterPasswordHint && + u.Kdf == legacyUser.Kdf && + u.KdfIterations == legacyUser.KdfIterations && + u.KdfMemory == legacyUser.KdfMemory && + u.KdfParallelism == legacyUser.KdfParallelism && + u.Key == legacyUser.Key + ), legacyData, emailVerificationToken); + + Assert.NotNull(newResult); + await _registerUserCommand.Received(1).RegisterUserViaEmailVerificationToken(Arg.Is(u => + u.Email == newUser.Email && + u.MasterPasswordHint == newUser.MasterPasswordHint + ), newData, emailVerificationToken); } - [Theory, BitAutoData] + [Theory, BitAutoData, SignatureKeyPairRequestModelCustomize] public async Task PostRegisterFinish_WhenGivenEmailVerificationTokenDuplicateUser_ThrowsBadRequestException( string email, string masterPasswordHash, string emailVerificationToken, string userSymmetricKey, - KeysRequestModel userAsymmetricKeys) + KeysRequestModel userAsymmetricKeys, AccountKeysRequestModel accountKeys) { // Arrange - var model = new RegisterFinishRequestModel + var legacyModel = new RegisterFinishRequestModel { Email = email, MasterPasswordHash = masterPasswordHash, @@ -474,14 +596,43 @@ public async Task PostRegisterFinish_WhenGivenEmailVerificationTokenDuplicateUse UserAsymmetricKeys = userAsymmetricKeys }; - var user = model.ToUser(); + var kdfModel = new KdfRequestModel + { + KdfType = KdfType.Argon2id, + Iterations = AuthConstants.ARGON2_ITERATIONS.Default + }; + + var newModel = new RegisterFinishRequestModel + { + Email = email, + EmailVerificationToken = emailVerificationToken, + MasterPasswordAuthentication = new MasterPasswordAuthenticationDataRequestModel + { + MasterPasswordAuthenticationHash = masterPasswordHash, + Kdf = kdfModel, + Salt = email.ToLowerInvariant().Trim(), + }, + MasterPasswordUnlock = new MasterPasswordUnlockDataRequestModel + { + Kdf = kdfModel, + MasterKeyWrappedUserKey = userSymmetricKey, + Salt = email.ToLowerInvariant().Trim(), + }, + AccountKeys = accountKeys, + }; + + var legacyUser = legacyModel.ToUser(false); + var legacyData = legacyModel.ToData(); + + var newUser = newModel.ToUser(true); + var newData = newModel.ToData(); // Duplicates throw 2 errors, one for the email and one for the username var duplicateUserNameErrorCode = "DuplicateUserName"; - var duplicateUserNameErrorDesc = $"Username '{user.Email}' is already taken."; + var duplicateUserNameErrorDesc = $"Username '{email}' is already taken."; var duplicateUserEmailErrorCode = "DuplicateEmail"; - var duplicateUserEmailErrorDesc = $"Email '{user.Email}' is already taken."; + var duplicateUserEmailErrorDesc = $"Email '{email}' is already taken."; var failedIdentityResult = IdentityResult.Failed( new IdentityError { Code = duplicateUserNameErrorCode, Description = duplicateUserNameErrorDesc }, @@ -489,26 +640,40 @@ public async Task PostRegisterFinish_WhenGivenEmailVerificationTokenDuplicateUse ); _registerUserCommand.RegisterUserViaEmailVerificationToken(Arg.Is(u => - u.Email == user.Email && - u.MasterPasswordHint == user.MasterPasswordHint && - u.Kdf == user.Kdf && - u.KdfIterations == user.KdfIterations && - u.KdfMemory == user.KdfMemory && - u.KdfParallelism == user.KdfParallelism && - u.Key == user.Key - ), masterPasswordHash, emailVerificationToken) + u.Email == legacyUser.Email && + u.MasterPasswordHint == legacyUser.MasterPasswordHint && + u.Kdf == legacyUser.Kdf && + u.KdfIterations == legacyUser.KdfIterations && + u.KdfMemory == legacyUser.KdfMemory && + u.KdfParallelism == legacyUser.KdfParallelism && + u.Key == legacyUser.Key + ), legacyData, emailVerificationToken) + .Returns(Task.FromResult(failedIdentityResult)); + + _registerUserCommand.RegisterUserViaEmailVerificationToken(Arg.Is(u => + u.Email == newUser.Email && + u.MasterPasswordHint == newUser.MasterPasswordHint + ), newData, emailVerificationToken) .Returns(Task.FromResult(failedIdentityResult)); // Act - var exception = await Assert.ThrowsAsync(() => _sut.PostRegisterFinish(model)); + var legacyException = await Assert.ThrowsAsync(() => _sut.PostRegisterFinish(legacyModel)); + var newException = await Assert.ThrowsAsync(() => _sut.PostRegisterFinish(newModel)); // We filter out the duplicate username error // so we should only see the duplicate email error - Assert.Equal(1, exception.ModelState.ErrorCount); - exception.ModelState.TryGetValue(string.Empty, out var modelStateEntry); - Assert.NotNull(modelStateEntry); - var modelError = modelStateEntry.Errors.First(); - Assert.Equal(duplicateUserEmailErrorDesc, modelError.ErrorMessage); + Assert.Equal(2, legacyException.ModelState.ErrorCount); + legacyException.ModelState.TryGetValue(string.Empty, out var legacyModelStateEntry); + Assert.NotNull(legacyModelStateEntry); + var legacyModelError = legacyModelStateEntry.Errors.First(); + Assert.Equal(duplicateUserEmailErrorDesc, legacyModelError.ErrorMessage); + + // TODO PM-27326 decrease back to 1 once legacy testing is removed + Assert.Equal(2, newException.ModelState.ErrorCount); + newException.ModelState.TryGetValue(string.Empty, out var newModelStateEntry); + Assert.NotNull(newModelStateEntry); + var newModelError = newModelStateEntry.Errors.First(); + Assert.Equal(duplicateUserEmailErrorDesc, newModelError.ErrorMessage); } @@ -594,14 +759,15 @@ public async Task PostRegisterVerificationEmailClicked_WhenTokenIsValidButExisti // PM-28143 - When removing the old properties, update this test to just test the new properties working // as expected. - [Theory, BitAutoData] + [Theory, BitAutoData, SignatureKeyPairRequestModelCustomize] public async Task PostRegisterFinish_EmailVerification_BothDataForms_ProduceEquivalentOutcomes( string email, string emailVerificationToken, string masterPasswordHash, string masterKeyWrappedUserKey, string publicKey, - string encryptedPrivateKey) + string encryptedPrivateKey, + AccountKeysRequestModel accountKeys) { // Arrange: new-form model (MasterPasswordAuthenticationData + MasterPasswordUnlockData) @@ -629,11 +795,7 @@ public async Task PostRegisterFinish_EmailVerification_BothDataForms_ProduceEqui MasterKeyWrappedUserKey = masterKeyWrappedUserKey, Salt = email }, - UserAsymmetricKeys = new KeysRequestModel - { - PublicKey = publicKey, - EncryptedPrivateKey = encryptedPrivateKey - } + AccountKeys = accountKeys, }; // Arrange: legacy-form model (MasterPasswordHash + legacy KDF + UserSymmetricKey) @@ -654,11 +816,18 @@ public async Task PostRegisterFinish_EmailVerification_BothDataForms_ProduceEqui } }; - var newUser = newModel.ToUser(); - var legacyUser = legacyModel.ToUser(); + var newUser = newModel.ToUser(true); + var newData = newModel.ToData(); + + var legacyUser = legacyModel.ToUser(false); + var legacyData = legacyModel.ToData(); _registerUserCommand - .RegisterUserViaEmailVerificationToken(Arg.Any(), masterPasswordHash, emailVerificationToken) + .RegisterUserViaEmailVerificationToken(Arg.Any(), legacyData, emailVerificationToken) + .Returns(Task.FromResult(IdentityResult.Success)); + + _registerUserCommand + .RegisterUserViaEmailVerificationToken(Arg.Any(), newData, emailVerificationToken) .Returns(Task.FromResult(IdentityResult.Success)); // Act: call with new form @@ -673,16 +842,9 @@ public async Task PostRegisterFinish_EmailVerification_BothDataForms_ProduceEqui // Assert: effective users are equivalent Assert.Equal(legacyUser.Email, newUser.Email); Assert.Equal(legacyUser.MasterPasswordHint, newUser.MasterPasswordHint); - Assert.Equal(legacyUser.Kdf, newUser.Kdf); - Assert.Equal(legacyUser.KdfIterations, newUser.KdfIterations); - Assert.Equal(legacyUser.KdfMemory, newUser.KdfMemory); - Assert.Equal(legacyUser.KdfParallelism, newUser.KdfParallelism); - Assert.Equal(legacyUser.Key, newUser.Key); - Assert.Equal(legacyUser.PublicKey, newUser.PublicKey); - Assert.Equal(legacyUser.PrivateKey, newUser.PrivateKey); // Assert: hash forwarded identically from both inputs - await _registerUserCommand.Received(2).RegisterUserViaEmailVerificationToken( + await _registerUserCommand.Received(1).RegisterUserViaEmailVerificationToken( Arg.Is(u => u.Email == newUser.Email && u.Kdf == newUser.Kdf && @@ -690,10 +852,10 @@ await _registerUserCommand.Received(2).RegisterUserViaEmailVerificationToken( u.KdfMemory == newUser.KdfMemory && u.KdfParallelism == newUser.KdfParallelism && u.Key == newUser.Key), - masterPasswordHash, + newData, emailVerificationToken); - await _registerUserCommand.Received(2).RegisterUserViaEmailVerificationToken( + await _registerUserCommand.Received(1).RegisterUserViaEmailVerificationToken( Arg.Is(u => u.Email == legacyUser.Email && u.Kdf == legacyUser.Kdf && @@ -701,13 +863,13 @@ await _registerUserCommand.Received(2).RegisterUserViaEmailVerificationToken( u.KdfMemory == legacyUser.KdfMemory && u.KdfParallelism == legacyUser.KdfParallelism && u.Key == legacyUser.Key), - masterPasswordHash, + legacyData, emailVerificationToken); } // PM-28143 - When removing the old properties, update this test to just test the new properties working // as expected. - [Theory, BitAutoData] + [Theory, BitAutoData, SignatureKeyPairRequestModelCustomize] public async Task PostRegisterFinish_OrgInvite_BothDataForms_ProduceEquivalentOutcomes( string email, string orgInviteToken, @@ -715,7 +877,8 @@ public async Task PostRegisterFinish_OrgInvite_BothDataForms_ProduceEquivalentOu string masterPasswordHash, string masterKeyWrappedUserKey, string publicKey, - string encryptedPrivateKey) + string encryptedPrivateKey, + AccountKeysRequestModel accountKeys) { var kdfData = new KdfRequestModel { @@ -743,11 +906,7 @@ public async Task PostRegisterFinish_OrgInvite_BothDataForms_ProduceEquivalentOu MasterKeyWrappedUserKey = masterKeyWrappedUserKey, Salt = email }, - UserAsymmetricKeys = new KeysRequestModel - { - PublicKey = publicKey, - EncryptedPrivateKey = encryptedPrivateKey - } + AccountKeys = accountKeys }; // Arrange: legacy-form model (MasterPasswordHash + legacy KDF + UserSymmetricKey) @@ -769,11 +928,18 @@ public async Task PostRegisterFinish_OrgInvite_BothDataForms_ProduceEquivalentOu } }; - var newUser = newModel.ToUser(); - var legacyUser = legacyModel.ToUser(); + var newUser = newModel.ToUser(true); + var newData = newModel.ToData(); + + var legacyUser = legacyModel.ToUser(false); + var legacyData = legacyModel.ToData(); _registerUserCommand - .RegisterUserViaOrganizationInviteToken(Arg.Any(), masterPasswordHash, orgInviteToken, organizationUserId) + .RegisterUserViaOrganizationInviteToken(Arg.Any(), newData, orgInviteToken, organizationUserId) + .Returns(Task.FromResult(IdentityResult.Success)); + + _registerUserCommand + .RegisterUserViaOrganizationInviteToken(Arg.Any(), legacyData, orgInviteToken, organizationUserId) .Returns(Task.FromResult(IdentityResult.Success)); // Act @@ -787,16 +953,9 @@ public async Task PostRegisterFinish_OrgInvite_BothDataForms_ProduceEquivalentOu // Assert: effective users are equivalent Assert.Equal(legacyUser.Email, newUser.Email); Assert.Equal(legacyUser.MasterPasswordHint, newUser.MasterPasswordHint); - Assert.Equal(legacyUser.Kdf, newUser.Kdf); - Assert.Equal(legacyUser.KdfIterations, newUser.KdfIterations); - Assert.Equal(legacyUser.KdfMemory, newUser.KdfMemory); - Assert.Equal(legacyUser.KdfParallelism, newUser.KdfParallelism); - Assert.Equal(legacyUser.Key, newUser.Key); - Assert.Equal(legacyUser.PublicKey, newUser.PublicKey); - Assert.Equal(legacyUser.PrivateKey, newUser.PrivateKey); // Assert: hash forwarded identically from both inputs - await _registerUserCommand.Received(2).RegisterUserViaOrganizationInviteToken( + await _registerUserCommand.Received(1).RegisterUserViaOrganizationInviteToken( Arg.Is(u => u.Email == newUser.Email && u.Kdf == newUser.Kdf && @@ -804,11 +963,11 @@ await _registerUserCommand.Received(2).RegisterUserViaOrganizationInviteToken( u.KdfMemory == newUser.KdfMemory && u.KdfParallelism == newUser.KdfParallelism && u.Key == newUser.Key), - masterPasswordHash, + newData, orgInviteToken, organizationUserId); - await _registerUserCommand.Received(2).RegisterUserViaOrganizationInviteToken( + await _registerUserCommand.Received(1).RegisterUserViaOrganizationInviteToken( Arg.Is(u => u.Email == legacyUser.Email && u.Kdf == legacyUser.Kdf && @@ -816,20 +975,19 @@ await _registerUserCommand.Received(2).RegisterUserViaOrganizationInviteToken( u.KdfMemory == legacyUser.KdfMemory && u.KdfParallelism == legacyUser.KdfParallelism && u.Key == legacyUser.Key), - masterPasswordHash, + legacyData, orgInviteToken, organizationUserId); } - [Theory, BitAutoData] + [Theory, BitAutoData, SignatureKeyPairRequestModelCustomize] public async Task PostRegisterFinish_NewForm_UsesUnlockDataForKdfAndKey_WhenRootFieldsNull( string email, string emailVerificationToken, string masterPasswordHash, string masterKeyWrappedUserKey, int iterations, - string publicKey, - string encryptedPrivateKey) + AccountKeysRequestModel accountKeys) { // Arrange: Provide only unlock-data KDF + key; leave root KDF fields null var unlockKdf = new KdfRequestModel @@ -858,15 +1016,13 @@ public async Task PostRegisterFinish_NewForm_UsesUnlockDataForKdfAndKey_WhenRoot // root KDF fields intentionally null Kdf = null, KdfIterations = null, - UserAsymmetricKeys = new KeysRequestModel - { - PublicKey = publicKey, - EncryptedPrivateKey = encryptedPrivateKey - } + AccountKeys = accountKeys }; + var data = model.ToData(); + _registerUserCommand - .RegisterUserViaEmailVerificationToken(Arg.Any(), masterPasswordHash, emailVerificationToken) + .RegisterUserViaEmailVerificationToken(Arg.Any(), data, emailVerificationToken) .Returns(Task.FromResult(IdentityResult.Success)); // Act @@ -874,12 +1030,8 @@ public async Task PostRegisterFinish_NewForm_UsesUnlockDataForKdfAndKey_WhenRoot // Assert: The user passed to command uses unlock-data values await _registerUserCommand.Received(1).RegisterUserViaEmailVerificationToken( - Arg.Is(u => - u.Email == email && - u.Kdf == unlockKdf.KdfType && - u.KdfIterations == unlockKdf.Iterations && - u.Key == masterKeyWrappedUserKey), - masterPasswordHash, + Arg.Is(u => u.Email == email), + data, emailVerificationToken); } @@ -909,8 +1061,10 @@ public async Task PostRegisterFinish_LegacyForm_UsesRootFields_WhenUnlockDataNul } }; + var data = model.ToData(); + _registerUserCommand - .RegisterUserViaEmailVerificationToken(Arg.Any(), masterPasswordHash, emailVerificationToken) + .RegisterUserViaEmailVerificationToken(Arg.Any(), data, emailVerificationToken) .Returns(Task.FromResult(IdentityResult.Success)); // Act @@ -923,7 +1077,7 @@ await _registerUserCommand.Received(1).RegisterUserViaEmailVerificationToken( u.Kdf == KdfType.PBKDF2_SHA256 && u.KdfIterations == AuthConstants.PBKDF2_ITERATIONS.Default && u.Key == legacyKey), - masterPasswordHash, + data, emailVerificationToken); } diff --git a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs index e190dda42715..4d961b9b0b2f 100644 --- a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs +++ b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs @@ -261,6 +261,53 @@ public async Task RegisterNewIdentityFactoryUserAsync( }; } + if (requestModel.AccountKeys != null) + { + var keys = requestModel.AccountKeys; + var encKeyPair = keys.PublicKeyEncryptionKeyPair; + var sigKeyPair = keys.SignatureKeyPair; + var securityState = keys.SecurityState; + + // enforce V2 encryption rules for AccountKeys structure + // all v2 parameters must be provided, or none + if (encKeyPair == null || sigKeyPair == null || securityState == null) + { + encKeyPair = null; + sigKeyPair = null; + securityState = null; + } + + if (encKeyPair != null) + { + encKeyPair = new PublicKeyEncryptionKeyPairRequestModel + { + WrappedPrivateKey = DefaultEncryptedString, + PublicKey = keys.PublicKeyEncryptionKeyPair.PublicKey, + SignedPublicKey = keys.PublicKeyEncryptionKeyPair.SignedPublicKey, + }; + } + + if (sigKeyPair != null) + { + sigKeyPair = new SignatureKeyPairRequestModel + { + SignatureAlgorithm = "ed25519", + WrappedSigningKey = DefaultEncryptedString, + VerifyingKey = keys.SignatureKeyPair.VerifyingKey, + }; + } + + // Force valid signature algorithm and encrypted strings to avoid model validation failure. + requestModel.AccountKeys = new AccountKeysRequestModel + { + UserKeyEncryptedAccountPrivateKey = DefaultEncryptedString, + AccountPublicKey = keys.AccountPublicKey, + PublicKeyEncryptionKeyPair = encKeyPair, + SignatureKeyPair = sigKeyPair, + SecurityState = securityState, + }; + } + var sendVerificationEmailReqModel = new RegisterSendVerificationEmailRequestModel { Email = requestModel.Email, From a247343a4602b5375d01da362fd4f6eaccc95bc7 Mon Sep 17 00:00:00 2001 From: Eli Grubb Date: Tue, 3 Feb 2026 23:59:01 -0700 Subject: [PATCH 39/44] Fixed lint and build errors. --- .../Accounts/RegisterFinishRequestModel.cs | 16 +++++----- .../Data/MasterPasswordAuthenticationData.cs | 2 +- .../Models/Data/MasterPasswordUnlockData.cs | 2 +- .../Models/Data/RegisterFinishData.cs | 6 ++-- .../Models/Data/UserAccountKeysData.cs | 2 +- src/Core/Repositories/IUserRepository.cs | 2 +- .../SignatureKeyPairRequestModelFixtures.cs | 4 +-- .../RegisterFinishRequestModelTests.cs | 6 ++-- .../Registration/RegisterUserCommandTests.cs | 2 +- .../Controllers/AccountsControllerTests.cs | 10 +++--- .../Factories/IdentityApplicationFactory.cs | 2 +- ...1-30_00_User_SetRegisterFinishUserData.sql | 31 +++++++++++++++++++ 12 files changed, 58 insertions(+), 27 deletions(-) create mode 100644 util/Migrator/DbScripts/2026-01-30_00_User_SetRegisterFinishUserData.sql diff --git a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs index a8e65709e394..6ceede492eaf 100644 --- a/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs +++ b/src/Core/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModel.cs @@ -105,13 +105,13 @@ public RegisterFinishData ToData() // https://bitwarden.atlassian.net/browse/PM-27326 return new RegisterFinishData { - MasterPasswordUnlockData = MasterPasswordUnlock?.ToData() ?? + MasterPasswordUnlockData = MasterPasswordUnlock?.ToData() ?? new MasterPasswordUnlockData { Kdf = new KdfSettings { KdfType = Kdf ?? throw new Exception("KdfType couldn't be found on either the MasterPasswordUnlockData or the Kdf property passed in."), - Iterations = KdfIterations ?? throw new Exception("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in."), + Iterations = KdfIterations ?? throw new Exception("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in."), // KdfMemory and KdfParallelism are optional (only used for Argon2id) Memory = KdfMemory, Parallelism = KdfParallelism, @@ -120,14 +120,14 @@ public RegisterFinishData ToData() // PM-28827 To be added when MasterPasswordSalt is added to the user column Salt = Email.ToLowerInvariant().Trim(), }, - UserAccountKeysData = AccountKeys?.ToAccountKeysData() ?? + UserAccountKeysData = AccountKeys?.ToAccountKeysData() ?? new UserAccountKeysData { PublicKeyEncryptionKeyPairData = new PublicKeyEncryptionKeyPairData ( - UserAsymmetricKeys?.EncryptedPrivateKey ?? - throw new Exception("WrappedPrivateKey couldn't be found in either AccountKeys or UserAsymmetricKeys."), - UserAsymmetricKeys?.PublicKey ?? + UserAsymmetricKeys?.EncryptedPrivateKey ?? + throw new Exception("WrappedPrivateKey couldn't be found in either AccountKeys or UserAsymmetricKeys."), + UserAsymmetricKeys?.PublicKey ?? throw new Exception("PublicKey couldn't be found in either AccountKeys or UserAsymmetricKeys") ), }, @@ -137,7 +137,7 @@ public RegisterFinishData ToData() Kdf = new KdfSettings { KdfType = Kdf ?? throw new Exception("KdfType couldn't be found on either the MasterPasswordUnlockData or the Kdf property passed in."), - Iterations = KdfIterations ?? throw new Exception("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in."), + Iterations = KdfIterations ?? throw new Exception("KdfIterations couldn't be found on either the MasterPasswordUnlockData or the KdfIterations property passed in."), // KdfMemory and KdfParallelism are optional (only used for Argon2id) Memory = KdfMemory, Parallelism = KdfParallelism, @@ -257,7 +257,7 @@ public IEnumerable Validate(ValidationContext validationContex if (AccountKeys == null && UserAsymmetricKeys == null) { yield return new ValidationResult( - $"{nameof(AccountKeys.PublicKeyEncryptionKeyPair.PublicKey)} and {nameof(AccountKeys.PublicKeyEncryptionKeyPair.WrappedPrivateKey)} not found in RequestModel", + $"{nameof(AccountKeys.PublicKeyEncryptionKeyPair.PublicKey)} and {nameof(AccountKeys.PublicKeyEncryptionKeyPair.WrappedPrivateKey)} not found in RequestModel", [nameof(AccountKeys.PublicKeyEncryptionKeyPair.PublicKey), nameof(AccountKeys.PublicKeyEncryptionKeyPair.WrappedPrivateKey)]); } diff --git a/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs b/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs index 8e3899eeb609..ae95bf6ad4a5 100644 --- a/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs +++ b/src/Core/KeyManagement/Models/Data/MasterPasswordAuthenticationData.cs @@ -1,4 +1,4 @@ -using Bit.Core.Entities; +using Bit.Core.Entities; using Bit.Core.Exceptions; using Bit.Core.KeyManagement.Models.Api.Request; diff --git a/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs b/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs index d63bbe9dd80d..3de075a7355c 100644 --- a/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs +++ b/src/Core/KeyManagement/Models/Data/MasterPasswordUnlockData.cs @@ -1,4 +1,4 @@ -using Bit.Core.Entities; +using Bit.Core.Entities; using Bit.Core.Exceptions; using Bit.Core.KeyManagement.Models.Api.Request; diff --git a/src/Core/KeyManagement/Models/Data/RegisterFinishData.cs b/src/Core/KeyManagement/Models/Data/RegisterFinishData.cs index c1c28f21c33b..35b4202aff49 100644 --- a/src/Core/KeyManagement/Models/Data/RegisterFinishData.cs +++ b/src/Core/KeyManagement/Models/Data/RegisterFinishData.cs @@ -1,10 +1,10 @@ -namespace Bit.Core.KeyManagement.Models.Data; +namespace Bit.Core.KeyManagement.Models.Data; public class RegisterFinishData { public required MasterPasswordUnlockData MasterPasswordUnlockData { get; set; } public required UserAccountKeysData UserAccountKeysData { get; set; } - public required MasterPasswordAuthenticationData MasterPasswordAuthenticationData {get; set; } + public required MasterPasswordAuthenticationData MasterPasswordAuthenticationData { get; set; } public bool IsV2Encryption() { @@ -28,4 +28,4 @@ public override int GetHashCode() { return HashCode.Combine(MasterPasswordUnlockData, UserAccountKeysData, MasterPasswordAuthenticationData); } -} \ No newline at end of file +} diff --git a/src/Core/KeyManagement/Models/Data/UserAccountKeysData.cs b/src/Core/KeyManagement/Models/Data/UserAccountKeysData.cs index cf687f469577..0bd45916bc3f 100644 --- a/src/Core/KeyManagement/Models/Data/UserAccountKeysData.cs +++ b/src/Core/KeyManagement/Models/Data/UserAccountKeysData.cs @@ -1,4 +1,4 @@ -namespace Bit.Core.KeyManagement.Models.Data; +namespace Bit.Core.KeyManagement.Models.Data; /// /// Represents an expanded account cryptographic state for a user. Expanded here means diff --git a/src/Core/Repositories/IUserRepository.cs b/src/Core/Repositories/IUserRepository.cs index 950df1187e89..01f9087e9af5 100644 --- a/src/Core/Repositories/IUserRepository.cs +++ b/src/Core/Repositories/IUserRepository.cs @@ -92,7 +92,7 @@ UpdateUserData SetMasterPassword(Guid userId, MasterPasswordUnlockData masterPas /// Actions to update user data. /// On success Task UpdateUserDataAsync(IEnumerable updateUserDataActions); - + UpdateUserData SetRegisterFinishUserData(Guid userId, RegisterFinishData registerFinishData); } diff --git a/test/Common/AutoFixture/SignatureKeyPairRequestModelFixtures.cs b/test/Common/AutoFixture/SignatureKeyPairRequestModelFixtures.cs index a58528707e18..f3b66104b0b3 100644 --- a/test/Common/AutoFixture/SignatureKeyPairRequestModelFixtures.cs +++ b/test/Common/AutoFixture/SignatureKeyPairRequestModelFixtures.cs @@ -1,4 +1,4 @@ -using AutoFixture; +using AutoFixture; using Bit.Core.KeyManagement.Models.Api.Request; using Bit.Test.Common.AutoFixture.Attributes; @@ -16,4 +16,4 @@ public void Customize(IFixture fixture) public class SignatureKeyPairRequestModelCustomizeAttribute : BitCustomizeAttribute { public override ICustomization GetCustomization() => new SignatureKeyPairRequestModelCustomization(); -} \ No newline at end of file +} diff --git a/test/Core.Test/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModelTests.cs b/test/Core.Test/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModelTests.cs index b9532f989d50..8654a94ae7db 100644 --- a/test/Core.Test/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModelTests.cs +++ b/test/Core.Test/Auth/Models/Api/Request/Accounts/RegisterFinishRequestModelTests.cs @@ -208,7 +208,7 @@ public void ToData_Returns_ToData(string email, string masterPasswordHint, KdfRe Assert.Equal(legacyData.MasterPasswordAuthenticationData.Kdf.Parallelism, kdfParallelism); Assert.Equal(legacyData.MasterPasswordAuthenticationData.MasterPasswordAuthenticationHash, masterPasswordAuthenticationHash); Assert.Equal(legacyData.MasterPasswordAuthenticationData.Salt, email.ToLowerInvariant().Trim()); - + Assert.True(newData.IsV2Encryption()); Assert.Equal(newData.MasterPasswordUnlockData.Kdf, kdfRequest.ToData()); @@ -223,8 +223,8 @@ public void ToData_Returns_ToData(string email, string masterPasswordHint, KdfRe [Theory] [BitAutoData] [SignatureKeyPairRequestModelCustomize] - public void ToUser_Returns_User(string email, string masterPasswordHint, AccountKeysRequestModel accountKeysRequest, - KdfRequestModel kdfRequest, string masterPasswordAuthenticationHash, string userSymmetricKey, + public void ToUser_Returns_User(string email, string masterPasswordHint, AccountKeysRequestModel accountKeysRequest, + KdfRequestModel kdfRequest, string masterPasswordAuthenticationHash, string userSymmetricKey, KeysRequestModel userAsymmetricKeys, KdfType kdf, int kdfIterations, int? kdfMemory, int? kdfParallelism) { // Arrange diff --git a/test/Core.Test/Auth/UserFeatures/Registration/RegisterUserCommandTests.cs b/test/Core.Test/Auth/UserFeatures/Registration/RegisterUserCommandTests.cs index 6728f6c42f63..a392bf69f421 100644 --- a/test/Core.Test/Auth/UserFeatures/Registration/RegisterUserCommandTests.cs +++ b/test/Core.Test/Auth/UserFeatures/Registration/RegisterUserCommandTests.cs @@ -9,8 +9,8 @@ using Bit.Core.Auth.UserFeatures.Registration.Implementations; using Bit.Core.Billing.Enums; using Bit.Core.Entities; -using Bit.Core.KeyManagement.Models.Data; using Bit.Core.Exceptions; +using Bit.Core.KeyManagement.Models.Data; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces; using Bit.Core.Repositories; using Bit.Core.Services; diff --git a/test/Identity.Test/Controllers/AccountsControllerTests.cs b/test/Identity.Test/Controllers/AccountsControllerTests.cs index 0eda69bf72ea..dcb0de50ff5c 100644 --- a/test/Identity.Test/Controllers/AccountsControllerTests.cs +++ b/test/Identity.Test/Controllers/AccountsControllerTests.cs @@ -24,8 +24,8 @@ using Microsoft.Extensions.Logging; using NSubstitute; using NSubstitute.ReturnsExtensions; -using SignatureKeyPairRequestModelCustomizeAttribute = Bit.Test.Common.AutoFixture.SignatureKeyPairRequestModelCustomizeAttribute; using Xunit; +using SignatureKeyPairRequestModelCustomizeAttribute = Bit.Test.Common.AutoFixture.SignatureKeyPairRequestModelCustomizeAttribute; namespace Bit.Identity.Test.Controllers; @@ -396,7 +396,7 @@ await _registerUserCommand.Received(1).RegisterUserViaOrganizationInviteToken(Ar u.KdfParallelism == legacyUser.KdfParallelism && u.Key == legacyUser.Key ), legacyData, orgInviteToken, organizationUserId); - + Assert.NotNull(newResult); await _registerUserCommand.Received(1).RegisterUserViaOrganizationInviteToken(Arg.Is(u => u.Email == newUser.Email && @@ -479,7 +479,7 @@ public async Task PostRegisterFinish_OrgInviteDuplicateUser_ThrowsBadRequestExce _registerUserCommand.RegisterUserViaOrganizationInviteToken(Arg.Is(u => u.Email == newUser.Email && - u.MasterPasswordHint == newUser.MasterPasswordHint + u.MasterPasswordHint == newUser.MasterPasswordHint ), newData, orgInviteToken, organizationUserId) .Returns(Task.FromResult(failedIdentityResult)); @@ -825,7 +825,7 @@ public async Task PostRegisterFinish_EmailVerification_BothDataForms_ProduceEqui _registerUserCommand .RegisterUserViaEmailVerificationToken(Arg.Any(), legacyData, emailVerificationToken) .Returns(Task.FromResult(IdentityResult.Success)); - + _registerUserCommand .RegisterUserViaEmailVerificationToken(Arg.Any(), newData, emailVerificationToken) .Returns(Task.FromResult(IdentityResult.Success)); @@ -937,7 +937,7 @@ public async Task PostRegisterFinish_OrgInvite_BothDataForms_ProduceEquivalentOu _registerUserCommand .RegisterUserViaOrganizationInviteToken(Arg.Any(), newData, orgInviteToken, organizationUserId) .Returns(Task.FromResult(IdentityResult.Success)); - + _registerUserCommand .RegisterUserViaOrganizationInviteToken(Arg.Any(), legacyData, orgInviteToken, organizationUserId) .Returns(Task.FromResult(IdentityResult.Success)); diff --git a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs index 4d961b9b0b2f..0cdc00d803a0 100644 --- a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs +++ b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs @@ -298,7 +298,7 @@ public async Task RegisterNewIdentityFactoryUserAsync( } // Force valid signature algorithm and encrypted strings to avoid model validation failure. - requestModel.AccountKeys = new AccountKeysRequestModel + requestModel.AccountKeys = new AccountKeysRequestModel { UserKeyEncryptedAccountPrivateKey = DefaultEncryptedString, AccountPublicKey = keys.AccountPublicKey, diff --git a/util/Migrator/DbScripts/2026-01-30_00_User_SetRegisterFinishUserData.sql b/util/Migrator/DbScripts/2026-01-30_00_User_SetRegisterFinishUserData.sql new file mode 100644 index 000000000000..c74a5501eabb --- /dev/null +++ b/util/Migrator/DbScripts/2026-01-30_00_User_SetRegisterFinishUserData.sql @@ -0,0 +1,31 @@ +CREATE OR ALTER PROCEDURE [dbo].[User_SetRegisterFinishUserData] + @Id UNIQUEIDENTIFIER, + @Kdf TINYINT, + @KdfIterations INT, + @KdfMemory INT, + @KdfParallelism INT, + @Key VARCHAR(MAX), + @PublicKey VARCHAR(MAX), + @PrivateKey VARCHAR(MAX), + @RevisionDate DATETIME2(7), + @AccountRevisionDate DATETIME2(7) +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[User] + SET + [Key] = @Key, + [Kdf] = @Kdf, + [KdfIterations] = @KdfIterations, + [KdfMemory] = @KdfMemory, + [KdfParallelism] = @KdfParallelism, + [PublicKey] = @PublicKey, + [PrivateKey] = @PrivateKey, + [RevisionDate] = @RevisionDate, + [AccountRevisionDate] = @AccountRevisionDate + WHERE + [Id] = @Id +END +GO From 4341d21a3f63f01e6cde4d988ec65f2be025ef3d Mon Sep 17 00:00:00 2001 From: Eli Grubb Date: Fri, 6 Feb 2026 15:06:31 -0700 Subject: [PATCH 40/44] Addressing code review comments --- src/Core/Repositories/IUserRepository.cs | 2 +- .../Services/Implementations/UserService.cs | 2 +- .../Repositories/UserRepository.cs | 16 ++++++------- .../Repositories/UserRepository.cs | 14 +++++------ ... User_SetMasterPasswordUnlockUserData.sql} | 6 +---- .../Controllers/AccountsControllerTests.cs | 24 ++++++++++--------- ..._User_SetMasterPasswordUnlockUserData.sql} | 6 +---- 7 files changed, 30 insertions(+), 40 deletions(-) rename src/Sql/dbo/KeyManagement/Stored Procedures/{User_SetRegisterFinishUserData.sql => User_SetMasterPasswordUnlockUserData.sql} (75%) rename util/Migrator/DbScripts/{2026-01-30_00_User_SetRegisterFinishUserData.sql => 2026-01-30_00_User_SetMasterPasswordUnlockUserData.sql} (74%) diff --git a/src/Core/Repositories/IUserRepository.cs b/src/Core/Repositories/IUserRepository.cs index 01f9087e9af5..37bd1f1588d4 100644 --- a/src/Core/Repositories/IUserRepository.cs +++ b/src/Core/Repositories/IUserRepository.cs @@ -93,7 +93,7 @@ UpdateUserData SetMasterPassword(Guid userId, MasterPasswordUnlockData masterPas /// On success Task UpdateUserDataAsync(IEnumerable updateUserDataActions); - UpdateUserData SetRegisterFinishUserData(Guid userId, RegisterFinishData registerFinishData); + UpdateUserData SetMasterPasswordUnlockUserData(Guid userId, MasterPasswordUnlockData masterPasswordUnlockData); } public delegate Task UpdateUserData(Microsoft.Data.SqlClient.SqlConnection? connection = null, diff --git a/src/Core/Services/Implementations/UserService.cs b/src/Core/Services/Implementations/UserService.cs index 046a66c3f33b..c7ab40597ba6 100644 --- a/src/Core/Services/Implementations/UserService.cs +++ b/src/Core/Services/Implementations/UserService.cs @@ -336,7 +336,7 @@ public async Task CreateUserAsync(User user, RegisterFinishData var result = await CreateAsync(user, registerFinishData.MasterPasswordAuthenticationData.MasterPasswordAuthenticationHash); if (result.Succeeded) { - var setRegisterFinishUserDataTask = _userRepository.SetRegisterFinishUserData(user.Id, registerFinishData); + var setRegisterFinishUserDataTask = _userRepository.SetMasterPasswordUnlockUserData(user.Id, registerFinishData.MasterPasswordUnlockData); await _userRepository.SetV2AccountCryptographicStateAsync(user.Id, registerFinishData.UserAccountKeysData, [setRegisterFinishUserDataTask]); } return result; diff --git a/src/Infrastructure.Dapper/Repositories/UserRepository.cs b/src/Infrastructure.Dapper/Repositories/UserRepository.cs index 8427ab492ccf..cbc0fedbf667 100644 --- a/src/Infrastructure.Dapper/Repositories/UserRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/UserRepository.cs @@ -487,24 +487,22 @@ public async Task UpdateUserDataAsync(IEnumerable updateUserData } } - public UpdateUserData SetRegisterFinishUserData(Guid userId, RegisterFinishData registerFinishData) + public UpdateUserData SetMasterPasswordUnlockUserData(Guid userId, MasterPasswordUnlockData masterPasswordUnlockData) { return async (connection, transaction) => { var timestamp = DateTime.UtcNow; await connection!.ExecuteAsync( - "[dbo].[User_SetRegisterFinishUserData]", + "[dbo].[User_SetMasterPasswordUnlockUserData]", new { Id = userId, - Kdf = registerFinishData.MasterPasswordUnlockData.Kdf.KdfType, - KdfIterations = registerFinishData.MasterPasswordUnlockData.Kdf.Iterations, - KdfMemory = registerFinishData.MasterPasswordUnlockData.Kdf.Memory, - KdfParallelism = registerFinishData.MasterPasswordUnlockData.Kdf.Parallelism, - Key = registerFinishData.MasterPasswordUnlockData.MasterKeyWrappedUserKey, - PublicKey = registerFinishData.UserAccountKeysData.PublicKeyEncryptionKeyPairData.PublicKey, - PrivateKey = registerFinishData.UserAccountKeysData.PublicKeyEncryptionKeyPairData.WrappedPrivateKey, + Kdf = masterPasswordUnlockData.Kdf.KdfType, + KdfIterations = masterPasswordUnlockData.Kdf.Iterations, + KdfMemory = masterPasswordUnlockData.Kdf.Memory, + KdfParallelism = masterPasswordUnlockData.Kdf.Parallelism, + Key = masterPasswordUnlockData.MasterKeyWrappedUserKey, RevisionDate = timestamp, AccountRevisionDate = timestamp, }, diff --git a/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs b/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs index 17c19aa38a9d..a4d9a9780ab8 100644 --- a/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs @@ -555,7 +555,7 @@ public async Task UpdateUserDataAsync(IEnumerable updateUserData await transaction.CommitAsync(); } - public UpdateUserData SetRegisterFinishUserData(Guid userId, RegisterFinishData registerFinishData) + public UpdateUserData SetMasterPasswordUnlockUserData(Guid userId, MasterPasswordUnlockData masterPasswordUnlockData) { return async (_, _) => { @@ -565,13 +565,11 @@ public UpdateUserData SetRegisterFinishUserData(Guid userId, RegisterFinishData var userEntity = await dbContext.Users.FindAsync(userId) ?? throw new ArgumentException("User not found", nameof(userId)); var timestamp = DateTime.UtcNow; - userEntity.Kdf = registerFinishData.MasterPasswordUnlockData.Kdf.KdfType; - userEntity.KdfIterations = registerFinishData.MasterPasswordUnlockData.Kdf.Iterations; - userEntity.KdfMemory = registerFinishData.MasterPasswordUnlockData.Kdf.Memory; - userEntity.KdfParallelism = registerFinishData.MasterPasswordUnlockData.Kdf.Parallelism; - userEntity.Key = registerFinishData.MasterPasswordUnlockData.MasterKeyWrappedUserKey; - userEntity.PublicKey = registerFinishData.UserAccountKeysData.PublicKeyEncryptionKeyPairData.PublicKey; - userEntity.PrivateKey = registerFinishData.UserAccountKeysData.PublicKeyEncryptionKeyPairData.WrappedPrivateKey; + userEntity.Kdf = masterPasswordUnlockData.Kdf.KdfType; + userEntity.KdfIterations = masterPasswordUnlockData.Kdf.Iterations; + userEntity.KdfMemory = masterPasswordUnlockData.Kdf.Memory; + userEntity.KdfParallelism = masterPasswordUnlockData.Kdf.Parallelism; + userEntity.Key = masterPasswordUnlockData.MasterKeyWrappedUserKey; userEntity.RevisionDate = timestamp; userEntity.AccountRevisionDate = timestamp; diff --git a/src/Sql/dbo/KeyManagement/Stored Procedures/User_SetRegisterFinishUserData.sql b/src/Sql/dbo/KeyManagement/Stored Procedures/User_SetMasterPasswordUnlockUserData.sql similarity index 75% rename from src/Sql/dbo/KeyManagement/Stored Procedures/User_SetRegisterFinishUserData.sql rename to src/Sql/dbo/KeyManagement/Stored Procedures/User_SetMasterPasswordUnlockUserData.sql index 648e9a66bbf0..06a12579a728 100644 --- a/src/Sql/dbo/KeyManagement/Stored Procedures/User_SetRegisterFinishUserData.sql +++ b/src/Sql/dbo/KeyManagement/Stored Procedures/User_SetMasterPasswordUnlockUserData.sql @@ -1,12 +1,10 @@ -CREATE PROCEDURE [dbo].[User_SetRegisterFinishUserData] +CREATE PROCEDURE [dbo].[User_SetMasterPasswordUnlockUserData] @Id UNIQUEIDENTIFIER, @Kdf TINYINT, @KdfIterations INT, @KdfMemory INT, @KdfParallelism INT, @Key VARCHAR(MAX), - @PublicKey VARCHAR(MAX), - @PrivateKey VARCHAR(MAX), @RevisionDate DATETIME2(7), @AccountRevisionDate DATETIME2(7) AS @@ -21,8 +19,6 @@ BEGIN [KdfIterations] = @KdfIterations, [KdfMemory] = @KdfMemory, [KdfParallelism] = @KdfParallelism, - [PublicKey] = @PublicKey, - [PrivateKey] = @PrivateKey, [RevisionDate] = @RevisionDate, [AccountRevisionDate] = @AccountRevisionDate WHERE diff --git a/test/Identity.Test/Controllers/AccountsControllerTests.cs b/test/Identity.Test/Controllers/AccountsControllerTests.cs index dcb0de50ff5c..024407ccca26 100644 --- a/test/Identity.Test/Controllers/AccountsControllerTests.cs +++ b/test/Identity.Test/Controllers/AccountsControllerTests.cs @@ -425,7 +425,9 @@ public async Task PostRegisterFinish_OrgInviteDuplicateUser_ThrowsBadRequestExce var kdfModel = new KdfRequestModel { KdfType = KdfType.Argon2id, - Iterations = AuthConstants.ARGON2_ITERATIONS.Default + Iterations = AuthConstants.ARGON2_ITERATIONS.Default, + Memory = AuthConstants.ARGON2_MEMORY.Default, + Parallelism = AuthConstants.ARGON2_ITERATIONS.Default }; var newModel = new RegisterFinishRequestModel @@ -523,7 +525,9 @@ public async Task PostRegisterFinish_WhenGivenEmailVerificationToken_ShouldRegis var kdfModel = new KdfRequestModel { KdfType = KdfType.Argon2id, - Iterations = AuthConstants.ARGON2_ITERATIONS.Default + Iterations = AuthConstants.ARGON2_ITERATIONS.Default, + Memory = AuthConstants.ARGON2_MEMORY.Default, + Parallelism = AuthConstants.ARGON2_PARALLELISM.Default, }; var newModel = new RegisterFinishRequestModel @@ -599,7 +603,9 @@ public async Task PostRegisterFinish_WhenGivenEmailVerificationTokenDuplicateUse var kdfModel = new KdfRequestModel { KdfType = KdfType.Argon2id, - Iterations = AuthConstants.ARGON2_ITERATIONS.Default + Iterations = AuthConstants.ARGON2_ITERATIONS.Default, + Memory = AuthConstants.ARGON2_MEMORY.Default, + Parallelism = AuthConstants.ARGON2_PARALLELISM.Default }; var newModel = new RegisterFinishRequestModel @@ -804,10 +810,8 @@ public async Task PostRegisterFinish_EmailVerification_BothDataForms_ProduceEqui Email = email, EmailVerificationToken = emailVerificationToken, MasterPasswordHash = masterPasswordHash, - Kdf = KdfType.Argon2id, - KdfIterations = AuthConstants.ARGON2_ITERATIONS.Default, - KdfMemory = AuthConstants.ARGON2_MEMORY.Default, - KdfParallelism = AuthConstants.ARGON2_PARALLELISM.Default, + Kdf = KdfType.PBKDF2_SHA256, + KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default, UserSymmetricKey = masterKeyWrappedUserKey, UserAsymmetricKeys = new KeysRequestModel { @@ -916,10 +920,8 @@ public async Task PostRegisterFinish_OrgInvite_BothDataForms_ProduceEquivalentOu OrgInviteToken = orgInviteToken, OrganizationUserId = organizationUserId, MasterPasswordHash = masterPasswordHash, - Kdf = kdfData.KdfType, - KdfIterations = kdfData.Iterations, - KdfMemory = kdfData.Memory, - KdfParallelism = kdfData.Parallelism, + Kdf = KdfType.PBKDF2_SHA256, + KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default, UserSymmetricKey = masterKeyWrappedUserKey, UserAsymmetricKeys = new KeysRequestModel { diff --git a/util/Migrator/DbScripts/2026-01-30_00_User_SetRegisterFinishUserData.sql b/util/Migrator/DbScripts/2026-01-30_00_User_SetMasterPasswordUnlockUserData.sql similarity index 74% rename from util/Migrator/DbScripts/2026-01-30_00_User_SetRegisterFinishUserData.sql rename to util/Migrator/DbScripts/2026-01-30_00_User_SetMasterPasswordUnlockUserData.sql index c74a5501eabb..ae9fee58d080 100644 --- a/util/Migrator/DbScripts/2026-01-30_00_User_SetRegisterFinishUserData.sql +++ b/util/Migrator/DbScripts/2026-01-30_00_User_SetMasterPasswordUnlockUserData.sql @@ -1,12 +1,10 @@ -CREATE OR ALTER PROCEDURE [dbo].[User_SetRegisterFinishUserData] +CREATE OR ALTER PROCEDURE [dbo].[User_SetMasterPasswordUnlockUserData] @Id UNIQUEIDENTIFIER, @Kdf TINYINT, @KdfIterations INT, @KdfMemory INT, @KdfParallelism INT, @Key VARCHAR(MAX), - @PublicKey VARCHAR(MAX), - @PrivateKey VARCHAR(MAX), @RevisionDate DATETIME2(7), @AccountRevisionDate DATETIME2(7) AS @@ -21,8 +19,6 @@ BEGIN [KdfIterations] = @KdfIterations, [KdfMemory] = @KdfMemory, [KdfParallelism] = @KdfParallelism, - [PublicKey] = @PublicKey, - [PrivateKey] = @PrivateKey, [RevisionDate] = @RevisionDate, [AccountRevisionDate] = @AccountRevisionDate WHERE From b4613acc2f1f4d6d6ed7c598da85b2b64d4976e4 Mon Sep 17 00:00:00 2001 From: Eli Grubb Date: Tue, 10 Feb 2026 10:03:17 -0700 Subject: [PATCH 41/44] Fix kdf settings for test and update migration script date --- test/Identity.Test/Controllers/AccountsControllerTests.cs | 2 ++ ...l => 2026-02-10_00_User_SetMasterPasswordUnlockUserData.sql} | 0 2 files changed, 2 insertions(+) rename util/Migrator/DbScripts/{2026-01-30_00_User_SetMasterPasswordUnlockUserData.sql => 2026-02-10_00_User_SetMasterPasswordUnlockUserData.sql} (100%) diff --git a/test/Identity.Test/Controllers/AccountsControllerTests.cs b/test/Identity.Test/Controllers/AccountsControllerTests.cs index 024407ccca26..7296e7ac388d 100644 --- a/test/Identity.Test/Controllers/AccountsControllerTests.cs +++ b/test/Identity.Test/Controllers/AccountsControllerTests.cs @@ -348,6 +348,8 @@ public async Task PostRegisterFinish_WhenGivenOrgInvite_ShouldRegisterUser( { KdfType = KdfType.Argon2id, Iterations = AuthConstants.ARGON2_ITERATIONS.Default, + Memory = AuthConstants.ARGON2_MEMORY.Default, + Parallelism = AuthConstants.ARGON2_PARALLELISM.Default }; var newModel = new RegisterFinishRequestModel diff --git a/util/Migrator/DbScripts/2026-01-30_00_User_SetMasterPasswordUnlockUserData.sql b/util/Migrator/DbScripts/2026-02-10_00_User_SetMasterPasswordUnlockUserData.sql similarity index 100% rename from util/Migrator/DbScripts/2026-01-30_00_User_SetMasterPasswordUnlockUserData.sql rename to util/Migrator/DbScripts/2026-02-10_00_User_SetMasterPasswordUnlockUserData.sql From 66c3938468d2329035f07a2001b8f91902c92682 Mon Sep 17 00:00:00 2001 From: Eli Grubb Date: Sun, 22 Feb 2026 23:34:24 -0700 Subject: [PATCH 42/44] Fix date for sql migration script --- ...sql => 2026-02-22_00_User_SetMasterPasswordUnlockUserData.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename util/Migrator/DbScripts/{2026-02-10_00_User_SetMasterPasswordUnlockUserData.sql => 2026-02-22_00_User_SetMasterPasswordUnlockUserData.sql} (100%) diff --git a/util/Migrator/DbScripts/2026-02-10_00_User_SetMasterPasswordUnlockUserData.sql b/util/Migrator/DbScripts/2026-02-22_00_User_SetMasterPasswordUnlockUserData.sql similarity index 100% rename from util/Migrator/DbScripts/2026-02-10_00_User_SetMasterPasswordUnlockUserData.sql rename to util/Migrator/DbScripts/2026-02-22_00_User_SetMasterPasswordUnlockUserData.sql From f7760be37e8505bd88ce5edf05271bf46da021b0 Mon Sep 17 00:00:00 2001 From: Eli Grubb Date: Wed, 4 Mar 2026 17:47:46 -0700 Subject: [PATCH 43/44] Fix date for sql migration script --- ...sql => 2026-03-04_00_User_SetMasterPasswordUnlockUserData.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename util/Migrator/DbScripts/{2026-02-22_00_User_SetMasterPasswordUnlockUserData.sql => 2026-03-04_00_User_SetMasterPasswordUnlockUserData.sql} (100%) diff --git a/util/Migrator/DbScripts/2026-02-22_00_User_SetMasterPasswordUnlockUserData.sql b/util/Migrator/DbScripts/2026-03-04_00_User_SetMasterPasswordUnlockUserData.sql similarity index 100% rename from util/Migrator/DbScripts/2026-02-22_00_User_SetMasterPasswordUnlockUserData.sql rename to util/Migrator/DbScripts/2026-03-04_00_User_SetMasterPasswordUnlockUserData.sql From eb9bc4558a468927172e1a76502055803268f929 Mon Sep 17 00:00:00 2001 From: Eli Grubb Date: Fri, 6 Mar 2026 14:34:13 -0700 Subject: [PATCH 44/44] Rename sql procedure --- src/Infrastructure.Dapper/Repositories/UserRepository.cs | 2 +- ...UserData.sql => User_UpdateMasterPasswordUnlockUserData.sql} | 2 +- ...> 2026-03-04_00_User_UpdateMasterPasswordUnlockUserData.sql} | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/Sql/dbo/KeyManagement/Stored Procedures/{User_SetMasterPasswordUnlockUserData.sql => User_UpdateMasterPasswordUnlockUserData.sql} (89%) rename util/Migrator/DbScripts/{2026-03-04_00_User_SetMasterPasswordUnlockUserData.sql => 2026-03-04_00_User_UpdateMasterPasswordUnlockUserData.sql} (88%) diff --git a/src/Infrastructure.Dapper/Repositories/UserRepository.cs b/src/Infrastructure.Dapper/Repositories/UserRepository.cs index c9d08ed75eca..a47e183719db 100644 --- a/src/Infrastructure.Dapper/Repositories/UserRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/UserRepository.cs @@ -523,7 +523,7 @@ public UpdateUserData SetMasterPasswordUnlockUserData(Guid userId, MasterPasswor var timestamp = DateTime.UtcNow; await connection!.ExecuteAsync( - "[dbo].[User_SetMasterPasswordUnlockUserData]", + "[dbo].[User_UpdateMasterPasswordUnlockUserData]", new { Id = userId, diff --git a/src/Sql/dbo/KeyManagement/Stored Procedures/User_SetMasterPasswordUnlockUserData.sql b/src/Sql/dbo/KeyManagement/Stored Procedures/User_UpdateMasterPasswordUnlockUserData.sql similarity index 89% rename from src/Sql/dbo/KeyManagement/Stored Procedures/User_SetMasterPasswordUnlockUserData.sql rename to src/Sql/dbo/KeyManagement/Stored Procedures/User_UpdateMasterPasswordUnlockUserData.sql index 06a12579a728..21fdb70d86c7 100644 --- a/src/Sql/dbo/KeyManagement/Stored Procedures/User_SetMasterPasswordUnlockUserData.sql +++ b/src/Sql/dbo/KeyManagement/Stored Procedures/User_UpdateMasterPasswordUnlockUserData.sql @@ -1,4 +1,4 @@ -CREATE PROCEDURE [dbo].[User_SetMasterPasswordUnlockUserData] +CREATE PROCEDURE [dbo].[User_UpdateMasterPasswordUnlockUserData] @Id UNIQUEIDENTIFIER, @Kdf TINYINT, @KdfIterations INT, diff --git a/util/Migrator/DbScripts/2026-03-04_00_User_SetMasterPasswordUnlockUserData.sql b/util/Migrator/DbScripts/2026-03-04_00_User_UpdateMasterPasswordUnlockUserData.sql similarity index 88% rename from util/Migrator/DbScripts/2026-03-04_00_User_SetMasterPasswordUnlockUserData.sql rename to util/Migrator/DbScripts/2026-03-04_00_User_UpdateMasterPasswordUnlockUserData.sql index ae9fee58d080..0daf50aeb148 100644 --- a/util/Migrator/DbScripts/2026-03-04_00_User_SetMasterPasswordUnlockUserData.sql +++ b/util/Migrator/DbScripts/2026-03-04_00_User_UpdateMasterPasswordUnlockUserData.sql @@ -1,4 +1,4 @@ -CREATE OR ALTER PROCEDURE [dbo].[User_SetMasterPasswordUnlockUserData] +CREATE OR ALTER PROCEDURE [dbo].[User_UpdateMasterPasswordUnlockUserData] @Id UNIQUEIDENTIFIER, @Kdf TINYINT, @KdfIterations INT,