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
Related
I use passport-jwt strategy here:
const JWTstrategy = require("passport-jwt").Strategy;
const ExtractJWT = require("passport-jwt").ExtractJwt;
module.exports = function (passport) {
const opts = {
jwtFromRequest: ExtractJWT.fromUrlQueryParameter("secret_token"),
secretOrKey: "nodeauthsecret",
};
passport.use(
new JWTstrategy(opts, function (token, done) {
try {
return done(null, token);
} catch (error) {
done(error);
}
})
);
};
Login:
const signin = (req, res) => {
const email = req.body.email;
const password = req.body.password;
pool.query(
"SELECT id, email, password FROM users WHERE email=$1",
[email],
(err, result) => {
if (err) {
throw err;
}
if (result.rows.length > 0) {
const first = result.rows[0];
bcrypt.compare(password, first.password, function (err, results) {
if (results && !err) {
var token = jwt.sign(
{ id: first.id },
"nodeauthsecret",
{ expiresIn: 86400 }
);
jwt.verify(token, "nodeauthsecret", function (err, data) {
console.log(err, data);
});
res.status(200).json({ success: true, token: "JWT " + token });
} else {
res.status(401).send({
success: false,
msg: "Authentication failed. Wrong password.",
});
}
});
} else {
res.status(401).send("No user found with this email.")
}
}
);
};
Route i'm trying to access:
router.get('/secret', passport.authenticate('jwt',{session: false}),(req,res,next)=>{
res.json("Secret Data")
})
Passport imported in routes
const passport = require('passport')
require('../auth/auth')(passport)
When i try to access /secret with the token as the 'secret_token' query parameter,
I get:
TypeError: req.logIn is not a function
at JwtStrategy.strategy.success (/app/node_modules/passport/lib/middleware/authenticate.js:247:13)
at verified (/app/node_modules/passport-jwt/lib/strategy.js:115:41)
at JwtStrategy._verify (/app/auth/auth.js:18:16)
at /app/node_modules/passport-jwt/lib/strategy.js:123:34
at /app/node_modules/jsonwebtoken/verify.js:223:12
at getSecret (/app/node_modules/jsonwebtoken/verify.js:90:14)
at Object.module.exports [as verify] (/app/node_modules/jsonwebtoken/verify.js:94:10)
at Function.module.exports [as JwtVerifier] (/app/node_modules/passport-jwt/lib/verify_jwt.js:4:16)
at /app/node_modules/passport-jwt/lib/strategy.js:104:25
at JwtStrategy._secretOrKeyProvider (/app/node_modules/passport-jwt/lib/strategy.js:40:13)
Any ideas on what i'm missing, or?
I had some issues yesterday, then I dowgraded passport to version 0.4.0
and, everything worked
Here are my dependencies
"jsonwebtoken": "^8.5.1",
"passport": "^0.4.0",
"passport-jwt": "^4.0.0"
Without passport, I have added simple middleware to authorize user as below.
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
const authHeader = req.get('Authorization');
const token = authHeader.split(' ')[1];
let jwttoken;
try {
jwttoken = jwt.verify(token, 'secret');
} catch (err) {
err.statusCode = 500;
throw err;
}
if (!jwttoken) {
const error = new Error('Not authenticated.');
error.statusCode = 401;
throw error;
}
req.userId = jwttoken.userId;
next();
};
with passport, I have added middleware as below
const options = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'SECRET'
}
module.exports = (passport) => {
passport.use(new Strategy(options, async (payload, done) => {
await user.findByPk(payload.userId).then(user => {
if (user) {
return done(null, user);
}
return done(null, false);
}).catch(error => {
return done(null, error);
});
}));
}
Question: like I was adding user to request without passport as req.userId = jwttoken.userId, how can we do it with passport middleware?
At passport this is accomplished with that line of code on your module.exports:
return done(null, user);
This tells passport that the information should be sent to the next function callback at req.user.
So for example, if you "user.findByPk(payload.userId)" response is something like:
{
"name": <NAME>
"profile": <profile>
}
At your protected endpoint's callback, you should see it on req.user.
For example:
app.post('/profile', passport.authenticate('jwt', { session: false }),
function(req, res) {
res.send(req.user.profile);
}
);
With req.user.profile being equal to of the "user.findByPk(payload.userId)" response.
This is passport function to extract headers. I am using fromAuthHeaderWithScheme one, I already tried fromAuthHeaderAsBearerToken with bearer token as well. I could not make it work no matter what?
const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt,
User = require('../models/user'),
Config = require('../config/database');
module.exports = function(passport) {
let opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme("JWT");
opts.secretOrKey = Config.secret;
//Code only comes until here.
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
console.log(jwt_payload);//Code never reaches here.
User.getByUserId({
id: jwt_payload._id
}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
}
Next is my getUserById function
module.exports.getByUserId = function(id, cb) {
User.findById(id, cb)
}
Next, is where above two gets called:
router.post('/login', function(req, res) {
let username = req.body.username;
password = req.body.password;
User.getByUserName(username, function(err, user) {
if (err) {
throw err;
}
if (!user) {
return res.json({
success: "false",
msg: "User not found"
})
}
//if found compareUser to regiestred one
User.comparePassword(password, user.password, function(err, isMatched) {
if (err) {
throw err;
}
if (isMatched) {
const token = jwt.sign(user.toJSON(), CONFIG.secret, {
expiresIn: 3600 /*Logout in 1 hour*/
});
res.json({
success: "true",
token: 'JWT ' + token,
user: user._id,
email: user.email,
username: user.username,
});
} else {
return res.json({
success: "false",
msg: " Password not Matched"
});
}
});
});
});
And these are comparePassword and getUserByName incase you need to see:
module.exports.comparePassword = function(typedPassword, hash, cb) {
bcrypt.compare(typedPassword, hash, (err, isMatched) => {
if (err) {
throw err;
}
return cb(null, isMatched);
})
};
module.exports.getByUserName = function(username, cb) {
const query = {
username: username
}
User.findOne(query, cb);
}
The secret key is same every where, that is not the issue. I cannot seem to figure out the issue.
router.get("/profile", passport.authenticate('jwt', {
session: false
}, function(req, res, next) {
res.json({
success: true,
message: "This is user profile",
user: req.user
});
}));
Now, above is how I authenticate, using postman and sending request as content type "Authorization" and The token. Encase, any of you are wondering, I already tried 'bearer '+token through bearer scheme.
I changed the first code block I posted above to this
const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt,
keys = require('./keys'),
mongoose = require('mongoose'),
User = require('../models/User');
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken('Bearer');
opts.secretOrKey = keys.secretOrKey;
module.exports = passport => {
passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
User.findOne({ id: jwt_payload.sub }, (err, user) => {
User.findById(jwt_payload.id)
.then(user => {
if (user) {
return done(null, user);
}
return done(null, false);
})
.catch(err => console.log(err));
});
}))
};
And second block to this. Basically change the token from 'JWT' to 'Bearer'.
router.post('/login', (req, res) => {
const email = req.body.email, password = req.body.password;
User.findOne({ email: email })
.then(user => {
if (!user) {
res.status(404).json({ msg: 'User not found' })
}
//Check password
bcrypt.compare(password, user.password)
.then(isMatch => {
if (isMatch) {
//User found
//Create Jwt Payload
const payload = {
id: user.id,
name: user.name,
avatar: user.avatar
}
jwt.sign(
payload,
keys.secretOrKey,
{ expiresIn: 3600 },
(err, token) => {
res.json({
success: true,
token: 'Bearer ' + token
});
});
} else {
return res.status(400).json({ password: 'Password do not match' })
}
})
// .catch(err => console.log(err));
})
});
Now its working for me.
I'm making authorizing with token and when I test with postman, I keep getting 'Unauthorized' after logged in and go to profile
Login code:
router.post('/authentication', (request, response, next) => {
const email = request.body.email;
const password = request.body.password;
userModel.getUserByEmail(email, (err, user) => {
if (err) throw err;
else {
if (!user) {
console.log('User does not exists!');
response.json({ success: false, msg: 'User does not exists!' });
} else {
userModel.comparePassword(password, user.password, (err, isMatch) => {
if (err) throw err;
else {
if (!isMatch) {
response.json({ success: false, msg: 'Password do not match!' });
} else {
const token = jwt.sign(user, config.secret, {
expiresIn: 86400 // 1 day
});
response.json({
success: true,
token: "JWT " + token,
user: {
id: user._id,
name: user.name,
email: user.email,
role: user.role
}
});
}
}
});
}
}
});
});
passport.js
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const userModel = require('../models/usersModel');
const config = require('../config/database');
module.exports = function (passport) {
let opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
userModel.getUserById(jwt_payload._doc._id, (err, user) => {
console.log(jwt_payload);
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
}
tested route :
router.get('/profile', passport.authenticate('jwt', { session: false }), (request, response, next) => {
response.json({ user: request.user });
});
(PS: I tried console.log(jwt_payload); and shows nothing in console. Still stucking in this please help. I'm a starter. )
After you get your jwt token, you should send it in every request.
header: authorization: JWT "token"
Are you doing it?
1) Once user is authenticated, How can I set the token in a cookie so that user does not send username password in each request?
2) What is the ideal way of sending token to the client side?
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 claims = {
sub: user._id,
email:user.email,
iss: 'https://NodeLogin.com',
permissions: user.role
};
var token = jwt.sign(claims, config.secret, {
expiresIn: 60 // in seconds
});
res.json({ success: true, token: 'JWT ' + token });
} else {
res.send({ success: false, message: 'Authentication failed. Passwords did not match.' });
}
});
}
});
});
apiRoutes.get('/dashboard',
passport.authenticate('jwt', { session: false }), function (req, res) {
res.send('Worked' + req.user._id + '.');
});
you should follow code:
user.comparePassword(req.body.password, function (err, isMatch) {
if (isMatch && !err) {
// Create token if the password matched and no error was thrown
var claims = {
sub: user._id,
email:user.email,
iss: 'https://NodeLogin.com',
permissions: user.role
};
var token = jwt.sign(claims, config.secret, {
expiresIn: 60 // in seconds
});
res.cookie('jwt',token); // add cookie here
res.json({ success: true, token: 'JWT ' + token });
} else {
res.send({ success: false, message: 'Authentication failed. Passwords did not match.' });
}
});
and passport config:
var cookieExtractor = function(req) {
var token = null;
if (req && req.cookies) token = req.cookies['jwt'];
return token;
};
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = cookieExtractor; // check token in cookie
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);
}
});
}));
};
it's working for me :)
For httpOnly, signed, secure Cookies you might need to use signedCookies
const cookieExtractor = function (req) {
let token = null;
if (req && req.signedCookies && req.signedCookies.jwt) {
token = req.signedCookies['jwt']['token'];
}
return token;
};