4
votes

After successfully logging in I am getting the jwt token, now to access restricted api's I am sending the authorization header, but I am always getting

401 Unauthorized

I have referred to this Authenticating node API with passport-jwt question asked here but didn't helped me,

this is the function I am calling for accessing data from resticted api

check() {
    console.log(this.token);
    var headers = new Headers();
    headers.append('Content-Type', 'application/json');
    headers.append("Authorization", "Bearer" +  this.token);
    let url = this.authenticationEndPoint + 'random';
    this.checkauth(url, headers).subscribe(
        data => {
            console.log(data);
        },
        error => {
            console.log("error");
        }
    );
}

checkauth(url:string, header:any): Observable<any> {

    return this.http.get(url, header)
            .map(this.extractData)
            .catch(this.handleError);
}
private extractData(res: Response) {
    let body = res;
    return body || {};
}

On my server in nodeJs here is the login code sending the jwt token:

app.post('/login_mobile', function(req, res) {
    if(!(req.body.email && req.body.passpord)) {
        user.findOne({'local.email' : req.body.email}, function(err, user) {
            if (err) { return done(err);}

            if (!user) {
                return res.json(200, "email is wrong");
            }
            if(!user.validPassword(req.body.password)) {
                return res.json(200, "wrong password");
            }
            var token = jwt.encode(user, configAuth.secret);
            return res.json(200, { user: user, jwtToken: token });
        });
    }
    else {
        res.send("no credentials provided");
    }
});

code for responding to restricted api request

app.get('/random', passport.authenticate('jwt', { session: false }),
    function(req, res) {
        res.send("success");
    });

this is the passport strategy I am using to authenticate user, but strangely it is not even printing here still it is sending 401 status.

var opts = {};
opts.secretOrKey = configAuth.secret;
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
passport.use(new JwtStrategy(opts, function(jwt_payload, done){
    console.log("here");
    console.log(jwt_payload);
    User.findOne({_id: jwt_payload._id}, function(err, found_user) {
        if (err) {
            return done(err, false);
        }
        if(found_user !== null) {
            res.send("finally");
        }
        else {
            res.send("authentication failed");
        }
        return found_user ? done(null, found_user) : done(null, false);
    })
}));

If someone can please point out my mistake.

2

2 Answers

3
votes

I fixed a similar problem replacing:

jwtFromRequest: ExtractJwt.fromAuthHeader(),

with:

jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('Bearer'),
2
votes

Update: This answer used ExtractJwt.fromAuthHeader, which is now deprecated. It has been updated to use ExtractJwt.fromAuthHeaderWithScheme('jwt')

I was having this headache and just resolved it after a couple hours of looking around.

The answer is similar to the one here:

Authenticating node API with passport-jwt

and here:

passport local strategy not getting called

What makes debugging this so difficult is that the strategies won't even run if they don't get the expected information. In the latter, the LocalStrategy verification callback wasn't being initiated because it didn't receive a username and password.

In my case, which may be the same as yours, the JWT token was not being supplied in the proper format in the header. This was because, after a successful login, I was returning a token like this:

res.status(200).json({
    token: `JWT${generateToken(userInfo)}`,
    user: userInfo,
});

When I should have been doing this:

res.status(200).json({
    token: `JWT ${generateToken(userInfo)}`,
    user: userInfo,
});

See the space after JWT. So, my jwtStrategy function was receiving something like JWTx759ghv... as the auth token, when it was expecting JWT x759ghv...

It would be handy if Passport threw a warning for this!

Here's my full setup, in case it's any help:

// passportService.js

const passport = require('passport');
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const LocalStrategy = require('passport-local');

const User = require('../models/User');
const config = require('../config');

/**
* Local Login Strategy
*/

const localOptions = {
    usernameField: 'email',
};

const localLogin = new LocalStrategy(localOptions, (email, password, done) => {
    console.log('Using Local strategy');
    console.log(email, password);
    User.findOne({ email }, (err, user) => {
        if (err) { return done(err); }
        if (!user) { return done(null, false, { message: 'Your login details could not be verified. Please try again.' }); }

        user.comparePassword(password, (passwordErr, isMatch) => {
            if (passwordErr) { return done(passwordErr); }
            if (!isMatch) { return done(null, false, { message: 'Your login details could not be verified. Please try again.' }); }
            return done(null, user);
        });
        return false;
    });
});


/**
* JWT Strategy
*/

const jwtOptions = {
    /*
     * Deprecated: fromAuthHeader() - see update above
    jwtFromRequest: ExtractJwt.fromAuthHeader(),
     */

    jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme("jwt"),
    secretOrKey: config.auth.passport.key,
};

console.log(jwtOptions);

const jwtLogin = new JwtStrategy(jwtOptions, (payload, done) => {
    User.findById(payload._id, (err, user) => {
        if (err) {
            return done(err, false);
        }
        if (user) {
            done(null, user);
        } else {
            done(null, false);
        }
    });
});

passport.use(localLogin);
passport.use(jwtLogin);

module.exports = {
    initialize: () => passport.initialize(),
    authenticateJWT: passport.authenticate('jwt', { session: false }),
    authenticateCredentials: passport.authenticate('local', { session: false }),
};

Then, in use in my routes:

// ... after of the other Express app initialization

const passportService = require('./services/passport');
const AuthenticationController = require('./controllers/Authentication');

const requireToken = passportService.authenticateJWT;
const requireCredentials = passportService.authenticateCredentials;

app.use(passportService.initialize());

authRoutes.post('/register', AuthenticationController.register);
apiRoutes.get('/protected', requireToken, AuthenticationController.login);
authRoutes.post('/login', requireCredentials, AuthenticationController.login);

If you're still having problems, try running a small piece of middleware before you call the strategy to ensure that the headers you're sending are in the correct format:

apiRoutes.get('/protected', (req, res) => {
  console.log(req.headers);
}, requireToken, AuthenticationController.login);