Reputation: 3064
I'm trying to setup JWT authentication using passport-jwt. I think I've taken the right steps, but a test GET won't succeed and I don't know how to debug it.
Here's what I've done:
setup passport-jwt straight out of the doc as much as possible
var jwtOptions = {
secretOrKey: 'secret',
issuer: "accounts.examplesoft.com", // wasn't sure what this was, so i left as defaulted in the doc
audience: "yoursite.net" // wasn't sure what this was, so i left as defaulted in the doc
};
jwtOptions.jwtFromRequest = ExtractJwt.fromAuthHeader();
passport.use(new JwtStrategy(jwtOptions, function(jwt_payload, done) {
User.findOne({id: jwt_payload.sub}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
// or you could create a new account
}
});
}));
Added a token result to my user /login endpoint
var jwt = require('jsonwebtoken');
// ...
exports.postLogin = function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) throw err;
if (!user) {
return res.send({ msg: 'Login incorrect' });
}
req.logIn(user, function(err) {
if (err) throw err;
var secretOrKey = jwtOptions.secretOrKey;
var token = jwt.sign(user, secretOrKey, {
expiresIn: 631139040 // 20 years in seconds
});
res.send({ user: user, jwtToken: "JWT " + token });
});
})(req, res, next);
};
Things were looking good up to here. I can login a user (using passport local auth) and the response was a I hoped...
{ "user": { "_id": "56c8b5bd80d16ef41ec705dd", "email": "[email protected]", "password": "$2a$10$zd ... etc.", "__v": 0, }, "jwtToken": "JWT eyJ0eXAiOiJ .... etc." }
I created an unprotected test route like this...
// in my routes file
app.get('/user/tokenTest', user.tokenTest);
And in my controller, a simple endpoint...
exports.tokenTest = function(req, res) {
console.log(req.headers);
res.send("token test!!");
};
And GET-ing that works fine, too.
But then I try to protect that route like this:
app.get('/user/tokenTest', passport.authenticate('jwt', { session: false }),
user.tokenTest);
After I do that, nothing but sadness. I send a request like this:
curl -k 'https://localhost:3443/user/tokenTest' -H 'Authorization: JWT eyJ0eXAiOiJ... etc.'
And always, always get a 401:
Unauthorized
Console logs in the controller don't seem to execute, neither does logging in the passport.use
strategy method. I've tweaked and tweaked, but I'm a little lost. The passport-jwt doc just supplies the example, and virtually no other help.
Please, any ideas about either a mistake that I'm making above, or at least how to go about debugging??
Upvotes: 14
Views: 13271
Reputation: 462
I may be late but I had a similar problem, and I have another solution. You can use this options.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('JWT')
to extract the JWT token from authentication header with the following format:
Authorization: JWT JSON_WEB_TOKEN_STRING.....
Here is the documentation I used: https://github.com/themikenicholson/passport-jwt
Extracting the JWT from the request
There are a number of ways the JWT may be included in a request. In order to remain as flexible as possible the JWT is parsed from the request by a user-supplied callback passed in as the jwtFromRequest parameter. This callback, from now on referred to as an extractor, accepts a request object as an argument and returns the encoded JWT string or null. Included extractors
A number of extractor factory functions are provided in passport-jwt.ExtractJwt. These factory functions return a new extractor configured with the given parameters.
fromHeader(header_name) creates a new extractor that looks for the JWT in the given http header fromBodyField(field_name) creates a new extractor that looks for the JWT in the given body field. You must have a body parser configured in order to use this method. fromUrlQueryParameter(param_name) creates a new extractor that looks for the JWT in the given URL query parameter. fromAuthHeaderWithScheme(auth_scheme) creates a new extractor that looks for the JWT in the authorization header, expecting the scheme to match auth_scheme. fromAuthHeaderAsBearerToken() creates a new extractor that looks for the JWT in the authorization header with the scheme 'bearer' fromExtractors([array of extractor functions]) creates a new extractor using an array of extractors provided. Each extractor is attempted in order until one returns a token.
Upvotes: 0
Reputation: 3064
For any poor soul that follows me here: the passport-jwt doc implies that the auth header should look like this...
Authorization: JWT JSON_WEB_TOKEN_STRING.....
That turned out to be misleading (for me, anyway).
Fortunately, thanks to this article I was able to learn how the token is built. (The token's prefix up to the first '.' is the base64 encoding of the scheme. That "JWT " at the front was noise that prevented the validation from working.
So the fix was to change the token returned by the user controller from:
res.send({ user: user, jwtToken: "JWT " + token });
To the simpler:
res.send({ user: user, jwtToken: token });
Phew. Is it me, or is it really a bummer how inadequately these things are explained in so many node package docs??
Upvotes: 27