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;
};
Related
I am persistently getting 'unauthorized' error while authenticating using a JWT. Below is my controller code:
exports.loginPost = async (req, res) => {
winston.info('Calling loginPost()...');
passport.authenticate('local', { session: false }, (err, user, info) => {
if (err) {
return utils.errorHandler(res, err);
} else if (!user) {
return utils.errorHandler(res, {
statusCode: 403,
message: 'Incorrect username or password.'
});
}
const token = jwt.sign(user, sharedSecret, { expiresIn: '24h' });
//req.user = user;
return res.json({ user, token });
// req.login(user, { session: false }, (err) => {
// if (err) {
// res.send(err);
// }
// // generate a signed json web token with the contents of user object and return it in the response
// const token = jwt.sign(user, sharedSecret, { expiresIn: '24h' });
// //req.user = user;
// return res.json({ user, token });
// });
})(req, res);
};
exports.isUserLoggedIn = async (req, res) => {
let login = {"message": "all good !"}
console.log(req)
return res.status(200).json(login);
//return res.status(200).json(req.user);
};
and passport.js strategy script is as follows:
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
}, async function (username, password, cb) {
//this one is typically a DB call. Assume that the returned user object is pre-formatted and ready for storing in JWT
try {
let user = await userService.getUserWithPassword(username, password);
console.log("passport.js: ",user);
if (!user || user.walletKey !== password) {
throw { statusCode: 403, message: 'Incorrect username or password.' };
}
// // purge password field
// delete user.currentPassword;
return cb(null, user, { message: 'Logged In Successfully' });
} catch (err) {
cb(err);
}
}));
passport.use(new JWTStrategy({
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
secretOrKey: sharedSecret,
passReqToCallback: true
},
async function (req, jwtPayload, cb) {
// Return user object from the JWTPayload
try {
let user = await userService.getUserWithPassword(jwtPayload.walletName, jwtPayload.walletKey);
console.log("passport.js: ",user);
req.user = user
return cb(null, user); //jwtPayload
} catch(err){
return cb(err,false);
}
}
));
I am able to generate token successfully, however, on calling isUserLoggedIn method using Bearer Token, it's prompting me unauthorized error. I am not making an traditional db call to login, instead I am just creating account in a Hyperledger-Indy pool nodes. Using swagger express middleware on a Node.js app.
Adding isUserLoggedIn method script below:
exports.isUserLoggedIn = async (req, res) => {
//let login = {"message": "all good !"}
console.log(req)
return res.status(200).json(req.user);
//return res.status(200).json(req.user);
};
I am using a JWT token based login system and it is working fine.
But I need to get user details based on JWT token
exports.signin = function(req, res) {
User.findOne({
username: req.body.username
}, function(err, user) {
if (err) throw err;
if (!user || !user.comparePassword(req.body.password)) {
return res.status(401).json({ message: 'Authentication failed. Invalid user or password.' });
}
return res.json({ token: jwt.sign({ email: user.email, username: user.username, _id: user._id }, 'RESTFULAPIs') });
});
};
app.use(function(req, res, next) {
if (req.headers && req.headers.authorization && req.headers.authorization.split(' ')[0] === 'JWT') {
jsonwebtoken.verify(req.headers.authorization.split(' ')[1], 'RESTFULAPIs', function(err, decode) {
if (err) req.user = undefined;
req.user = decode;
next();
});
} else {
req.user = undefined;
next();
}
});
I also need to set the expiration time.
How can I do that?
Ref : NodeJs - Retrieve user infor from JWT token?
exports.me = function(req,res){
if (req.headers && req.headers.authorization) {
var authorization = headers.authorization,
decoded;
try {
decoded = jwt.verify(authorization, secret.secretToken);
} catch (e) {
return res.status(401).send('unauthorized');
}
var userId = decoded.id;
// Fetch the user by id
User.findOne({_id: userId}).then(function(user){
// Do something with the user
return res.send(200);
});
}
return res.send(500);
}
Ref: For token expiration / extending it JWT (JSON Web Token) automatic prolongation of expiration
Web applications
A good pattern is to refresh the token before it expires.
Set the token expiration to one week and refresh the token every time the user open the web application and every one hour. If a user doesn't open the application for more than a week, they will have to login again and this is acceptable web application UX.
To refresh the token your API needs a new endpoint that receives a valid, not expired JWT and returns the same signed JWT with the new expiration field. Then the web application will store the token somewhere
Modify Your code as follow:-
exports.signin = function (req, res) {
User.findOne({
username: req.body.username
}, function (err, user) {
if (err) throw err;
if (!user || !user.comparePassword(req.body.password)) {
return res.status(401).json({ message: 'Authentication failed. Invalid user or password.' });
}
let NumberOfDayInMiliSec = 1000 * 60 * 60 * 24 * 1 //One Day
return res.json({ token: jwt.sign({ exp: Date.now() + NumberOfDayInMiliSec, email: user.email, username: user.username, _id: user._id }, 'RESTFULAPIs') });
});
};
app.use(function (req, res, next) {
if (req.headers && req.headers.authorization && req.headers.authorization.split(' ')[0] === 'JWT') {
jsonwebtoken.verify(req.headers.authorization.split(' ')[1], 'RESTFULAPIs', function (err, decode) {
if (err) req.user = undefined;
if (decode.exp < Date.now()) {
return res.status(400).json({ status: false, msg: "Token expired" });
}
req.user = decode;
next();
});
} else {
req.user = undefined;
next();
}
});
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 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
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?