From fac1d4bdc2328d8adab91116a1e2ec4c92a8ea7c Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Mon, 29 Dec 2025 11:57:25 -0500 Subject: [PATCH] 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 252247f7c6..bc84e9cf58 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 class RegisterFinishRequestModel : IValidatableObject // 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 class RegisterFinishRequestModel : IValidatableObject public IEnumerable Validate(ValidationContext validationContext) { - MasterPasswordUnlockData.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthenticationData, MasterPasswordUnlockData); + // PM-28143 - Remove line below + MasterPasswordUnlockData.ThrowIfExistsAndNotMatchingAuthenticationData(MasterPasswordAuthentication, MasterPasswordUnlock); // PM-28143 - Remove line below - var kdf = MasterPasswordUnlockData?.Kdf.KdfType + MasterPasswordAuthenticationData.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 = 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 a521816985..06ee097f32 100644 --- a/src/Identity/Controllers/AccountsController.cs +++ b/src/Identity/Controllers/AccountsController.cs @@ -156,7 +156,7 @@ public class AccountsController : Controller 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 468fed55cd..e64e9b88fb 100644 --- a/test/Identity.Test/Controllers/AccountsControllerTests.cs +++ b/test/Identity.Test/Controllers/AccountsControllerTests.cs @@ -607,7 +607,7 @@ public class AccountsControllerTests : IDisposable { Email = email, EmailVerificationToken = emailVerificationToken, - MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData + MasterPasswordAuthentication = new MasterPasswordAuthenticationData { Kdf = new KdfSettings { @@ -619,7 +619,7 @@ public class AccountsControllerTests : IDisposable 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 class AccountsControllerTests : IDisposable Email = email, OrgInviteToken = orgInviteToken, OrganizationUserId = organizationUserId, - MasterPasswordAuthenticationData = new MasterPasswordAuthenticationData + MasterPasswordAuthentication = new MasterPasswordAuthenticationData { Kdf = new KdfSettings { @@ -735,7 +735,7 @@ public class AccountsControllerTests : IDisposable MasterPasswordAuthenticationHash = masterPasswordHash, Salt = email }, - MasterPasswordUnlockData = new MasterPasswordUnlockData + MasterPasswordUnlock = new MasterPasswordUnlockData { Kdf = new KdfSettings { @@ -842,14 +842,14 @@ public class AccountsControllerTests : IDisposable { 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 class AccountsControllerTests : IDisposable 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 class AccountsControllerTests : IDisposable { 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 class AccountsControllerTests : IDisposable { 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 class AccountsControllerTests : IDisposable { 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 d605cc9ff8..4101553c01 100644 --- a/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs +++ b/test/IntegrationTestCommon/Factories/IdentityApplicationFactory.cs @@ -206,7 +206,7 @@ public class IdentityApplicationFactory : WebApplicationFactoryBase 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 class IdentityApplicationFactory : WebApplicationFactoryBase 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 class IdentityApplicationFactory : WebApplicationFactoryBase }; } - 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,