I have decided to use passport.js for authentication of my node app. I implemented local strategies for login and signup and they works fine but there is a problem that confuses me. I have built a module in the past that generates and returns http errors using http-error package. I use it for standardizing error generation. For example if some credentials are missing or invalid I call
Errors.ValidationError({name:"password",reason:"password field is missing in your request"})
this creates and returns a proper http error and I send it to the users. It is useful for error generation. I want to use this logic with passport so I opted to create my own callbacks. Here is some code:
local login strategy definition
/**
* User login
* @param {*} request
* @param {*} response
* @return {*} logged existant user or error
*/
passport.use('login',
new localStrategy(
{
usernameField: 'email',
passwordField: 'password',
},
async (email, password, done)=> {
if(email==null||password==null)
{
AppLogger.debug(21)
return done(null, false);
}
User.findOne({ email: email }).
then(async(user)=>{
if (!user) {
AppLogger.debug(22)
return done(null, false);
}
if (!(await user.verifyPassword(password))) {
AppLogger.debug(23)
return done(null, false);
}
return done(null, user);
}).
catch((err)=>{
AppLogger.debug(24)
return done(err);
});
}
)
);
login route
/**
* Login an existant user
*/
AuthRouter.post('/login', (req, res, next)=> {
passport.authenticate('login', (err, user, info) =>{
if (err) {
AppLogger.debug(1)
return next(err);
}
if (!user) {
AppLogger.debug(2)
return next(info);
}
req.login(user,(err)=>{
if(err){
AppLogger.debug(3)
return next(err);
}
res.send({
success:true,
message:"logged in"
})
})
})(req, res, next);
});
considering all the debug lines on the code I get this output: logs
My log lines prints 2 which means there is no exception but user is null and I am trying to understand where exactly the missing credentials error is created in login strategy so I can generate my custom http error. Oddly enough none of the debugs in the strategy is not getting executed. Note that there is a middleware that catches all the errors and logs so the error log comes from there. And also keep in kind the app works as expected but I can't spot where this error is generated. I assume some method in passport middleware throws an error and my log middleware catches it but how can I interact with it and generate my http error in a meaningful way? Any suggestions are appreciated.
CodePudding user response:
Here is the work around I came up with:
router
/**
* Login an existant user
*/
AuthRouter.post('/login', (req, res, next)=> {
if(!req.body.email || !req.body.password){
const subErrors = []
if(!req.body.email){
subErrors.push({
name:"email",
reason:"login request must contain email"
})
}
if(!req.body.password){
subErrors.push({
name:"password",
reason:"login request must contain password"
})
}
const httpError = Errors.ValidationError(subErrors);
return next(httpError);
}
passport.authenticate('login', (err, user, info) =>{
if (err) {
return next(err);
}
if (!user) {
return next(info);
}
req.login(user,(err)=>{
if(err){
return next(err);
}
res.send({
success:true,
message:"logged in"
})
})
})(req, res, next);
});
Since the ultimate goal was replacing passport's default missing credentials error with my custom http error and I couldn't find a way within passportjs strategy, I check the request body before calling the passport.authenticate()
I can successfully create and return my custom error but this means although I check manually, passportjs also checks if the credentials are there. So its not optimal for performance.
Also I know that using
if(x===null)
causes some problems in some cases and I also used '==' instead of '===' in the original code. It has nothing to do with main problem but its not the best practice.
finally here are the default and custom error responses:
this is what passportjs returns
{"message":"Missing credentials"}
this is my custom error
{
"statusCode":400,
"error":"Bad Request",
"invalid_params":[
{
"name":"password",
"reason":"login request must contain password"
}
],
"type":"about:blank",
"title":"invalid parameters"}
I hope I explained well what I was trying to achieve. Now it all works as desired but as I said with this logic I am checking twice for the same thing so any better solution is appreciated.