Express Middleware jsonwebtoken authentication - node.js

My server has a registration api that provides a token after registration, and a middleware that authenticates a user's token. I need to register an account to get the token to do something else with my server. However, the middleware blocks my network request because I don't have a token yet.
So how can I create my account and token in this case? Get pass the middleware with some tricks?
Middleware:
// Middleware to verify token, it will be called everytime a request is sent to API
api.use((req, res, next)=> {
var token = req.headers.token
if (token) {
jwt.verify(token, secret, (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." })
}
})
Signin:
// Sign In with email API
api.post('/signInWithEmail', (req, res)=> {
User.findOne({
email: req.body.email
}).select(userFields).exec((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 {
var token = createToken(user);
res.json({
success: true,
message: "Login Successfully!",
token: token
})
}
}
})
})

Make a function to check tokens and expose your routes such that whenever you need to call an authenticated route then you'll be checking the token first and then you'll expose the route.
Sample Code
Let's say this is my check token function
function checkToken(req, res, next) {
var x = req.token; //This is just an example, please send token via header
if (x === token)
{
next();
}
else
{
res.redirect(/unauthorized); //here do whatever you want to do
}
}
Now let's use the function for routes.
app.post('/protectedroute', checkToken, routename.functionname);
app.post('/notprotected', routename.functionname);
It's your call if you'd like to have separate routes for different codes or else you can just call specific code block via keeping them in function etc. on the main file i.e. app.js or server.js, whatever you have chosen.
What actually we are doing here is - we are making a middleware of our own to expose our routes through a channel of code blocks or functions.

Related

Add authorization header in nodejs to persist after login securely?

I wanted to know what is the best way to add authorization header in nodejs app so that client req header gets that header set from the server on login. That's, once a user logs in, server should set an authorization header with the token, hence let that header persist on all routes, so that if it is present on the client, the server can allow user to visit routes.
I have seen alot of talk about adding it as a cookie but I prefer authorization header for this scenario. In between I would want to make sure on logout token is destroyed.
I am using JWT, Passport, express, nodejs for this app. Any help would be appreciated.
let access;
/\*\*
* #route POST api/auth/login
* #desc Login user and return token
* #access public
\*/
exports.login = async (req, res, next) =\> {
try {
// Make sure this account exists
const user = await Users.findOne({ email: req.body.email }).select(
'+password',
);
if (!user)
return res.status(401).json({
message: 'Invalid email or password',
});
// Validate password
const validate = await bcrypt.compare(req.body.password, user.password);
if (!validate)
return res.status(401).json({ message: 'Invalid email or password' });
// Make sure user has been verified
if (!user.isVerified)
return res.status(401).json({
type: 'no-verified',
message: 'Your account has not been verified.',
});
const token = user.generateJWT();
access = token;
// Login user, write token, and send back user
const { password, ...data } = user._doc;
res.status(200).json({ hash: token });
} catch (err) {
res.status(500).json({ success: false, message: err.message });
}
};
exports.addHeader = (req, res, next) =\> {
console.log(access);
if (!access) {
console.log('token: undefined');
}
req.headers.authorization = 'Bearer ' + access;
next();
};
// In index.js
app.all('\*', authController.addHeader);
Tried that, could not get it to work.

How does app.get() work on the server file?

I am trying to understand how app.get() works in calling functions when trying to switch between web pages.
I've created a user-login page that assigns a token to the user and checks it in a function.
I use app.post('/login', login); to call the login function which sends the user object to the server. After creating the token I'm hoping to then render the next page in a function after checking the token. (See code below)
However, I don't really understand how app.get('/', checkToken, getProfilePage) is then called. As I don't think it ever gets called.
I've looked at some websites that explain about HTTP requests but I'm struggling to find out, how it all links together inside app.js.
App.js:
app.post('/login', login);
app.get('/', authorize.checkToken, getProfilePage);
function login(req, res, next) {
userService.login(req.body, (err, user) => {
if (err) {
res.redirect('error.ejs');
console.log(error.message);
}
console.log(user);
if (!user) {
res.status(400).json({ success: false, message: 'Username or
password is incorrect' });
}
else {
res.json(user);
}
})
}
The next login function assigns the token and is used above as middleware:
function login({ username, password }, callback) {
grabUsers((err, users) => {
let user = users.find(u => u.username === username && u.password
=== password);
if (user) {
const token = jwt.sign({ username: username }, config.secret,
{ expiresIn: '24h'
}
);
const { password, ...userWithoutPassword } = user;
user = {
...userWithoutPassword,
success: true,
message: 'Authentication successful!',
token: token
}
};
callback(null,user);
})
}
Inside authorize.js:
let jwt = require('jsonwebtoken');
const config = require('./config.js');
let checkToken = (req, res, next) => {
console.log("check token running...");
let token = req.headers['x-access-token'] ||
req.headers['authorization']; // Express headers are auto
converted to lowercase
if (token.startsWith('Bearer ')) {
// Remove Bearer from string
token = token.slice(7, token.length);
}
if (token) {
jwt.verify(token, config.secret, (err, decoded) => {
if (err) {
return res.json({
success: false,
message: 'Token is not valid'
});
} else {
req.decoded = decoded;
next();
}
});
} else {
return res.json({
success: false,
message: 'Auth token is not supplied'
});
}
};
module.exports = {
checkToken: checkToken }
getProfilePage function:
module.exports = {
getProfilePage: (req, res) => {
res.render('profile.ejs');
}
}
So my login form posts to /login and then once it has been verified I would like to check the token and then call getProfilePage. But how do I call the app.get() after the login data has been posted and authenticated?
You don't call a route from your app, what you need to do is redirect to it : res.redirect('/');
I believe there is a problem with how you try to authenticate a user. Seems to me you send a token in authorization header which is a common practice when you accessing API. Not sure how/when you generate the token and set this header though...
Anyway, this approach is good for authorization but not so good for authentication.
After successful /login request you should set the authentication cookie (user session). To simplify, you can just generate a JWT with userId encoded into it and use it as the value for this cookie (let's call it user-session).
Now after that each time user makes a request the cookie will be sent with it and you can decode the userId from this JWT. (Same thing, but now you'll take token from req.cookies['user-session'] instead of req.headers['authorization']).
But how do I call the app.get() after the login data has been posted and authenticated?
You can either navigate to this page from the client right after you receive successful /login response if you're using AJAX (i.e. window.location.replace('/')) or you can do res.redirect('/') instead of res.json(user) on successful login if you submit the HTML form without AJAX.
Redirect forces a browser to immediately make another request to the URL you specify and by that time you'll have user-session cookie set, i.e. you'll be able to retrieve userId and return correct profile page.

How to exclude a router/api url from jsonwebtoken

I am using node.js express app. jsonwebtoken for authentication. I want to exlude some api url from the jsonwebtoken verification. below is what I have tried and my code
router.use('/authentication', mountAllRoutes(authenticationModule));
// route middleware to verify a token
router.use((req, res, next) => {
const r = req;
const token = req.body.token || req.query.token || req.headers.authorization;
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, (req.app.get('superSecret')), (err, decoded) => {
if (err) {
// res.json({ success: false, message: 'Failed to authenticate token.' });
res.status(401).send({
success: false,
message: 'Failed to authenticate token.'
});
} else {
// if everything is good, save to request for use in other routes
r.decoded = decoded;
next();
// console.log(decoded);
}
return {};
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
return {};
});
router.use('/test', mountAllRoutes(testModule));
router.use('/other', mountAllRoutes(otherModule));
router.use('/data', mountAllRoutes(dataModule));
Here I have placed routes above middleware which I dont want to protect. and I have placed after middleware which I want to protect. But it is protected which I placed above middleware. In authenticationModule, login and user registration api comes. so for user registration it gives response no token provided
Note: I have refrerred this link How-to-ignore-some-request-type-in-Jsonwebtoken
create separate route file for the API you want to exclued.
//Routes
var users = require('./routes/users');
var api = require('./routes/publicApi');
App.js:
// route middleware to verify a token
router.use((req, res, next) => {
const r = req;
const token = req.body.token || req.query.token || req.headers.authorization;
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, (req.app.get('superSecret')), (err, decoded) => {
if (err) {
// res.json({ success: false, message: 'Failed to authenticate token.' });
res.status(401).send({
success: false,
message: 'Failed to authenticate token.'
});
} else {
// if everything is good, save to request for use in other routes
r.decoded = decoded;
next();
// console.log(decoded);
}
return {};
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
return {};
});
app.use('/users', router);//will use Token Authentican
app.use('/publicApi', router);//Dont do this.

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

How to ignore some request type in Jsonwebtoken

I want to ignore some API URL of being checked against token authentication
I want to protect post and put methods but not get of this url
localhost:3000/api/events/
router.use(function(request, response) {
var token = request.body.token || request.query.token || request.headers['x-access-token'];
if (token) {
jwt.verify(token, app.get(superSecret), function(err, decoded) {
if (err)
return response.json({
sucess: false,
message: "Failed token Authentication"
});
else {
request.decoded = decoded;
next();
}
});
} else {
return response.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
How can I do this using jsonwebtoken in node,express
I want this to apply to only post,put,delete requests but not on get requests.
You can move your anonymous middleware to normal declared function and then pass it to all protected routes (you decide which route you want to protect!)
Your code could look like:
function tokenProtection(request, response, next) {
var token = request.body.token || request.query.token || request.headers['x-access-token'];
if (token) {
jwt.verify(token, app.get(superSecret), function(err, decoded) {
if (err)
return response.json({
sucess: false,
message: "Failed token Authentication"
});
else {
request.decoded = decoded;
next();
}
});
} else {
return response.status(403).send({
success: false,
message: 'No token provided.'
});
}
}
and now your routes could look like (your decision what you want to protect):
router.get('/item', function(req, res) { ... }); // not protected
router.get('/item/:id', function(req, res) { ... }); // not protected
router.post(tokenProtection,'/item', function(req, res) { ... });//protected
router.put(tokenProtection,'/item', function(req, res) { ... });//protected
router.get('/book', function(req, res) { ... });// not protected
router.get('/book/:id', function(req, res) { ... });// not protected
router.post(tokenProtection,'/book', function(req, res) { ... });//protected
router.put(tokenProtection,'/book', function(req, res) { ... });//protected
Put the routes you want to protect below your authentication route and the ones you do not want to protect can above the authentication route. Something like this,
// Require what will be needed
var express = require('express'),
User = require('../models/user'),
usersRouter = express.Router();
var jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens
var config = require('./config'); // get our config file
var secret = {superSecret: config.secret}; // secret variable,
// Create a new user and return as json for POST to '/api/users'
usersRouter.post('/', function (req, res) {
var user = new User(req.body);
user.save(function(){ //pre-save hook will be run before user gets saved. See user model.
res.json({user : user, message: "Thank You for Signing Up"});
});
});
usersRouter.post('/authentication_token', function(req, res){
var password = req.body.password;
// find the user
User.findOne({
email: req.body.email
}, function(err, user) {
//If error in finding the user throw the error
if (err) throw err;
//If there is no error and the user is not found.
if (!user) {
res.json({ success: false, message: 'Authentication failed. User not found.' });
//if the user is found
} else if (user) {
// check if password matches
user.authenticate(password, function(isMatch){
if(isMatch){
// if user is found and password is right
// create a token with full user object. This is fine because password is hashed. JWT are not encrypted only encoded.
var token = jwt.sign({email: user.email}, secret.superSecret, {
expiresIn: 144000
});
// set the user token in the database
user.token = token;
user.save(function(){
// return the information including token as JSON
res.json({
success: true,
id: user._id,
message: 'Enjoy your token!',
token: token
});
});
} else {
res.json({ success: false, message: 'Authentication failed. Wrong password.' });
}
});
}
});
});
//***********************AUTHENTICATED ROUTES FOR USERS******************************
// Return ALL the users as json to GET to '/api/users'
usersRouter.get('/', function (req, res) {
User.find({}, function (err, users) {
res.json(users);
});
});
// Export the controller
module.exports = usersRouter;
I actually explained this yesterday itself on my blog because I was struggling to figure it out. If you are still not clear, you can check it out here, Node API Authentication with JSON Web Tokens - the right way.
If there are other resources like in my case it was plans. Below is the code I put above all the routes for plans I wanted to authenticate.
// route middleware to verify a token. This code will be put in routes before the route code is executed.
PlansController.use(function(req, res, next) {
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['x-access-token'];
// If token is there, then decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, secret.superSecret, function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
// if everything is good, save to incoming 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.'
});
}
});
//***********************AUTHENTICATED ROUTES FOR PLAN BELOW******************************
PlansController.get('/', function(req, res){
Plan.find({}, function(err, plans){
res.json(plans);
});
});

Resources