Fix account registration username capitalization login bug (#460)

This commit is contained in:
Leendert de Borst
2024-12-13 11:50:18 +01:00
parent 1e65f14323
commit 8bd05b5c2e
2 changed files with 90 additions and 4 deletions

View File

@@ -68,11 +68,49 @@ public class AuthTests : ClientPlaywrightTest
}
/// <summary>
/// Test if logging out and logging in works.
/// Test if logging in with different case variations of username works.
/// </summary>
/// <returns>Async task.</returns>
[Test]
[Order(3)]
public async Task CapitalizedUsernameTest()
{
// Logout current user
await Logout();
// Create a new user with capital letters in username
var capitalUsername = "TestUser@Example.com";
await Register(checkForSuccess: true, username: capitalUsername);
await Logout();
// Test Case 1: Try to login with lowercase version of the username
var lowercaseUsername = capitalUsername.ToLower();
await LoginWithUsername(lowercaseUsername);
await VerifySuccessfulLogin();
// Test Case 2: Try to login with exact capitalized username
await Logout();
await LoginWithUsername(capitalUsername);
await VerifySuccessfulLogin();
// Test Case 3: Create new user with lowercase
await Logout();
var lowercaseUser = "testuser2@example.com";
await Register(checkForSuccess: true, username: lowercaseUser);
await Logout();
// Try logging in with uppercase version
var uppercaseVersion = lowercaseUser.ToUpper();
await LoginWithUsername(uppercaseVersion);
await VerifySuccessfulLogin();
}
/// <summary>
/// Test if logging out and logging in works.
/// </summary>
/// <returns>Async task.</returns>
[Test]
[Order(4)]
public async Task LogoutAndLoginRememberMeTest()
{
await Logout();
@@ -101,7 +139,7 @@ public class AuthTests : ClientPlaywrightTest
/// </summary>
/// <returns>Async task.</returns>
[Test]
[Order(4)]
[Order(5)]
public async Task RegisterFormWarningTest()
{
await Logout();
@@ -116,7 +154,7 @@ public class AuthTests : ClientPlaywrightTest
/// </summary>
/// <returns>Async task.</returns>
[Test]
[Order(5)]
[Order(6)]
public async Task PasswordAuthLockoutTest()
{
await Logout();
@@ -152,4 +190,41 @@ public class AuthTests : ClientPlaywrightTest
var pageContent = await Page.TextContentAsync("body");
Assert.That(pageContent, Does.Contain("locked out"), "No account lockout message.");
}
/// <summary>
/// Login with a given username.
/// </summary>
/// <param name="username">The username to login with.</param>
/// <returns>Async task.</returns>
private async Task LoginWithUsername(string username)
{
await NavigateToLogin();
var emailField = await WaitForAndGetElement("input[id='email']");
var passwordField = await WaitForAndGetElement("input[id='password']");
await emailField.FillAsync(username);
await passwordField.FillAsync(TestUserPassword);
var loginButton = await WaitForAndGetElement("button[type='submit']");
await loginButton.ClickAsync();
}
/// <summary>
/// Verify that a login was successful.
/// </summary>
/// <returns>Async task.</returns>
private async Task VerifySuccessfulLogin()
{
// Wait for the index page to load which should show "Credentials" in the top menu.
await WaitForUrlAsync("**", "Credentials");
// Check if the login was successful by verifying content.
var pageContent = await Page.TextContentAsync("body");
Assert.That(pageContent, Does.Contain(WelcomeMessage), "No index content after logging in.");
// Check if login has created an auth log entry.
var authLogEntry = await ApiDbContext.AuthLogs.FirstOrDefaultAsync(x =>
x.EventType == AuthEventType.Login);
Assert.That(authLogEntry, Is.Not.Null, "Auth log entry not found in database after login.");
}
}

View File

@@ -26,7 +26,9 @@ public static class Srp
/// <returns>SrpSignup model.</returns>
public static SrpPasswordChange PasswordChangeAsync(SrpClient client, string salt, string username, string passwordHashString)
{
// Derive a key from the password using Argon2id
// Derive a key from the password using Argon2id.
// Make sure the username is lowercase as the SRP protocol is case sensitive.
username = username.ToLowerInvariant();
// Signup or password change: client generates a salt and verifier.
var privateKey = DerivePrivateKey(salt, username, passwordHashString);
@@ -44,6 +46,9 @@ public static class Srp
/// <returns>Private key as string.</returns>
public static string DerivePrivateKey(string salt, string username, string passwordHashString)
{
// Make sure the username is lowercase as the SRP protocol is case sensitive.
username = username.ToLowerInvariant();
var client = new SrpClient();
return client.DerivePrivateKey(salt, username, passwordHashString);
}
@@ -80,6 +85,9 @@ public static class Srp
/// <returns>session.</returns>
public static SrpSession DeriveSessionClient(string privateKey, string clientSecretEphemeral, string serverEphemeralPublic, string salt, string username)
{
// Make sure the username is lowercase as the SRP protocol is case sensitive.
username = username.ToLowerInvariant();
var client = new SrpClient();
return client.DeriveSession(
clientSecretEphemeral,
@@ -101,6 +109,9 @@ public static class Srp
/// <returns>SrpSession.</returns>
public static SrpSession? DeriveSessionServer(string serverEphemeralSecret, string clientEphemeralPublic, string salt, string username, string verifier, string clientSessionProof)
{
// Make sure the username is lowercase as the SRP protocol is case sensitive.
username = username.ToLowerInvariant();
try
{
var server = new SrpServer();