REST API is not changing users according to Passport JWT Token in header? - node.js

So I have a REST API written in Node.JS, MongoDB, and Express. I'm using passport and passport-jwt to authenticate using a JSON web token, but when I use different tokens for different user accounts, the request is saving the same user everytime.
Here is my authenticate endpoint in routes.js:
// Authenticate the user and get a JSON Web Token to include in the header of future requests.
apiRoutes.post('/authenticate', function(req, res) {
User.findOne({
email: req.body.email
}, function(err, user) {
if (err) throw err;
if (!user) {
res.send({ success: false, message: 'Authentication failed. User not found.' });
} else {
// Check if password matches
user.comparePassword(req.body.password, function(err, isMatch) {
if (isMatch && !err) {
// Create token if the password matched and no error was thrown
var token = jwt.sign(user, config.secret, {
//expiresIn: 10080 // in seconds
});
res.json({ success: true, token: 'JWT ' + token, _id: user._id });
} else {
res.send({ success: false, message: 'Authentication failed. Passwords did not match.' });
}
});
}
});
});
So, if the user credentials match in the database, it creates a new token and returns it. Then I put that token into the header of this endpoint:
// GET user
apiRoutes.get('/users', passport.authenticate('jwt', { session: false }), function(req, res) {
res.json(req.user);
});
I could authenticate a new user every time and get a different token every time, but the /users endpoint always returns the same user. Everything else is working in the API, the database is getting updated when registering new users and other things are being returned, but even with different tokens the same user is returned from passport. I know that I am putting the header in the right way in Postman with the key of Authorization and the value of the token with JWT in front of it, but I don't understand why this is happening.
Here is my passport.js if it matters:
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var User = require('../models/user');
var config = require('./main');
// Setup work and export for the JWT passport strategy
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({id: jwt_payload.id}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
};
Any ideas?

Mongo uses a "_" before the document id. Instead of having User.findOne({id: jwt_payload.id} you should have User.findOne({_id: jwt_payload._id} and that should fix the issue.
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var User = require('../models/user');
var config = require('./main');
// Setup work and export for the JWT passport strategy
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({_id: jwt_payload._id}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
};

So I figured out that it was displaying the first entry in the user document because the JWT was too big. So I changed the authenticate route to make the JWT out of just the user's id and username like so:
apiRoutes.post('/authenticate', function(req, res) {
User.findOne({
email: req.body.email
}, function(err, user) {
if (err) throw err;
if (!user) {
res.send({ success: false, response: 'Authentication failed. User not found.' });
} else {
// Check if password matches
user.comparePassword(req.body.password, function(err, isMatch) {
if (isMatch && !err) {
// Create token if the password matched and no error was thrown
var token = jwt.sign({id: user._id, username: user.username }, config.secret, {
expiresIn: 10080 // in seconds
});
res.json({ success: true, response: 'Authentication succeeded! User found.', token: 'JWT ' + token});
} else {
res.send({ success: false, response: 'Authentication failed. Passwords did not match.' });
}
});
}
});
});

Payload has lot more data after jwt.sign in version 5.5.4, now _id is a part of _doc under payload.
As "SamuelDuke" suggest above is correct, just change to jwt_payload._doc._id.
See below updated code.
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var User = require('../models/user');
var config = require('./main');
// Setup work and export for the JWT passport strategy
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({_id: jwt_payload._doc._id}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
};

Related

How can I combine JWT with passport-facebook?

I'm creating an express application using passportJS, and I'm trying to authenticate my api using JSON Web Tokens:
The application has both local and facebook users, defined together in a mongoose schema:
var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
var UserSchema = mongoose.Schema({
name: {
type: String
},
password: {
type: String
},
local: {
type: String,
enum: [
'0',
'1'
]
},
fbId : String,
fbToken : String,
fbEmail : String,
fbName : String
});
UserSchema.pre('save', function(callback) {
var user = this;
if (user.local == '1') {
if (!user.isModified('password'))
return callback();
bcrypt.genSalt(5, function (err, salt) {
if (err)
return callback(err);
bcrypt.hash(user.password, salt, null, function (err, hash) {
if (err)
return callback(err);
user.password = hash;
callback();
});
});
}
else {
callback();
}
});
UserSchema.methods.verifyPassword = function(password, cb) {
bcrypt.compare(password, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
module.exports = mongoose.model('User', UserSchema);
The main, app.js, file includes the following code, where I configure the passport using the config/passport.js file (which follows right after)
var express = require('express');
var app = express();
var passport = require('passport');
var jwt = require('jsonwebtoken');
app.use(passport.initialize());
require('./config/passport')(passport);
app.get('/auth/facebook', passport.authenticate('facebook', {scope:['email']}));
app.get('/auth/facebook/callback',
passport.authenticate('facebook', {session: false, failureRedirect: '/', successRedirect: '/user'})
);
app.post('/authenticate', function(req, res) {
User.findOne({
name: req.body.name
}, function(err, user) {
if (err) throw err;
if (!user) {
res.send({success: false, msg: 'Authentication failed. User not found.'});
} else {
user.verifyPassword(req.body.password, function (err, isMatch) {
if (isMatch && !err) {
var token = jwt.sign(user, "mysecret");
res.json({success: true, token: 'JWT ' + token});
} else {
res.send({success: false, msg: 'Authentication failed. Wrong password.'});
}
});
}
});
});
app.all('/api/*',
passport.authenticate('jwt', {session: false}),
function (req, res, next) {
next();
});
Here is the config/passport.js file, where I define the JwtStrategy and the FacebookStrategy. Also, this is where I issue the token for facebook users:
var FacebookStrategy = require('passport-facebook').Strategy;
var User = require('../models/user');
var jwt = require('jsonwebtoken');
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
opts.secretOrKey = "mysecret";
passport.use(new JwtStrategy(opts, function (jwt_payload, done) {
console.log("I'm here!");
User.findOne({id: jwt_payload.id}, function (err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
passport.use(new FacebookStrategy(
{
clientID : '...',
clientSecret : '...',
callbackURL : 'http://localhost:3000/auth/facebook/callback',
profileFields : ['id', 'emails', 'name']
},
function(accessToken, refreshToken, profile, done) {
process.nextTick(function() {
User.findOne({ 'fbId' : profile.id }, function(err, user) {
if (err)
return done(err);
if (user) {
var token = jwt.sign(user, "mysecret");
console.log('JWT ' + token); //I'm doing this in orer to copy the token and paste it on Postman
return done(null, ({success: true, token: 'JWT ' + token}));
} else {
var newUser = new User();
newUser.local = '0';
newUser.fbId = profile.id;
newUser.fbToken = accessToken;
newUser.fbName = profile.name.givenName + " " + profile.name.familyName;
newUser.fbEmail = profile.emails[0].value;
newUser.save(function(err) {
if (err)
throw err;
var token = jwt.sign(newUser, "mysecret");
return done(null, {success: true, token: 'JWT ' + token});
});
}
});
});
}
));
};
Authentication and authorization for local users works fine, I've tested it with Postman:
But when I replace the token of the local user with the one I created for the facebook user, I don't get authorized:
Plus, when I try to access the API with the facebook user's token, I don't even get the "I'm here!!" console log message (that I've defined in the JwtStrategy), whereas in the case of local users I do.
Any ideas what I'm doing wrong?
Thanks a lot in advance!
Edit: I found out that if, instead of the user, I sign the following object:
var toSign = user.fbId + user.fbName + user.fbEmail
i.e. without the fbToken I get authorized.
Any ideas why that is?

How to set JWT bearer token on frontend (handlebars)

I want to access protected routes with my frontend but i am unable to do it, i can do it only with postman.
I try to save jwt in local storage now i am trying to set it as header but i am affraid i am still doing something wrong and cant figure out what.
This is my login
// Login
router.post("/login", (req, res, next) => {
const username = req.body.username;
const password = req.body.password;
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) {
const token = jwt.sign({
data: user
},
config.secret, {
expiresIn: 604800 // 1 week
}
);
res.setHeader('Authorization', "Bearer " + token);
res.redirect('profile');
} else {
return res.json({
success: false,
msg: "Wrong password"
});
}
});
});
});
// Profile
router.get(
"/profile",
passport.authenticate("jwt", {
session: false
}),
(req, res, next) => {
res.json({
user: req.user
});
}
);
module.exports = router;
Passport.js
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const User = require('../models/user');
const config = require('../config/database');
module.exports = function(passport){
let opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
User.getUserById(jwt_payload.data._id, (err, user) => {
if(err){
return done(err, false);
}
if(user){
return done(null, user);
} else {
return done(null, false);
}
});
}));
}
Profile.handlebars
<h1>Welcome on profile</h1>
I really dont know how to do it i am mostly on tutorials but i just cant find something working for this. And my attempts are all false.

NodeJs JWT Passport

I use nodejs with passport Auth JWT. I can create the JWT Token but If I secure my route with passport.authenticate('jwt') it's not work and i have an errors.
My Error :
TypeError: Cannot read property '_id' of undefined
at JwtStrategy._verify (D:\Programes\nodejs\node\CRUD_NodeAngular5\NodeServer\config\passport.js:15:39)
at D:\Programes\nodejs\node\CRUD_NodeAngular5\NodeServer\node_modules\passport-jwt\lib\strategy.js:110:26
at D:\Programes\nodejs\node\CRUD_NodeAngular5\NodeServer\node_modules\passport-jwt\node_modules\jsonwebtoken\verify.js:27:18
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)
Passport.js
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var User = require('../models/User');
var config = require('./database');
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findById(jwt_payload.$__._id, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
};
Login
router.post('/login', function(req, res) {
User.findOne({ email: req.body.email }, function(err, user) {
if (err) throw err;
if (!user) {
res.send({ success: false, message: 'Authentication failed. User not found.' });
console.log('User not found');
} else {
user.comparePassword(req.body.password, function(err, isMatch) {
if (isMatch && !err) {
var token = jwt.sign(user.toJSON(), config.secret, {expiresIn: 1000 });
var decoded = jwt.decode(token);
console.log(decoded);
res.json({ success: true, token:'JWT ' + token, decoded: decoded });
console.log('Connected : ' + token);
} else {
res.send({ success: false, message: 'Authentication failed. Passwords did not match.' });
console.log('Password is wrong');
}
});
}
});
});
Route Dashboard Not work
router.get('/dashboard', passport.authenticate('jwt', { session: false }), function(req, res) {
res.send('It worked! User id is: ' );
});
lets clean your code it's simple :-
Step1: create config file and load where you keep your routes
var checkAuth = require('../config/check-auth');
Step2: copy paste it in check-auth
const jwt = require('jsonwebtoken');
module.exports = (req,res,next)=>{
try{
const token = req.headers.authorization.split(" ")[1];
const decoded = jwt.verify(token,"secret");
req.userData = decoded;
next();
}catch(error){
return res.status(401).json({message:'Auth failed'});
}
}
step 3 :protect your route
router.get('/dashboard',checkAuth , { session: false }),
function(req, res) {
res.send('It worked! User id is: ' );
});
protect all your routes by passing checkAuth as parameter

node.js trying to get passport and ExtraxtJwt to work

I'm trying to successfully authorize using jwt and passport in node.js.
I'm using passport.use with a JwtStrategy.
I have set turned on authenticate on one of my routes to test that it would unauthorize me without a token,
// Profile
router.get('/profile', passport.authenticate('jwt', { session: false }), (req, res, next) => {
res.json({ user: req.user });
});
When I send a post request with postman to http://localhost:3000/users/profile I get Unauthorized so far so good...
When I login through postman on localhost:3000/users/authenticate with a correct username and password, I get back a token. looks like this:
{
"success":true,
"token":"JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Il9pZCI6IjVhMzNiMzE1NDFlYWU1MDk2MDRhNTE0ZiIsIm5hbWUiOiJyYXMiLCJlbWFpbCI6InJhc0BwLmQiLCJ1c2VybmFtZSI6InJwdWxzIiwicGFzc3dvcmQiOiIkMmEkMTAkTFR5eS50TmdualB2a0htWXNuUkV0TzRwWEVEL0gycDJCYWRDRlVXMUtWaXI5TnltSUtnVUciLCJfX3YiOjB9LCJpYXQiOjE1MTMzNDI1MjksImV4cCI6MTUxMzk0NzMyOX0.amCkcFWtPwf_HYUxpXOPvcRShksH9fw7O4vVOsBm4yA",
"user":{
"id":"5a33b31541eae509604a514f",
"name":"n",
"username":"testUser",
"email":"bla#bla"}
}
I have tried to grab that token exactly like this:
JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7Il9pZCI6IjVhMzNiMzE1NDFlYWU1MDk2MDRhNTE0ZiIsIm5hbWUiOiJyYXMiLCJlbWFpbCI6InJhc0BwLmQiLCJ1c2VybmFtZSI6InJwdWxzIiwicGFzc3dvcmQiOiIkMmEkMTAkTFR5eS50TmdualB2a0htWXNuUkV0TzRwWEVEL0gycDJCYWRDRlVXMUtWaXI5TnltSUtnVUciLCJfX3YiOjB9LCJpYXQiOjE1MTMzNDI1MjksImV4cCI6MTUxMzk0NzMyOX0.amCkcFWtPwf_HYUxpXOPvcRShksH9fw7O4vVOsBm4yA
and add it as an Authorization header, but I still get Unauthorized ..... It is really difficult to proceed because I get absolute no error messages.
Here is my passport code:
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const User = require('../models/user');
const config = require('../config/database');
module.exports = function (passport) {
let opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
User.getUserById(jwt_payload._id, (err, user) => {
if (err) {
return done(err, false);
console.log(err);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
}
is this line the problem? opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken(); I have also tried with ExtractJwt.fromAuthHeaderWithScheme("jwt") instead, but same result.
Here is my Authenticate code (which i guess woks since i get back a token on login with correct user/pass)
// Authenticate
router.post('/authenticate', (req, res, next) => {
const username = req.body.username;
const password = req.body.password;
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) {
const token = jwt.sign({data: user}, config.secret, {
expiresIn: 604800 // 1 week
});
res.json({
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: 'Wrong password' });
}
});
});
});
Could also be that I'm doing something wrong in post man when making the GET request?
I really wanted this to work, but it seems that the new version of passport and passport-jwt have poor documentation, and is very difficult to debug....
I uninstalled both modules and installed express-jwt instead which by default requires a token for all routes. To allow my login and register routes i simply had to use one line of configuration.
const expressJWT = require('express-jwt')
.
.
.
app.use(expressJWT({secret: secret}).unless({path : ['/api/authenticate', '/api/register']}))
I can recommend this solution for securing routes in the sense that it is very easy to setup and use. I have no knowledge however about how secure it is compared to passport.

passport-jwt 401 Unauthorized

I'm trying to implement passport-jwt authentication but I'm always getting 401 Unauthorized when trying to call the endpoint.
Here is my setup
passport.js
var passport = require('passport');
var User = require('../models/user');
var config = require('./auth');
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var LocalStrategy = require('passport-local').Strategy;
var localOptions = {
usernameField: 'email'
};
var localLogin = new LocalStrategy(localOptions, function(email, password, done) {
User.findOne({
email: email
}, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, { error: 'Login failed. Please try again' });
}
user.comparePassword(password, function(err, isMatch) {
if (err) {
return done(err);
}
if (!isMatch) {
return done(null, false, { error: 'Login Failed. Please try again.' });
}
user.status = 'online';
user.save(function(err, user) {
if (err) {
return done(err);
}
});
return done(null, user);
});
});
});
var jwtOptions = {
jwtFromRequest: ExtractJwt.fromHeader('Authorization'),
secretOrKey: config.secret
};
var jwtLogin = new JwtStrategy(jwtOptions, function(payload, done) {
console.log(payload);
User.findById(payload._id, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user)
} else {
done(null, false);
}
});
});
passport.use(localLogin);
passport.use(jwtLogin);
module.exports = {
initialize: () => passport.initialize(),
authenticateJWT: passport.authenticate('jwt', { session: false }),
authenticateCredentials: passport.authenticate('local', { session: false }),
};
user.js
var express = require('express');
var router = express.Router();
var AuthController = require('../controllers/authentication');
var passportService = require('../config/passport');
var passport = require('passport');
const requireToken = passportService.authenticateJWT;
const requireCredentials = passportService.authenticateCredentials;
router.post('/signup', AuthController.register);
router.post('/signin', requireCredentials, AuthController.login);
router.get('/protected', requireToken function(req, res, next){
res.send({msg:'Success!'});
});
module.exports = router;
I've made sure that my header contains: 'JWT ' + [some token]...
Also tried without the 'JWT ' still nothing...
I've checked the other posts about the same problem but still can't resolve it.
SHORT
Legacy
'JWT ' + [some token]
Version 0.4.0
'bearer ' + [some token]
EXAMPLE
So when you send the tokens now is like this:
Legacy
res.json ({ success: true, token: 'JWT ' + token })
Version 0.4.0
res.json ({ success: true, token: 'bearer ' + token })
In depth
There may be other ways to do this as well
If you look in the /node_module/passport-jwt/lib/extract_jwt.js file you can see that theres a function called versionOneCompatibility(options)
After many hours of trying finally I've managed to fix the problem by using the ExtractJwt.fromAuthHeaderWithScheme('Bearer') method. For some reason the extractor wasn't able to get the token with the other methods.
I've got the same issue and after researching i figured out what causing this problem.
When you creating you res.json inside the route
//Authenticate
router.post('/authenticate', (req, res, next) => {
const username = req.body.username;
const password = req.body.password;
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) {
const token = jwt.sign(user.toJSON(), config.secret, {
expiresIn: 604800 // 1 week
});
res.json({
success: true,
token: 'jwt ' + token, //Here you have to put space after name inside quotes
user: {
id: user._id,
name: user.name,
username: user.username,
email: user.email
}
});
} else {
return res.json({
success: false,
msg: 'Wrong password'
});
}
});
});
});
inside the token:'jwt ' + token,it's really important to put a space behind the name jwt,and also the name you put inside ' ' quotation mark that name you have to put inside your
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('jwt');//Here you have to put the same name inside quotes '' like you put inside token but without space after name
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
console.log(jwt_payload);
User.findOne({
id: jwt_payload.sub
}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
These two have to match,it doesn't matter if you put jwt or JWT or bearer or anything else,the important thing is that they have to match,and there has to be space when you are creating token:'name '.

Resources