1
votes

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()
        });
1
if you need this information for login: write own strategy, if not: add this info to session after login process (in last (req: Request, res: Response))bato3
@bato3 I tried to write my own strategy, but It will create other problems like, I need to write whole supportive code which LocalStrategy provide. That will be again looping back kind of thing here. As you can see in the above code I am write additional details to session using req.logIn method of passport, But the issue here is my passport.serializeUser method is called twice. First time it is called when I call 'done' inside the LocalStartegy callback, Second time it is called when I call req.logIn() method in login service. ThanksZiaullhaq Savanur

1 Answers

2
votes

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;
}