How to get errors from Passport strategy to create json response?

707 views Asked by At

I'm creating an JSON API with express and I'm using Passport with the HTTP Bearer strategy for authentication.

I've added passport.authenticate('bearer', {session: false}) as middleware. But whenever something is wrong with the session it simply responds with the body Unauthorized and status 401. The actual information is in the WWW-Authenticate header, in the form of a challenge, something like: Bearer realm="Users", error="invalid_token", error_description="invalid token" And when no authentication tokens where given: Bearer realm="Users"

I'd like to change the response into json which includes the error information.

As I don't have a lot of experience writing things for the backend, I've been reading through the docs and code. The Bearer strategy simply calls fail with 400 or a challenge string. Basically there is where my issue begins, since I don't want to write a parser for these challenge strings, why isn't this information passed as regular variables?

In Passport's authenticate middleware this challenge is then, depending on your config, passed to callback, req.flash, the session and/or the WWW-Authenticate header. Looking through this code it tries to actually retrieve challenge.type and challenge.message first. When failure handling is not delegated to the application it checks whether the challenges are strings and if so puts them into the WWW-Authenticate header. This might be the reason the bearer strategy returns the challenge as string? So maybe in my case it makes sense to supply a callback, so that I can directly access these challenges? But then still, bearer's challenge is a string which requires parsing? Are people not using this bearer strategy for JSON API's?

1

There are 1 answers

0
JoshuaJ On

The default failure response in passport is here:

https://github.com/jaredhanson/passport/blob/master/lib/middleware/authenticate.js#L174

However, if you provide an authenticate callback it will run this code:

https://github.com/jaredhanson/passport/blob/master/lib/middleware/authenticate.js#L107

So the solution is to provide a callback argument to authenticate(passport, name, options, callback). You can then you can intercept the authentication failures and respond to them as you please.

Note that the res object is not actually passed to your callback. You will have to wrap your call to authenticate like so to respond:

(req, res, next) => {
  passport.authenticate('bearer', { /* options */ }, (err, user, challenges, statuses) => {
    if (statuses) {
      res
        .status(401)
        .json({
          challenges,
          statuses,
        })
        .end();
    } else {
       next(); // We are authenticated!
    }
  })(req, res, next);
}