I build an application API with expressJS. In this application there is a public part and an private part.
Both part need to display a list of user. But only the private part can display the full list.
I use express-authorize middlewar to handle autorization. So i have defined two authorization :
'public:users:list'
and
'private:users:list'
In express i have tried to write routes like this :
router.route('/users')
.get([passport.authenticate('jwt', { session: false }), authorization.authorizer.isPermitted('private:users:list')], (req, res) => {
getUserList(req, res);
})
.get([passport.authenticate('jwt', { session: false }), authorization.authorizer.isPermitted('public:users:list')], (req, res) => {
req.query.filter.roles.name = 'user'
getUserList(req, res);
});
But this does not work. Request fail as unauthorized on the first get and never goes to the second. Is there a mean to make this works? Or any others techniques. If possible avoid to create another endpoint like (router.route('usersPublic').
Thx
EDIT:
I have override the onDenied function like this and make the change in the route.
var authorizer = new authorizer.Authorizer(authorizer.options);
authorizer.options.onDenied = function (req, res, next) { next('route') };
authorizer.options.withSubject = function (req, res, done) {}
That seems to work. But if no route are valid. i get a 404 error instead of 403. Any suggestion?
EDIT2:
onDenied should not be set at global but inside the route itself like this
router.route('/users')..get([passport.authenticate('jwt', { session: false }), authorization.authorizer.onDenied((req, res, next) => next('route')).isPermitted('private:users:list')], (req, res) => {
getUserList(req, res);
});
router.route('/users')..get([passport.authenticate('jwt', { session: false }), authorization.authorizer.isPermitted('public:users:list')], (req, res) => {
req.query.filter.roles.name = 'user'
getUserList(req, res);
});
I'm not familiar with express-authorize
, so the following is pieced together from its source and may not work.
First, you need to change how authentication mismatches are handled. The default is to redirect to /login
, but you need to change that so the request gets passed to the next route chain:
let myAuthorizer = authorization.authorizer.onDenied((req, res, next) => next('route'));
Then you need to set up two separate routes for /users
:
router.route('/users').get([passport.authenticate('jwt', { session: false }), myAuthorizer.isPermitted('private:users:list') ], (req, res) => {
getUserList(req, res);
})
router.route('/users').get([passport.authenticate('jwt', { session: false }), authorization.authorizer.isPermitted('public:users:list')], (req, res) => {
req.query.filter.roles.name = 'user'
getUserList(req, res);
});
The next('route')
is explained here, but it basically means that instead of passing the request to the next handler in the current chain, it's passed to the next full route chain (which in this case is the second router.route('/users')
if private access is denied).