2
votes

I am using passport-jwt. And I'm trying to implement an acl pattern in my system . There are some different roles in my system. And I want to control their access to some resource.

I think the common way to do that in token based authentication is to write a middleware for express.js which will validate the token and add a 'role' field to 'req.user'. And then mount another middleware to every route which will specify which roles can access them. So my question is , How to combine this approach with passport-jwt .

plus: The common way of using passport-jwt strategy :

  app.get('/myapi', passport.authenticate('jwt', {session: false}),
    function(req, res, next){ ...})

The common way I think in token based authentication:

app.use(function(req, res, next){
  token = extractToken(req)
  jwt.verify(token, key, function(err, decoded) {
    if (err) {
      return res.send(err);
    } else {
      users.findOne({userId: decoded.userId}, function(err, user){
        if(!user) res.send('unknown')
        else{
          req.user = user //in which include a role field.
                      // for example: user.role = 'user' | 'manager'
        }
      })
    }
  });
})

and at every route:

function checkRole(roles){
  return function (req, res, next) {
    if (req.session.user && roles.includes(req.user.role)) {
        next();
    } else {
        res.send(403);
    }
  }
}

app.get('/myapi', checkRole(['user']), function(req, res){
  ....
})
1

1 Answers

2
votes

I figure out myself. I write a middleware to do those things together:

/*middleware/acl.js */

export function checkRoleWithPassport(roles, passport, strategy, opts){
  return function(req, res, next){
    passport.authenticate(strategy, opts, function(err, user, info){                        
      if(err) res.status(403).send('forbidden')                                             
      else if(!user) res.status(403).send('forbidden')                                      
      else{                                                                                 
        if(roles.length == 0)                                                               
          next()                                                                            
        else if(roles.includes(user.role))                                                  
          next()                                                                            
        else                                                                                
          res.status(403).send('forbidden')                                                 
      }                                                                                     
    })(req, res, next)                                                                                    
  }                                                                                         
}     

/* route/index.js */
app.get('/myapi', checkRoleWithPassport(['manager'], passport, 
  'jwt', {session, false}), function(req, res){
   ...
})   // this should only allow the 'manager' to access /myapi