Nodejs/mongodb- Checking if a user has Admin privileges (token based auth)

10,594

Solution 1

I am in the coursera class as well. The thorough explanations provided by the other answers clearly show a deep understanding of these tools, but I think they might be overkill for this assignment. To quote Professor Jogesh's response on the coursera message board to a thread with a similar issue:

"Do you make sure that you first called verifyOrdinaryUser before you called verifyAdmin? The two have to be chained one after another.

From the error, it looks like req.decoded is not available. It means that verifyOrdinaryUser was not called. This function adds the decoded property to req."

Your code from verify.js and app.js look correct and shouldn't need changing. However, when calling your verifyAdmin function in your routers, be sure to always include verifyAdmin after calling verifyOrdinaryUser like so:

.get(Verify.verifyOrdinaryUser, Verify.verifyAdmin, function(req, res, next) {

Because req.decode is established in verifyOrdinaryUser, when you call verifyAdmin without verifyOrdinaryUser first, your decode remains undefined. You can make your verifyAdmin function more thorough like the other answers suggest, but again, it isn't necessary for this assignment.

Solution 2

I also stuck in the same problem. Here is the snippet of verify.js for verifyAdmin()

exports.verifyAdmin = 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'];

// verifies secret and checks exp
jwt.verify(token, config.secretKey, function (err, decoded) {
if (err) {
  var err = new Error('You are not authenticated!');
  err.status = 401;
  return next(err);
} else {
  // They are an admin
  if (decoded._doc.admin){
    return next();
  } else {
    // They are not an admin
    var err = new Error('You are not authorized to perform this operation!');
    err.status = 403;
    return next(err);
  }
}
});
};

Solution 3

I´m doing the same Coursera course that you are. I have just finished this assignment! You should do this: - In your verify.js file add this:

exports.verifyAdmin = function (req, res, next) {

    if (req.decoded._doc.admin == true) {
        next();
    } else {
        // if the user is not admin
        // return an error
        var err = new Error('You are not authorized to perform this operation!');
        err.status = 403;
        return next(err);
    }

};

Then in your router files (like dishRouter.js) you should use this middleware injections:

dishRouter.route('/')
.get(Verify.verifyOrdinaryUser, function(req,res,next){
    //res.end('Will send all the dishes to you!');
    Dishes.find({}, function (err, dish) {
      if (err) throw err;
      res.json(dish);
    });
})

.post(Verify.verifyOrdinaryUser, Verify.verifyAdmin, function(req, res, next){
    //res.end('Will add the dish: ' + req.body.name + ' with details: ' + req.body.description);
    Dishes.create(req.body, function (err, dish) {
      if (err) throw err;
      console.log('Dish created!');
      var id = dish._id;

      res.writeHead(200, {
          'Content-Type': 'text/plain'
      });
      res.end('Added the dish with id: ' + id);
    });
})

.delete(Verify.verifyOrdinaryUser, Verify.verifyAdmin, function(req, res, next){
    //res.end('Deleting all dishes');
    Dishes.remove({}, function (err, resp) {
      if (err) throw err;
      res.json(resp);
    });
});

Solution 4

your code looks fine, only must edit the verifyAdmin function to this:

exports.verifyAdmin = function(req, res, next){
    var isAdmin = req.decoded._doc.admin
    if (isAdmin) {
        return next();
    }
    else {
        // if user is not admin
        // return an error
        var err =  new Error ('You are not autorized to perform this   operation!');
        err.status =  403;
        return next(err);

    }
}

In your case the error is because the variable "error" is not defined in the code.

Regards.

Solution 5

req.decoded._doc.admin NOT WORKING

But you can use this function.

jwt.decode(token [, options]);

Example for course, it works for me:

exports.verifyAdmin = 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'];

    // get the decoded payload and header "req.decoded._doc.admin" NOT WORKING
    var decAdmin = jwt.decode(token, { complete: true });

    // decode token
    if (token) {
        // verifies secret and checks exp
        jwt.verify(token, config.secretKey, function(err, decoded) {
            if (err || !decAdmin.payload._doc.admin) {
                var err = new Error('You are not authorized to perform this operation!');
                err.status = 403;
                return next(err);
            } 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
        var err = new Error('No token provided!');
        err.status = 403;
        return next(err);
    }
};
Share:
10,594
forkinspace
Author by

forkinspace

Avid learner, web programmer wannabe and tech enthusiast.

Updated on August 03, 2022

Comments

  • forkinspace
    forkinspace over 1 year

    In my express/mongoose app I'm defining verifyOrdinaryUser function to check if a user is authenticated on a server. Which works well, however I've defined verifyAdmin function below to check if a user has admin privileges also (I'm using passport-local-mongoose module to define user Schemas). As you can see user's token is checked in verifyOrdinaryUser() function, it will load a new property named decoded to the request object which I'm trying to reuse in verifyAdmin, and that's when I'm getting the following error in postman.

    {
      "message": "Cannot read property '_doc' of undefined",
      "error": {}
    }
    

    The following is the

    var User = require('../models/user');
    var jwt = require('jsonwebtoken'); 
    var config = require('../config.js');
    
    exports.getToken = function (user) {
        return jwt.sign(user, config.secretKey, {
            expiresIn: 3600
        });
    };
    
    exports.verifyOrdinaryUser = 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'];
    
        // decode token
        if (token) {
            // verifies secret and checks exp
            jwt.verify(token, config.secretKey, function (err, decoded) {
                if (err) {
                    var err = new Error('You are not authenticated!');
                    err.status = 401;
                    return next(err);
                } 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
            var err = new Error('No token provided!');
            err.status = 403;
            return next(err);
        }
    };
    
    exports.verifyAdmin = function(req,res,next){
        if(req.decoded._doc.admin !== true)  {
            return next(err);
        }else {
            return next();
        }
    };
    

    I'm sure I've messed up something in the verifyAdmin function. Middleware order looks right to me Suggestions are welcome

    thank you

    EDIT: Middleware goes here from app.js

    app.set('views', path.join(__dirname, 'views'));
    app.set('view engine', 'jade');
    
    app.use(logger('dev'));
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: false }));
    app.use(cookieParser());
    
    // passport config
    var User = require('./models/user');
    app.use(passport.initialize());
    passport.use(new LocalStrategy(User.authenticate()));
    passport.serializeUser(User.serializeUser());
    passport.deserializeUser(User.deserializeUser());
    
    app.use(express.static(path.join(__dirname, 'public')));
    
    app.use('/', routes);
    app.use('/users', users);
    app.use('/dishes',dishRouter);
    app.use('/promotions',promoRouter);
    app.use('/leadership',leaderRouter);
    
  • forkinspace
    forkinspace about 8 years
    I am assigning different privileges to different http requests, which is done in ...Router.js files and that's where I had to call middleware something like the following .get(Verify.verifyOrdinaryUser, Verify.verifyAdmin, function(req,res,next){...} so after calling verifyOrdinaryUser it would return the decoded in the request which I could make use of when I would call verifyAdmin. I was calling verifyAdmin without first checking if user is authenticated, in other words first calling the verifyOrdinaryUser. Thanks for your efforts anyway.
  • ohid
    ohid about 8 years
    @ForkInSpace, in my case your way only worked for admin user. In the ordinary user it gives following error: { "message": "err is not defined", "error": {} }
  • mehmet riza oz
    mehmet riza oz over 7 years
    I tried and this worked for me. It is almost the same with the verifyOrdinaryUser code except if (err || !decAdmin.payload._doc.admin) line. But good point to catch. Thank you.
  • tcoulson
    tcoulson over 7 years
    I followed all of these codes and I am still getting the error. Here is some of my code in dishRouter and users respectively: .delete(Verify.verifyOrdinaryUser, Verify.verifyAdmin, function (req, res, next) and if(req.decoded._doc.admin === true){
  • double-beep
    double-beep about 4 years
    While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.