0
votes

I am trying to test if a GET request to a specific route is protected by using JSON Web Tokens with passport JwtStrategy; however, it seems like the passport.use middleware function I have in my server.js file is not executing. The console.log I have in there never shows up in my shell/terminal. My login route works, but not the profile route. I am using postman and I entered http://localhost:3000/profile for the GET method and in the Headers tab I chose Authorization for Key and for the Value I copied and pasted the long JSON web token string, but it keeps saying unauthorized. That is because my passport.use function is never getting executed.

//Server.js file

var JwtStrategy = require("passport-jwt").Strategy;
var ExtractJwt = require("passport-jwt").ExtractJwt;

var User = require("../models/user");
var config = require('./secret');

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

let options = {};
//pass token back and forth
options.jwtFromRequest = ExtractJwt.fromAuthHeader();
options.secretOrKey = config;
passport.use(new JwtStrategy(options, (jwt_payload, done) => {
  *******************************************
  //this console log doesn't show up in shell which makes be believe its never getting here
  *******************************************
  console.log("JWT PAYLOAD", jwt_payload)
  User.getUserById(jwt_payload._id, (err, user) => {
    if(err){
      return done(err, false);
    }

    if(user){ //null for error
      return done(null, user);
    }else{
      return done(null, false);
    }
  });
}));

//Routes file where the passport.authenticate callback is called

var passport = require('passport');
var jwt = require('jsonwebtoken');
var secret = require('../config/secret')

var User = require('../models/user');

router.post('/login', (req, res) => {
    var username = req.body.username;
    var password = req.body.password;
    console.log("SECRET2", secret);
    console.log("SECRET", secret.secret);
    User.getUserByUsername(username, (err, user) => {
        if(err){
            throw err;
        }
        if(!user){
            return res.json({ success: false, msg: "User not found"});
        }

        User.comparePassword(password, user.password, (err, isMatch) => {
            if(err){
                throw err;
            }
            if(isMatch){
                var token = jwt.sign(user, secret.secret, {
                    expiresIn: 604800 //1 week in seconds, token expires and requires to log back in
                });

                console.log('TOKEN IN LOGIN ROUTE', token)

                res.json({
                    //tokens are then stored in local storage or cookie
                    success: true,
                    token: 'JWT ' + token,
                    user: {
                        id: user._id,
                        name: user.name,
                        username: user.username,
                        email: user.email
                    }
                });
            }else{
                return res.json({ success: false, msg: "Incorrect password"});
            }
        });
    });
});

router.get('/profile', passport.authenticate('jwt', {session:false}), (req, res) => {
    res.json({user: req.user});
});

//User model

var mongoose = require('mongoose');
var bcrypt = require('bcryptjs');

var Schema = mongoose.Schema;

var UserSchema = new Schema({
  name: {
    type: String,
    trim: true,
    required: "First Name is Required"
  },
  email: {
    type: String,
    required: true
  },
  username: {
    type: String,
    required: true
  },
  password: {
    type: String,
    required: true
  }
});

var User = mongoose.model("User", UserSchema);

module.exports = User;

//Alternate syntax for exporting Schema
// var User = module.exports = mongoose.model("User", UserSchema);

module.exports.getUserById = function(id, callback){
    User.findById(id, callback);
}

module.exports.getUserByUsername = function(username, callback){
    var query = { username: username }
    User.findOne(query, callback);
}

//Custom User model function that will take in the newUser object and hash the password.
module.exports.addUser = function(newUser, callback){
    bcrypt.genSalt(10, (err, salt) => {
        bcrypt.hash(newUser.password, salt, (err, hash) => {
            if(err){
                throw err
            }

            newUser.password = hash;
            newUser.save(callback);
        });
    });
}

module.exports.comparePassword = function(password, hash, callback){
    bcrypt.compare(password, hash, (err, isMatch) => {
        if(err){
            throw err
        }
        callback(null, isMatch);
    });
}

Update: I tried putting a space after 'JWT' in for the value for Authorization in the postman, but it still does not work and the console log is not showing. Is it because I am somehow not exporting or linking the passport.use I have defined in my server.js to my GET '/profile' route in my routes file?

Update 2: Added model and Login route

1
The value of the Authorization header has to look like this: JWT the-actual-token. Look here. - robertklep
Do you mean when I am testing on postman the GET route to profile? I have type set to Authorization and the value as the entire jwt token I pasted. Unless i need that JWT space then the token. But im not sure this is issue as the Authenticate (passport.use) method never seems to get called because the console log never shows. - henhen
Yes, you need to prefix the token in the value with "JWT-space". Like it says in the documentation I linked to: github.com/themikenicholson/… My guess is that the strategy is never called because the Authorization header is "invalid". - robertklep
You are generating the token with secret.secret, but in the strategy options, you're using secret, which doesn't seem right. As an aside: expiresIn should be in milliseconds, not seconds (it's now set to expire in 10 minutes), and strictly speaking, JWT should not be part of the token. - robertklep
It's quite common to use those id's clientside (in URL's or otherwise) to identify users, so that's not a big deal. - robertklep

1 Answers

0
votes

Maybe it is necessary to include more code to see your issue, but it seems like the strategy is not being exported correctly. When you create a new strategy, you can include an 'alias' to use it in the entry point:

passport.use('local-login', new JwtStrategy(options, (jwt_payload, done) => {
    ....
}

router.get('/profile', passport.authenticate('local-login', {session:false}), (req, res) => {
    res.json({user: req.user});
});