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

Support case-insensitive username logins #14585

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
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
9 changes: 9 additions & 0 deletions test/api/v3/integration/user/auth/POST-login-local.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ describe('POST /user/auth/local/login', () => {
expect(response.apiToken).to.eql(user.apiToken);
});

it('success with case-insensitive username', async () => {
user = await generateUser({}, { username: 'CASE-INSENSITIVE' });
const response = await api.post(endpoint, {
username: 'Case-Insensitive',
password,
});
expect(response.apiToken).to.eql(user.apiToken);
});

it('success with email', async () => {
const response = await api.post(endpoint, {
username: user.auth.local.email,
Expand Down
16 changes: 13 additions & 3 deletions website/server/controllers/api-v3/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,21 @@ api.loginLocal = {
if (validator.isEmail(String(username))) {
login = { 'auth.local.email': username.toLowerCase() }; // Emails are stored lowercase
} else {
login = { 'auth.local.username': username };
login = { 'auth.local.lowerCaseUsername': username.toLowerCase() };
}

// load the entire user because we may have to save it to convert the password to bcrypt
const user = await User.findOne(login).exec();
// load the entire user because we may have to save it to convert the password to bcrypt.
// lookup the user by case-insensitive username.
// there's a rare chance for duplicates before registration enforced case-insensitive uniqueness
const potentialUsers = await User.find(login).exec();
let user;

if (potentialUsers.length > 1) {
// multiple users share the same username. fallback to case-sensitive username matching
user = potentialUsers.find(u => u.auth.local.username === username);
} else {
[user] = potentialUsers;
}

// if user is using social login, then user will not have a hashed_password stored
if (!user || !user.auth.local.hashed_password) throw new NotAuthorized(res.t('invalidLoginCredentialsLong'));
Expand Down