Home > Enterprise >  when exactly missing credentials error created in passport.js login cycle
when exactly missing credentials error created in passport.js login cycle

Time:02-16

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.

  • Related