jaredhanson / passport

Simple, unobtrusive authentication for Node.js.

Home Page:https://www.passportjs.org?utm_source=github&utm_medium=referral&utm_campaign=passport&utm_content=about

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Using AuthenticationCallback/req.logIn() breaks deserialization in 0.7.0

Mark-Elliott5 opened this issue · comments

I am creating a chat app with websockets and want to use req.user within websocket event listener handlers. Using a simple passport.authenticate('local') works fine and populates req.user in later requests as expected. However, I want to provide an AuthenticationCallback to send a variable JSON message back to my React frontend on login success/failure rather than just a 401 status. User information is retrieved successfully but later requests do not deserialize. Downgrading to 0.5.3 fixes the issue.

Expected behavior

req.user is populated through deserialization on requests after authorization

Actual behavior

req.user is undefined on requests after authorization

Steps to reproduce

Route:

app.post('/login', (req: IReq, res: IRes, next: INext) =>
  passport.authenticate('local', function (err, user) {
    if (err) {
      console.log('err');
      return res.json({
        authenticated: false,
        message: 'Server error. Try again.',
      });
    }
    if (!user) {
      return res.json({
        authenticated: false,
        message: 'User not found.',
      });
    }
    req.logIn(user, { session: true }, (err) => {
      console.log(err);
    });
    res.json({ authenticated: true });
  } as AuthenticateCallback)(req, res, next)
);

My passport configuration:

import { Application } from 'express';
import passport from 'passport';
import { Strategy as LocalStrategy } from 'passport-local';
import 'dotenv/config';
import session from 'express-session';
import bcrypt from 'bcrypt';
import { User } from '../types/mongoose/User';

const configureAuthentication = (app: Application) => {
  const secret =
    process.env.SECRET ??
    (() => {
      throw new Error('.env secret key not found! Sessions need a secret key.');
    })();

  app.use(
    session({
      secret,
      resave: false,
      saveUninitialized: true,
    })
  );

  passport.use(
    new LocalStrategy(async (username, password, done) => {
      try {
        const user = await User.findOne({ username });
        if (!user) {
          return done(null, false);
        }
        const match = await bcrypt.compare(password, user.password);
        if (!match) {
          return done(null, false);
        }
        return done(null, user);
      } catch (err) {
        return done(err);
      }
    })
  );

  passport.serializeUser((user, done) => {
    done(null, user._id);
  });

  passport.deserializeUser(async (_id, done) => {
    try {
      const user = await User.findById(_id);
      done(null, user);
    } catch (err) {
      done(err);
    }
  });

  app.use(passport.initialize());
  app.use(passport.session());
};

export default configureAuthentication;

My websocket (logs undefined on 0.7.0, logs correctly on 0.5.3):

import WebSocket from 'ws';
import { IReq } from '../types/express';

function websocketHandler(ws: WebSocket, req: IReq) {
  ws.on('message', function (msg: WebSocket.RawData) {
    console.log(req.user); // undefined
    ws.send(msg);
  });
}

Environment

  • Operating System: macOS 14.1.1
  • Node version: 20.10
  • passport version: 0.7.0