3
votes

I'm having an absolute nightmare trying to set up JWT with my express app! Think I've got it mostly working now, I have a register route and login route that both work correctly and generate valid tokens and I have another route in my '/users' route that I test the authentication with and this is all fine. But I have another file containing routes for '/api' which is where the authentication is actually important and I have a similar test route that tries to access req.user (just like I do in my other route) but it seems like req.user is undefined. Through some debugging it looks like the user is in req.account which is very odd and I don't understand why its not in req.user

I define my jwt strategy in /config/passport.js

'use strict';
const User = require('../models/user'),
      config = require('./main'),
      JwtStrategy = require('passport-jwt').Strategy,
      ExtractJwt = require('passport-jwt').ExtractJwt;

//exported to be used by passport in server set up
module.exports = function (passport) {
  const jwtOptions = {  
    // Telling Passport to check authorization headers for JWT
    jwtFromRequest: ExtractJwt.fromAuthHeader(),
    // Telling Passport where to find the secret
    secretOrKey: config.secret
  };

  const jwtLogin = new JwtStrategy(jwtOptions, function(payload, done) {
    User.findById(payload._id, function(err, user) {
      if (err) { return done(err, false); }

      if (user) {
        done(null, user);
      } else {
        done(null, false);
      }
    });
  });

  passport.use(jwtLogin);

}

passport is passed as an argument to this and then initialised in the main express file

here is the /users route file, this works fine. sending a GET request to /users/isAuth with Authorization header and 'JWT ' works fine and I get my username sent back to me

"use strict";
const   express = require('express'),
        router = express.Router(),
        jwt = require('jsonwebtoken'),
        User = require('../models/user'),
        config = require('../config/main'),
        passport = require ('passport');

function generateToken(user) {
    return jwt.sign({_id: user._id}, config.secret, {
        expiresIn: 10080
    });
}
.
. Here are routes for login and register they perform as expected
. and work fine
.
/*  ==================================
       Test Authentication Route
    ================================== */
router.get('/isAuth', passport.authenticate('jwt', { session: false }), function(req, res) {
    console.log(req.user);
    res.json({username: req.user.username});
});



module.exports = router;

In this file though, for the api routes sending a request to GET /api/testAuth exactly the same as before with the same token and the same headers I get back no req.user and in the console I see that req.user is undefined. But in the console there does seem to be the user object just as req.account? I don't understand what is happening here hopefully someone can help!

"use strict";
const   express = require('express'),
        router = express.Router(),
        jwt = require('jsonwebtoken'),
        Server = require('../models/server'),
        passport = require('passport');

// Test route to see if logged in user is matt
router.get('/testAuth', passport.authorize('jwt', { session: false }), function(req, res) {
    console.log(req.user);
    if (req.user) {
        if(req.user.username == "matt") {
        res.send("You are matt!");
        } else {
        res.send("You are not matt!");
        }
    } else {
        res.send("no req.user");
    }

})

module.exports = router;
2
Samson Maosa's answer solve my problem.Jeex Box

2 Answers

4
votes

You are using passport.authorize in your testAuth route, this is for users that are already logged in and have session information. Since you are not using session storage you do not have a persistent req.user object and so should use passport.authenticate on all routes

http://passportjs.org/docs/authorize

15
votes

req.user object is only set when you use a passport authentication strategy that uses sessions. In this case, the authentication is stateless since you have specified {session: false}, which is how it should be for an api. Thus, the session does not have a user object. Here is how I set my req.user object in the passport.authenticate middleware:

  1. Modify your jwtOptions to enable passing the req object to JwtStrategy function:

    const jwtOptions = {  
        // Telling Passport to check authorization headers for JWT
        jwtFromRequest: ExtractJwt.fromAuthHeader(),
        // Telling Passport where to find the secret
        secretOrKey: config.secret,
        passReqToCallback: true, //<= Important, so that the verify function can accept the req param ie verify(req,payload,done)
      };
    
  2. Modify the parameters to your JwtStrategy to include the request object as the first parameter; then within the if (user) block, just assign the returned user object to req.user:

    const jwtLogin = new JwtStrategy(jwtOptions, function(req, payload, done) {
        User.findById(payload._id, function(err, user) {
          if (err) { return done(err, false); }
    
          if (user) {
            req.user = user; // <= Add this line
            done(null, user);
          } else {
            done(null, false);
          }
        });
      });
    

That is it: now any route that has the passport.authenticate("jwt", {session: false}) middleware will receive req.user upon successful authentication.