Search code examples
node.jsauthenticationpassport-local

How to get http request.body or request in passport LocalStrategy callback


Just want to get http request or request.body inside the callback of LocalStrategy as you can see in the attached files we code like this new LocalStrategy({ usernameField: 'email', passwordField: 'pwd' },(username: any, password: any, done: any) => { We are able to get username & password, but I want to get entire req.body

  1. I want to get some additional information which is passed in login request and want to store this additional information as part of a session created.
  2. I tried to solve this using req.logIn() method of passport inside callback of passport.authenticate('local', callback).
  3. It worked but the problem here is the passport.serialize method is called twice hence it creates two sessions. I want to avoid double session creation.
  4. Hence the solution I thought is to get additional information LocalStrategy method.

Code

import UserDetailsRepo from '../../repo/UserDetailsRepo'
import UserDetails from '../../model/UserDetails'
import * as passport from 'passport'
import { Strategy as LocalStrategy } from 'passport-local'
// import JwtConfiguration from './express-jwt-config'
import * as HttpStatus from 'http-status-codes'

class PassportAuth {
    public passport: any;
    constructor() {
        this.passport = passport.use(new LocalStrategy({
            usernameField: 'email',
            passwordField: 'pwd'
          },(username: any, password: any, done: any) => {
            UserDetailsRepo.fetch(username)
                .then(function (userDetails: UserDetails) {
                    if (!userDetails) {
                        return done(null, false, { errorCode: HttpStatus.UNAUTHORIZED, message: 'Incorrect username.' });
                    }
                    if (!userDetails.validatePassword(password)) {
                        return done(null, false, { errorCode: HttpStatus.UNAUTHORIZED, message: 'Incorrect password.' });
                    }
                    return done(null,  userDetails);
                })
                .catch((err: any) => {
                    return done(err);
                })
        }))
        // passport.use(JwtConfiguration.getStrategy())
        passport.serializeUser(function (user, done) {
            if(!user) {
                done({ errorCode: HttpStatus.UNPROCESSABLE_ENTITY,message:'ser' },user)
            } else {
                done(null, user);
            }
        });

        passport.deserializeUser(function (user, done) {
            console.log("Deseriaize User");
            console.log(user);
            done(null, user);
        });
    }
}
export default new PassportAuth().passport;


router.post('/login', passport.authenticate('local'), (req: Request, res: Response, next: NextFunction) => {
            passport.authenticate('local', (err: any, user: UserDetails, info: any) => {
                if (user) {
                    let loginUser = user.checkAttributes(req.body.role, req.body.department);
                    // if (loginUser) {
                        req.logIn(loginUser, function (err) {
                            if (err) {
                                next(err)
                            }
                            next()
                        });
                    // } else {
                    //  next({ errorCode: HttpStatus.UNPROCESSABLE_ENTITY })
                    // }
                } else {
                    next(info)
                }
            })(req, res, next)
        }, (req: Request, res: Response) => {
            res.send(req.body)
            res.end()
        });

Solution

  • If you look at the code below

    this.passport = passport.use(new LocalStrategy({
                usernameField: 'email',
                passwordField: 'pwd',
                passReqToCallback:true
              },(req:any,username: any, password: any, done: any) => {
                UserDetailsRepo.fetch(username)
                    .then(function (userDetails: UserDetails) {
                        if (!userDetails) {
                            return done(null, false, { errorCode: HttpStatus.UNAUTHORIZED, message: 'Incorrect username.' });
                        }
                        if (!userDetails.validatePassword(password)) {
                            return done(null, false, { errorCode: HttpStatus.UNAUTHORIZED, message: 'Incorrect password.' });
                        }
                        try {
                            return done(null, userDetails.getLoginUserDetails(req.body.role,req.body.department));
                        } catch (e){
                            return done(null, false, { errorCode: HttpStatus.UNAUTHORIZED, message: e.message } );
                        }                    
                    })
                    .catch((err: any) => {
                        return done(err);
                    })
            }))
    

    The passReqToCallback:true is added to the LocalStrategy, When we set it to true we will get request as first argument in the LocalStrategy's callback function i.e (req:any,username: any, password: any, done: any)

    Where to look into? If you see at the code of the LocalStrategy constructor

    declare class Strategy extends PassportStrategy {
        constructor(
            options: IStrategyOptionsWithRequest,
            verify: VerifyFunctionWithRequest
        );
        constructor(options: IStrategyOptions, verify: VerifyFunction);
        constructor(verify: VerifyFunction);
    
        name: string;
    }
    

    In the above code there are two main interfaces IStrategyOptionsWithRequest, IStrategyOptions

    
    interface IStrategyOptions {
        usernameField?: string;
        passwordField?: string;
        session?: boolean;
        passReqToCallback?: false;
    }
    
    interface IStrategyOptionsWithRequest {
        usernameField?: string;
        passwordField?: string;
        session?: boolean;
        passReqToCallback: true;
    }
    
    

    Now it is clear that by passing true or false value to passReqToCallback, we will get the request object in LocalStrategy's callback.

    Why to place request as first argument in callback? If you look at constructor code above there are two functions VerifyFunctionWithRequest and VerifyFunction The first argument in VerifyFunctionWithRequest interface is req Hope it is clear...

    
    interface VerifyFunctionWithRequest {
        (
            req: express.Request,
            username: string,
            password: string,
            done: (error: any, user?: any, options?: IVerifyOptions) => void
        ): void;
    }
    
    interface VerifyFunction {
        (
            username: string,
            password: string,
            done: (error: any, user?: any, options?: IVerifyOptions) => void
        ): void;
    }