Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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);
like image 791
forkinspace Avatar asked Mar 23 '16 17:03

forkinspace


3 Answers

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.

like image 148
ericArbour Avatar answered Oct 16 '22 02:10

ericArbour


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);
  }
}
});
};
like image 5
Saugat Bhattarai Avatar answered Oct 16 '22 02:10

Saugat Bhattarai


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);
    });
});
like image 4
wsoaresfilho Avatar answered Oct 16 '22 03:10

wsoaresfilho