JWT get user details and set expire time - node.js

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

Related

How to get current user in another file while working on postman and node.js

I have this code for login. How do I use the current user's information from this code to another file using postman and node.js?
exports.loginUser = (req,res, next) => {
User.find({email: req.body.email})
.exec()
.then(user => {
if(user.length < 1) {
return res.status(401).json({
message: 'Auth failed'
});
}
bcrypt.compare(req.body.password, user[0].password, (err ,result) => {
if(err){
return res.status(401).json({
message: 'Auth failed'
});
}
if (result) {
const token = jwt.sign({
email: user[0].email,
userId: user[0]._id
},
process.env.JWT_KEY ,
{
//options
expiresIn: "1h"
});
You should tell exactly what you want, what you said is confusing, but If you mean how to pass the logged in user to the next middleware, you gotto assign the user to req
exports.loginUser = async (req, res, next) => {
const user = await User.find({ email: req.body.email }).exec()
if (user.length < 1) {
return res.status(401).json({
message: 'Auth failed'
});
}
bcrypt.compare(req.body.password, user[0].password, (err, result) => {
if (err) {
return res.status(401).json({
message: 'Auth failed'
});
}
if (result) {
const token = jwt.sign({
email: user[0].email,
userId: user[0]._id
},
process.env.JWT_KEY, {
//options
expiresIn: "1h"
});
req.user = user[0];
return next();
}
})
}
Then in the next middleware you have access to logged in user, using req.user.
UPDATE:
To implement the functionality that you want, according to what you described in the comment:
Before anything import these packages:
const jwt = require("jsonwebtoken");
const { promisify } = require("util");
First you implement a route that checks for credentials and sends back a signed jwt:
exports.login = CatchAsync(async(req, res, next) => {
const { email, password } = req.body;
if (!email || !password) {
return next(new Error("Please provide email and password"));
}
const user = await UserModel.findOne({email});
if (!user) {
return next(new Error("There is no user with that email"));
}
if(!(await bcrypt.compare(password, user.password))) {
// actually the pass is not correct but for security reasons we don't say that
return next(new Error("Email or password is not correct");
}
// pass the user id to jwt so later can identify user
const token = jwt.sign({ id: user._id }, 'yourJwtSecret', {
expiresIn: '90d',
});
// httpOnly prevents access to token in client's browser, so it is safe
const cookieOptions = {
expires: new Date(
Date.now() + 90 * 24 * 60 * 60 * 1000
),
httpOnly: true,
};
res.cookie("jwt", token, cookieOptions);
res.status(200).json({
status: 'success',
message: 'logged in successfully'
});
});
Then for every route that needs to check for logged In user, use this middleware:
exports.isLoggedIn = CatchAsync(async(req, res, next) => {
// Check if there is a token
// if no token was provided it means user is not logged in
let token;
if (req.cookies.jwt) {
token = req.cookies.jwt;
} else {
return next();
}
// Verify token
// decoded now has access to id of user
let decoded;
try {
decoded = await promisify(jwt.verify)(token, 'yourJwtSecret');
} catch (err) {
// if token was modified or expired or not valid
return next();
}
// get the user
const user = await UserModel.findOne({
_id: decoded.id
});
// access granted, user is logged in
req.user = user; // you can access the logged in user in the next middleware
res.locals.user = user; // you can access the logged in user in template engines
next();
});
If the user is not logged in, req.user won't be assigned. therefore in next middlewares if req.user was undefined you know user is not logged in.
for more info jwt docs.
If you have never taken any NodeJs course, I'd recommend this course

Refreshing JWT access token with refresh token within single middleware function on a post route

I'm trying to learn JWT authentication in express and one thing that I'm came across this code from Github
that this guy has initialised an middleware function to authenticate and check expiry of access token as per below:
app.post("/protected", auth, (req, res) => {
return res.json({ message: "Protected content!" });
})
async function auth(req, res, next) {
let token = req.headers["authorization"];
token = token.split(" ")[1]; //Access token
jwt.verify(token, "access", async (err, user) => {
if (user) {
req.user = user;
next();
} else if (err.message === "jwt expired") {
return res.json({
success: false,
message: "Access token expired"
});
} else {
console.log(err);
return res
.status(403)
.json({ err, message: "User not authenticated" });
}
});
}
and a separate route for refreshing the access token with the help of refresh token
app.post("/refresh", (req, res, next) => {
const refreshToken = req.body.token;
if (!refreshToken || !refreshTokens.includes(refreshToken)) {
return res.json({ message: "Refresh token not found, login again" });
}
// If the refresh token is valid, create a new accessToken and return it.
jwt.verify(refreshToken, "refresh", (err, user) => {
if (!err) {
const accessToken = jwt.sign({ username: user.name }, "access", {
expiresIn: "20s"
});
return res.json({ success: true, accessToken });
} else {
return res.json({
success: false,
message: "Invalid refresh token"
});
}
});
});
So, my question is how secure it is and how can I create single middleware function that could do both authentication and refreshing access token without hitting the app.post('/refresh') as in my view it wouldn't be a smooth experience to deal with it in frontend API management within react
Edit
My middleware seems to work well but it doesn't identify the wrong refresh token and then actually getting worked on protected route
app.post('/home', authenticateUser, (req, res) => {
res.send('welcome');
});
async function authenticateUser(req, res, next) {
let token = req.headers['authorization'];
token = token.split(' ')[1];
jwt.verify(token, JWT_AUTH_TOKEN, async (err, phone) => {
if (phone) {
req.phone = phone;
next();
} else if (err) {
const refreshToken = req.body.refreshToken;
if (!refreshToken || !refreshTokens.includes(refreshToken)) {
return res.json({ message: 'Refresh token not found, login again' });
} else {
jwt.verify(refreshToken, JWT_REFRESH_TOKEN, (err, phone) => {
if (!err) {
const accessToken = jwt.sign({ phone }, JWT_AUTH_TOKEN, { expiresIn: '30s' });
return res.json({ success: true, accessToken });
} else {
return res.json({
success: false,
message: 'Invalid refresh token'
});
}
next();
});
}
} else {
console.log(err);
return res.status(403).json({ err, message: 'User not authenticated' });
}
});
}

checking Jwt token on every request?

I am developing a android aplication with nodejs and postgreSQL, at the moment i just have the login and the register.
When i do a login and everything is fine the server send me a token, that token is stored on the device SharedPreference, now my confusion is, do i need to decode this token on every request, or do i need to do it just 1 time?
in this tutorial at the end, he decodes on every route the token, but i don't need to do that when i do for example a request to register.
What is the best way to implement this?
here is my server code:
//****************************************************Begin of login request **********************************/
router.post('/login', function (req, res, next) {
if (JSON.stringify(req.body) == "{}") {
return res.status(400).json({ Error: "Login request body is empty" });
}
if (!req.body.username || !req.body.password) {
return res.status(400).json({ Error: "Missing fields for login" });
}
// search a user to login
User.findOne({ where: { username: req.body.username } }) // searching a user with the same username and password sended in req.body
.then(function (user) {
if (user && user.validPassword(req.body.password)) {
//return res.status(200).json({ message: "loged in!" }); // username and password match
var payload = { user: user };
// create a token
var token = jwt.sign(payload, 'superSecret', {
expiresIn: 60 * 60 * 24
});
// return the information including token as JSON
res.json({
success: true,
message: 'Enjoy your token!',
token: token
});
}
else {
return res.status(401).json({ message: "Unauthorized" }); // if there is no user with specific fields send
}
}).catch(function (err) {
console.error(err.stack)
return res.status(500).json({ message: "server issues when trying to login!" }); // server problems
});
});
//****************************************************End of Login request **********************************/
//****************************************************Begin of register request******************************/
router.post('/register', function (req, res, next) {
if (JSON.stringify(req.body) == "{}") {
return res.status(400).json({ Error: "Register request body is empty" });
}
if (!req.body.email || !req.body.username || !req.body.password) {
return res.status(400).json({ Error: "Missing fields for registration" });
}
var password = User.generateHash(req.body.password);
User.create({
username: req.body.username,
email: req.body.email,
password: password
}).then(function () {
return res.status(200).json({ message: "user created" });
}).catch(function (err) {
return res.status(400).send({ message: err.message }); //
}).catch(function (err) {
return res.status(400).json({ message: "issues trying to connect to database" });
})
});
//****************************************************End of register request **********************************/
module.exports = router;
If you don't want to use JWT token check for all routes, you can skip those routes.
const url = require('url');
apiRoutes.use((req, res, next) => {
const path = url.parse(req.url).pathname;
console.log(path);
//No JWT token check
if (/^\/register/.test(path)) {
return next();
}
return jwtTokenValidate();
});
function jwtTokenValidate() {
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['x-access-token'];
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, app.get('superSecret'), function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
// if everything is good, save to request for use in other routes
req.decoded = decoded;
next();
}
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
}

Node.js passport-jwt how to send token in a cookie?

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

restrict login page access if user isLoggedin using node and express

login api:
api.post('/login', function(req, res) {
User.findOne({
username: req.body.username
}).select('password').exec(function(err, user) {
if(err) throw err;
if(!user) {
res.send({ message: "User doesn't exist."});
} else if(user) {
var validPassword = user.comparePassword(req.body.password);
if(!validPassword) {
res.send({ message: "Invalid password."});
} else {
/////// token
var token = createToken(user);
res.json({
success: true,
message: "Successfully logged in.",
token: token
});
}
}
});
});
middleware:
api.use(function(req, res, next) {
console.log("somebody just came to our app.");
var token = req.body.token || req.param('token') || req.headers['x-access-token'];
// check if token exists
if(token) {
jsonwebtoken.verify(token, secretKey, function(err, decoded) {
if(err) {
res.status(403).send({ success: false, message: "failed to authenticate user."});
} else {
req.decoded = decoded;
next();
}
});
} else {
res.status(403).send({ success: false, message: "no token provided."});
}
});
authService:
authFactory.login = function(username, password) {
return $http.post('/api/login', {
username: username,
password: password
})
.success(function(data) {
AuthToken.setToken(data.token);
return data;
})
}
authFactory.isLoggedIn = function() {
if(AuthToken.getToken())
return true;
else
return false;
}
Now, if my user is logged in and he tries to access: localhost:3000/login , then he should be redirected to localhost:3000/
only after he logs out, he should be able to access the login page (similar to facebook).
How to do this?
1
In login API store user info in respond object(res).
2
app.get('/',function(req,res){
if(req.user){
res.redirect('/login');
} else {
res.render('home');
}
});
RECOMMENDED:PASSPORT and its Local-Strategy.
exports.loginPage = (req, res) => {
if(req.session.user){
res.redirect("/");
} else {
res.render("loginPage");
}
};
On your login route, add the logic inside the middleware function. You could the same on the registration page as well.
exports.registrationPage = (req, res) => {
if(req.session.user){
res.redirect("/");
} else {
res.render("registrationPage");
}
};
The key takeaway is this: if you want a user not to see a page on your site, go to the page's route, add the if/else logic.
Hope that helps, cheers!

Resources