Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix account registration username capitalization login bug #461

Merged
merged 1 commit into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 78 additions & 3 deletions src/Tests/AliasVault.E2ETests/Tests/Client/Shard4/AuthTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,49 @@ public async Task LogoutAndLoginTest()
}

/// <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();
Expand Down Expand Up @@ -101,7 +139,7 @@ public async Task LogoutAndLoginRememberMeTest()
/// </summary>
/// <returns>Async task.</returns>
[Test]
[Order(4)]
[Order(5)]
public async Task RegisterFormWarningTest()
{
await Logout();
Expand All @@ -116,7 +154,7 @@ public async Task RegisterFormWarningTest()
/// </summary>
/// <returns>Async task.</returns>
[Test]
[Order(5)]
[Order(6)]
public async Task PasswordAuthLockoutTest()
{
await Logout();
Expand Down Expand Up @@ -152,4 +190,41 @@ public async Task PasswordAuthLockoutTest()
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.");
}
}
13 changes: 12 additions & 1 deletion src/Utilities/Cryptography/AliasVault.Cryptography.Client/Srp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -44,6 +46,9 @@ public static SrpPasswordChange PasswordChangeAsync(SrpClient client, string sal
/// <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);
}
Expand Down Expand Up @@ -80,6 +85,9 @@ public static SrpEphemeral GenerateEphemeralServer(string verifier)
/// <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,
Expand All @@ -101,6 +109,9 @@ public static SrpSession DeriveSessionClient(string privateKey, string clientSec
/// <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();
Expand Down
Loading