Express: call a middleware from another middleware - node.js

I'm new to Express 4 and i'm wondering something about how to implement this thing: I'm using jwt to authenticate the consumer of my API, to do that i have a pretty simple middleware to check the validity of the jwt token:
var requireValidToken = function(req, res, next) {
var token = req.body.token || req.query.token || req.headers['x-access-token'];
if (token) {
try {
var decoded = jwt.verify(token, req.app.get('superSecret'));
} catch(err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
}
req.user = decoded.user;
next();
} else {
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
};
This is working pretty well, but now, i want to extend this to be able to check the role of the user:
router.get('/anAdminRoute', requireRole('ROLE_ADMIN'), function (req, res, next) {
// some code...
});
So i added a middleware for this:
var requireRole = function(role) {
return function(req, res, next){
// Dummy tests...
if(req.user.role == roles.admin || req.user.role == role){
next();
} else {
return res.status(403)({
success: false,
message: "Token valid, but you don't have the right permission to access this resource :)"
});
}
}
}
But as this requireRole() function while obviously checks for a valid jwt token, i'm wondering how can i call my requireValidToken middleware within this function, and so not having to explicitly call it for each route i want to protect.
An easy solution would have been not to use requireValidToken as a middleware but i still want to be able to use it to protect certain routes
Edit: Solution
Chaining middlewares is a simple as that:
var middleware2 = function(param) {
return function(req, res, next){
middleware1(req, res, function(){
// middleware2 code
});
}
}
If anybody interested, my final working solution to validate a user role:
var jwt = require('jsonwebtoken'),
roles = require('../models/user').roles;
// authentication middleware
// check if the given jwt token is valid
var requireValidToken = function(req, res, next) {
var token = req.body.token || req.query.token || req.headers['x-access-token'];
if (token) {
try {
var decoded = jwt.verify(token, req.app.get('superSecret'));
} catch(err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
}
req.user = decoded.user;
next();
} else {
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
};
var requireRole = function(role) {
return function(req, res, next){
requireValidToken(req, res, function(){
if(req.user.role == roles.admin || req.user.role == role){
next();
} else {
return res.status(403).send({
success: false,
message: "Token valid, but you don't have the right permission to access this resource :)"
});
}
});
}
}
module.exports = {
requireValidToken: requireValidToken,
requireRole: requireRole
}

Completely misread your question. If you want to call requireValidToken for certain situations, you can pass along the req and res objects to the middleware function, along with an anonymous callback. How you get the middleware function largely depends on your application architecture so I'll assume I have the requireValidToken within my context:
var requireRole = function(role) {
return function(req, res, next){
// Dummy tests...
requireValidToken(req, res, function () {
if(req.user.role == roles.admin || req.user.role == role){
next();
} else {
return res.status(403)({
success: false,
message: "Token valid, but you don't have the right permission to access this resource :)"
});
}
});
}
};

This syntax worked for me. Where auth is another middleware module
import auth from "../middlewares/auth.js";
export default function (req, res, next) {
auth(req, res, function () {
if (req.params.id !== req.user._id)
return res
.status(401)
.send({ status: "error", message: "User does not have rights to modify this data." });
next();
});
}

Related

how to get the json of a route without fetch

l have a route which return me a json with user grant.
router.get('/test', function(req, res,next ) {
var token = req.cookies.auth;
if (!token) return res.status(401).send({ message: ""});
jwt.verify(token, config.secret, function(err, decoded) {
if (err) return res.status(500).send({ message: "Failed to authenticate token."});
User.findById(decoded.id,function(err,user){
if (err) return res.status(500).send({ message: "problmes found user, sorry "});
if(!user) return res.status(404).send({message: "user not found "});
res.status(200).send({message:user.role});
});
});
});
i want to catch the json response in a variable for another route to authorize an action.
router.get('/', function(req, res, next) {
if (jsonresponse == grant ){
var allMusique;
var idMaxMusique;
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var dbo = db.db("projet_node");
dbo.collection("musiques").find({}).toArray(function(err, result) {
if (err) throw err;
allMusique = result;
var size = allMusique.length-1;
idMaxMusique = parseInt(result[size].id)+1;
res.render('musiques', { resultat: allMusique, idMax: idMaxMusique });
});
});}
else{
res.render('unauthorized');
}
});
create Controllers/middlewares/authenticate.js file and write the below code
const jwt = require('jsonwebtoken')
module.exports = (req, res, next) => {
const token = req.headers['token'] || req.body.token || req.cookies.token;
if (token) {
try {
req.decoded = jwt.verify(token, JWT_SECRET_KEY)
// JWT_SECRET_KEY -> config.secret
next()
} catch (err) {
res.status(403)
.send({ success: false, message: 'Failed to authenticate token.' })
}
}
return res.status(403).send({
success: false,
message: 'No token provided.'
})
}
create Controlller/UserController.js file
exports.getUser = function (req, res) {
// Perform Your requirement of code
// return Something
}
exports.getUserTest = function (req, res) {
// Perform Your requirement of code
// return Something
}
In Your routes/routes.js file
const authenticate = require('./Controllers/middlewares/authenticate');
const UserController = require('./Controllers/UserController');
// Routes with authentication. User must be login for get this routes
router.get('/getUser', authenticate, UserController.getUser);
// Routes without authentication. No need to login
router.post('/getUserTest', UserController.getUserTest);
Most Important require('PathOfFile') properly or Simply always double check path of file and folder in require.

Nodejs middleware for specific requests

I would like to have the middleware for these routes:
POST /tickets
PUT /tickets/:id
DELETE /tickets/:id
etc...
but currently my middleware executes on every HTTP request made on /tickets and so on:
app.use('/tickets', function(req, res, next) {
var token = req.body.token || req.query.token || req.headers['x-access-token'];
if (token) {
jwt.verify(token, app.get('superSecret'), function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
req.decoded = decoded;
next();
}
});
} else {
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
How could I use this on specific requests?
Express has post/get/delete methods for routing. More at documentation
Your should write middleware function and reuse that at routing.
Example:
function JWTCheckMiddleware(req, res, next) {
var token = req.body.token || req.query.token || req.headers['x-access-token'];
if (token) {
jwt.verify(token, app.get('superSecret'), function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
req.decoded = decoded;
next();
}
});
} else {
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
app.post('/tickets', JWTCheckMiddleware, function createTicket(req, res) {...});
app.put('/tickets/:id', JWTCheckMiddleware, function updateTicket(req, res) {...});
app.delete('/tickets/:id', JWTCheckMiddleware, function removeTicket(req, res) {...});

Node.js API with JWT authentication

I have a Node.js Express API that doesn't have any security and now I'm looking to patch on JWT-based authorization.
I have it working (using the code below) for one method. Is there an easy way to apply this to all the methods? e.g., maybe using app.all? Or do I have to add the code to every method?
app.get('/people/:id', ensureToken, (req, res, next) => {
var id = req.params.id;
getPerson(id, (err, people) => {
if (err) {
next(err);
return;
}
jwt.verify(req.token, process.env.JWT_KEY, function(err, data) {
if (err) {
res.sendStatus(403);
} else {
res
.status(200)
.set('Content-Type', 'application/json')
.set("Connection", "close")
.send(person);
}
});
});
});
function ensureToken(req, res, next) {
const bearerHeader = req.headers["authorization"];
if (typeof bearerHeader !== 'undefined') {
const bearer = bearerHeader.split(" ");
const bearerToken = bearer[1];
req.token = bearerToken;
next();
} else {
res.sendStatus(403);
}
}
You can do as:
app.use(function(req, res, next){
const bearerHeader = req.headers["authorization"];
if (typeof bearerHeader !== 'undefined') {
const bearer = bearerHeader.split(" ");
const bearerToken = bearer[1];
req.token = bearerToken;
next();
} else {
res.sendStatus(403);
}
});
I'd recommend checking out passport-jwt
You can use this as middleware as well:
app.get('/people/:id', passport.authenticate('jwt', { session: false }), (req, res, next) => { });
The advantage to this over using app.use is that you can specify which routes should be authenticated. i.e., you can exclude a login or registration route. without having to hack in checks like if (req.path == '/login')The other advantage is that using passport you can later add additional authentication methods, should you choose. There's also quite a bit of community support.
I implemented it with app.all, excluding the login route from the check:
app.all(process.env.API_BASE + "*", (req, res, next) => {
if (req.path.includes(process.env.API_BASE + "login")) return next();
return auth.authenticate((err, user, info) => {
if (err) { return next(err); }
if (!user) {
if (info.name === "TokenExpiredError") {
return res.status(401).json({ message: "Your token has expired. Please generate a new one" });
} else {
return res.status(401).json({ message: info.message });
}
}
app.set("user", user);
return next();
})(req, res, next);
});
The full solution here: https://jonathas.com/token-based-authentication-in-nodejs-with-passport-jwt-and-bcrypt/

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.

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