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