|
|
|
|
@ -75,7 +75,7 @@ public class AccountsControllerTests : IDisposable
@@ -75,7 +75,7 @@ public class AccountsControllerTests : IDisposable
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
public async Task PostPasswordPrelogin_WhenUserExists_ShouldReturnUserKdfInfo() |
|
|
|
|
public async Task PostPrelogin_WhenUserExists_ShouldReturnUserKdfInfo() |
|
|
|
|
{ |
|
|
|
|
var userKdfInfo = new UserKdfInformation |
|
|
|
|
{ |
|
|
|
|
@ -84,113 +84,30 @@ public class AccountsControllerTests : IDisposable
@@ -84,113 +84,30 @@ public class AccountsControllerTests : IDisposable
|
|
|
|
|
}; |
|
|
|
|
_userRepository.GetKdfInformationByEmailAsync(Arg.Any<string>()).Returns(userKdfInfo); |
|
|
|
|
|
|
|
|
|
var response = await _sut.PostPasswordPrelogin(new PasswordPreloginRequestModel { Email = "user@example.com" }); |
|
|
|
|
var response = await _sut.PostPrelogin(new PreloginRequestModel { Email = "user@example.com" }); |
|
|
|
|
|
|
|
|
|
Assert.Equal(userKdfInfo.Kdf, response.Kdf); |
|
|
|
|
Assert.Equal(userKdfInfo.KdfIterations, response.KdfIterations); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
public async Task PostPrelogin_And_PostPasswordPrelogin_ShouldUseSamePreloginLogic() |
|
|
|
|
public async Task PostPrelogin_WhenUserDoesNotExistAndNoDefaultKdfHmacKeySet_ShouldDefaultToPBKDF() |
|
|
|
|
{ |
|
|
|
|
// Arrange: No user exists and no default HMAC key to force default path |
|
|
|
|
var email = "same-user@example.com"; |
|
|
|
|
SetDefaultKdfHmacKey(null); |
|
|
|
|
_userRepository.GetKdfInformationByEmailAsync(Arg.Any<string>()).Returns(Task.FromResult<UserKdfInformation?>(null)); |
|
|
|
|
|
|
|
|
|
// Act |
|
|
|
|
var legacyResponse = await _sut.PostPrelogin(new PasswordPreloginRequestModel { Email = email }); |
|
|
|
|
var newResponse = await _sut.PostPasswordPrelogin(new PasswordPreloginRequestModel { Email = email }); |
|
|
|
|
|
|
|
|
|
// Assert: Both endpoints yield identical results, implying shared logic path |
|
|
|
|
Assert.Equal(legacyResponse.Kdf, newResponse.Kdf); |
|
|
|
|
Assert.Equal(legacyResponse.KdfIterations, newResponse.KdfIterations); |
|
|
|
|
Assert.Equal(legacyResponse.KdfMemory, newResponse.KdfMemory); |
|
|
|
|
Assert.Equal(legacyResponse.KdfParallelism, newResponse.KdfParallelism); |
|
|
|
|
Assert.Equal(legacyResponse.Salt, newResponse.Salt); |
|
|
|
|
Assert.NotNull(legacyResponse.KdfSettings); |
|
|
|
|
Assert.NotNull(newResponse.KdfSettings); |
|
|
|
|
Assert.Equal(legacyResponse.KdfSettings!.KdfType, newResponse.KdfSettings!.KdfType); |
|
|
|
|
Assert.Equal(legacyResponse.KdfSettings!.Iterations, newResponse.KdfSettings!.Iterations); |
|
|
|
|
Assert.Equal(legacyResponse.KdfSettings!.Memory, newResponse.KdfSettings!.Memory); |
|
|
|
|
Assert.Equal(legacyResponse.KdfSettings!.Parallelism, newResponse.KdfSettings!.Parallelism); |
|
|
|
|
|
|
|
|
|
// Both methods should consult the repository once each with the same email |
|
|
|
|
await _userRepository.Received(2).GetKdfInformationByEmailAsync(Arg.Is<string>(e => e == email)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
public async Task PostPasswordPrelogin_WhenUserExists_ReturnsNewFieldsAlignedWithLegacy_Argon2() |
|
|
|
|
{ |
|
|
|
|
var email = "user@example.com"; |
|
|
|
|
var userKdfInfo = new UserKdfInformation |
|
|
|
|
{ |
|
|
|
|
Kdf = KdfType.Argon2id, |
|
|
|
|
KdfIterations = AuthConstants.ARGON2_ITERATIONS.Default, |
|
|
|
|
KdfMemory = AuthConstants.ARGON2_MEMORY.Default, |
|
|
|
|
KdfParallelism = AuthConstants.ARGON2_PARALLELISM.Default |
|
|
|
|
}; |
|
|
|
|
_userRepository.GetKdfInformationByEmailAsync(Arg.Any<string>()).Returns(userKdfInfo); |
|
|
|
|
|
|
|
|
|
var response = await _sut.PostPasswordPrelogin(new PasswordPreloginRequestModel { Email = email }); |
|
|
|
|
|
|
|
|
|
// New fields exist and match repository values |
|
|
|
|
Assert.NotNull(response.KdfSettings); |
|
|
|
|
Assert.Equal(userKdfInfo.Kdf, response.KdfSettings!.KdfType); |
|
|
|
|
Assert.Equal(userKdfInfo.KdfIterations, response.KdfSettings!.Iterations); |
|
|
|
|
Assert.Equal(userKdfInfo.KdfMemory, response.KdfSettings!.Memory); |
|
|
|
|
Assert.Equal(userKdfInfo.KdfParallelism, response.KdfSettings!.Parallelism); |
|
|
|
|
|
|
|
|
|
// New and legacy fields are aligned during migration |
|
|
|
|
Assert.Equal(response.Kdf, response.KdfSettings!.KdfType); |
|
|
|
|
Assert.Equal(response.KdfIterations, response.KdfSettings!.Iterations); |
|
|
|
|
Assert.Equal(response.KdfMemory, response.KdfSettings!.Memory); |
|
|
|
|
Assert.Equal(response.KdfParallelism, response.KdfSettings!.Parallelism); |
|
|
|
|
|
|
|
|
|
// Salt is set to the input email during migration |
|
|
|
|
Assert.Equal(email, response.Salt); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
public async Task PostPasswordPrelogin_WhenUserDoesNotExistAndNoDefaultKdfHmacKeySet_ShouldDefaultToPBKDF() |
|
|
|
|
{ |
|
|
|
|
SetDefaultKdfHmacKey(null); |
|
|
|
|
_userRepository.GetKdfInformationByEmailAsync(Arg.Any<string>()).Returns(Task.FromResult<UserKdfInformation?>(null)); |
|
|
|
|
|
|
|
|
|
var response = await _sut.PostPasswordPrelogin(new PasswordPreloginRequestModel { Email = "user@example.com" }); |
|
|
|
|
var response = await _sut.PostPrelogin(new PreloginRequestModel { Email = "user@example.com" }); |
|
|
|
|
|
|
|
|
|
Assert.Equal(KdfType.PBKDF2_SHA256, response.Kdf); |
|
|
|
|
Assert.Equal(AuthConstants.PBKDF2_ITERATIONS.Default, response.KdfIterations); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Fact] |
|
|
|
|
public async Task PostPasswordPrelogin_NoUser_NoDefaultHmacKey_ReturnsAlignedNewFieldsAndSalt() |
|
|
|
|
{ |
|
|
|
|
var email = "user@example.com"; |
|
|
|
|
SetDefaultKdfHmacKey(null); |
|
|
|
|
_userRepository.GetKdfInformationByEmailAsync(Arg.Any<string>()).Returns(Task.FromResult<UserKdfInformation?>(null)); |
|
|
|
|
|
|
|
|
|
var response = await _sut.PostPasswordPrelogin(new PasswordPreloginRequestModel { Email = email }); |
|
|
|
|
|
|
|
|
|
// New fields exist |
|
|
|
|
Assert.NotNull(response.KdfSettings); |
|
|
|
|
|
|
|
|
|
// New and legacy fields are aligned during migration |
|
|
|
|
Assert.Equal(response.Kdf, response.KdfSettings!.KdfType); |
|
|
|
|
Assert.Equal(response.KdfIterations, response.KdfSettings!.Iterations); |
|
|
|
|
Assert.Equal(response.KdfMemory, response.KdfSettings!.Memory); |
|
|
|
|
Assert.Equal(response.KdfParallelism, response.KdfSettings!.Parallelism); |
|
|
|
|
|
|
|
|
|
// Salt is set to the input email during migration |
|
|
|
|
Assert.Equal(email, response.Salt); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Theory] |
|
|
|
|
[BitAutoData] |
|
|
|
|
public async Task PostPasswordPrelogin_WhenUserDoesNotExistAndDefaultKdfHmacKeyIsSet_ShouldComputeHmacAndReturnExpectedKdf(string email) |
|
|
|
|
public async Task PostPrelogin_WhenUserDoesNotExistAndDefaultKdfHmacKeyIsSet_ShouldComputeHmacAndReturnExpectedKdf(string email) |
|
|
|
|
{ |
|
|
|
|
// Arrange: |
|
|
|
|
var defaultKey = "my-secret-key"u8.ToArray(); |
|
|
|
|
var defaultKey = Encoding.UTF8.GetBytes("my-secret-key"); |
|
|
|
|
SetDefaultKdfHmacKey(defaultKey); |
|
|
|
|
|
|
|
|
|
_userRepository.GetKdfInformationByEmailAsync(Arg.Any<string>()).Returns(Task.FromResult<UserKdfInformation?>(null)); |
|
|
|
|
@ -205,7 +122,7 @@ public class AccountsControllerTests : IDisposable
@@ -205,7 +122,7 @@ public class AccountsControllerTests : IDisposable
|
|
|
|
|
var expectedKdf = defaultKdfResults[expectedIndex]; |
|
|
|
|
|
|
|
|
|
// Act |
|
|
|
|
var response = await _sut.PostPasswordPrelogin(new PasswordPreloginRequestModel { Email = email }); |
|
|
|
|
var response = await _sut.PostPrelogin(new PreloginRequestModel { Email = email }); |
|
|
|
|
|
|
|
|
|
// Assert: Ensure the returned KDF matches the expected one from the computed hash |
|
|
|
|
Assert.Equal(expectedKdf.Kdf, response.Kdf); |
|
|
|
|
@ -215,16 +132,6 @@ public class AccountsControllerTests : IDisposable
@@ -215,16 +132,6 @@ public class AccountsControllerTests : IDisposable
|
|
|
|
|
Assert.Equal(expectedKdf.KdfMemory, response.KdfMemory); |
|
|
|
|
Assert.Equal(expectedKdf.KdfParallelism, response.KdfParallelism); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// New and legacy fields are aligned during migration |
|
|
|
|
Assert.NotNull(response.KdfSettings); |
|
|
|
|
Assert.Equal(response.Kdf, response.KdfSettings!.KdfType); |
|
|
|
|
Assert.Equal(response.KdfIterations, response.KdfSettings!.Iterations); |
|
|
|
|
Assert.Equal(response.KdfMemory, response.KdfSettings!.Memory); |
|
|
|
|
Assert.Equal(response.KdfParallelism, response.KdfSettings!.Parallelism); |
|
|
|
|
|
|
|
|
|
// Salt is set to the input email during migration |
|
|
|
|
Assert.Equal(email, response.Salt); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
[Theory] |
|
|
|
|
|