I have a node.js login system with passport but I am trying to figure out how to log in a user with either their username or email. I am only able to log in the user with email or username seperately. I don't know how to write the code to cater for both their username and email. So if a user wants to login with username, they can or if the wish to use their email, they also can. Here is my localstrategy code in my users.js file:
passport.use(new LocalStrategy(
function(email, password, done) {
User.getUserByEmail(email, function(err, user, next){
if(err) throw err;
if(!user){
return done(null, false, {message: 'Unknown user'});
}
User.comparePassword(password, user.password, function(err, isMatch){
if(err) throw err;
if(isMatch){
return done(null, user);
} else {
return done(null, false, {message: 'Invalid password'});
}
});
});
}));
And here's my module.exports in my user.js:
module.exports.getUserByEmail = function(email, callback){
var query = {email: email};
User.findOne(query, callback);
}
module.exports.getUserById = function(id, callback){
User.findById(id, callback);
}
module.exports.comparePassword = function(candidatePassword, hash, callback){
bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
if(err) throw err;
callback(null, isMatch);
});
}
The above code only allows user to login with their email. I want users to have the opportunity to login with either their email or username.
Expect both username
and password
within your authentication middleware and then proceed with whatever value you have found as the condition to find the user.
Middleware example:
function authenticateUser(req, res, done) {
let username = req.body.username,
password = req.body.password,
email = req.body.email;
let conditions = !!username ? {username: username} : {email: email};
UserModel.findOne(conditions, (err, user) => {
if (err) return done(err);
if (!user) return done(new Error('Incorrect username or email'));
return user.comparePassword(password, user.password)
.then(match => {
if (match) return done();
else return done(new Error('Incorrect password'));
})
.catch(error => {
if (error) return done(new Error(`Unable to validated password. - ${error}`));
});
});
}
Now, a front-end developer — with the right documentation — can now actually use either the username
, email
or both (you will need a bit of JavaScript for both) when building login forms using your endpoint. Here is an example of using both:
HTML:
<form id="login-form" method="POST" action="/login">
<input id="username-or-email" type="text" placeholder="Username or Email" required/>
<input type="password" name="password" placeholder="Password" required/>
<input type="submit"/>
</form>
JavaScript:
// select the form element
let loginForm = document.querySelector('#login-form');
// add a form handler on submit
loginForm.addEventListener("submit", formHandler);
// validate and the set name attribute as appropriate
function formHandler() {
/** W3C Email regex: (RFC5322) */
const email_regex = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
/** Must starts with a letter then can include underscores (_) & hyphens (-) */
const username_regex = /^[a-zA-Z][\w-]+$/;
let input = document.querySelector('#username-or-email');
if (email_regex.test(input.value)) {
// it is an email, send the value as an email
input.setAttribute("name", "email");
} else if (username_regex.test(input.value)) {
// it is a username, send the value as a username
input.setAttribute("name", "username");
} else {
// invalid email or username format, return an error message to user
}
}
So you get to validate and set your dynamic input at the same time. Keep in mind that the regular expression should match your username and email data model as much as possible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With