5

I have an authentication route on my Node.js server that authenticates requests:

app.get('/loggedin', auth, function(req, res){
    console.log(req.authenticated);
    res.send(req.authenticated ? req.authenticated: false)
})

As I understand it, auth is run before app.get(). Here is the code for auth:

var jwt = require('jsonwebtoken');
var config = require('./config');

module.exports = function(req,res,next){
    var bearerHeader = req.headers['authorization'];
    var token;
    console.log(bearerHeader);
    req.authenticated = false;
    if (bearerHeader){
        console.log("11111");
        var bearer = bearerHeader.split(" ");
        token = bearer[1];
        jwt.verify(token, config.secret, function (err, decoded){
            console.log("22222");
            if (err){
                console.log(err);
                req.authenticated = false;
                req.decoded = null;
            } else {
                console.log("33333");
                req.decoded = decoded;
                req.authenticated = true;
            }
        });
    }
    next();
}

On the server log, however, I receive the following output:

Bearer jsflkdjlsdfjksodfkjlsdfjkls
11111
false
22222
33333

This means that there is a token on the client's side, and that is passes the jwt verification. However, the server decides to begin running app.get() before it finishes returning information in the authentication callback. What gives?

db2791
  • 1,040
  • 6
  • 17
  • 30
  • can you add a 5 second wait before calling next() when authenticating. I want to be sure it's not a log writting problem. – limbo Jul 22 '16 at 18:52
  • @limbo Just did that, output is the same. It actually pauses before calling `jwt.verify()`, so logic is immediately passed onto the code after `jwt.verify()`. – db2791 Jul 22 '16 at 18:57

3 Answers3

17

According to jwt documentation you can implement the jwt.verify() method with two options:

Asynchronous: If a callback is supplied, function acts asynchronously. The callback is called with the decoded payload if the signature is valid and optional expiration, audience, or issuer are valid. If not, it will be called with the error.

// async
jwt.verify(token, pass, (err, decoded) => { async callback } );

Synchronous: If a callback is not supplied, function acts synchronously. Returns the payload decoded if the signature is valid and optional expiration, audience, or issuer are valid. If not, it will throw the error.

//sync
try {
 const decoded = jwt.verify(token, pass);
}
catch (ex) { console.log(ex.message); }
Fabio
  • 593
  • 3
  • 14
6

Ok just fixed it. The problem is that jwt.verify() is async, so it won't do it immediately. The only way to get around this to call next() only after you've got either result:

module.exports = function(req,res,next){
    var bearerHeader = req.headers['authorization'];
    var token;
    console.log(bearerHeader);
    req.authenticated = false;
    if (bearerHeader){
        console.log("11111");
        var bearer = bearerHeader.split(" ");
        token = bearer[1];
        jwt.verify(token, config.secret, function (err, decoded){
            console.log("22222");
            if (err){
                console.log(err);
                req.authenticated = false;
                req.decoded = null;
                next();
            } else {
                console.log("33333");
                req.decoded = decoded;
                req.authenticated = true;
                next();
            }
        });
    }
}
db2791
  • 1,040
  • 6
  • 17
  • 30
  • This should be the clearest most straightforward answer to this question. The most obvious flaw in the asked question is that next() is called before the jwt verification is done. – Ramy Farid Nov 20 '21 at 23:29
1

You can use synchronous version and promisify

 const promisify = fn => {
            return (...args) => {
                return new Promise((resolve, reject) => {
                    function customCallback(err, ...results) {
                        if (err) {
                            return reject(err);
                        }
                        return resolve(results.length === 1 ? results[0] : results)
                    }

                    args.push(customCallback);
                    fn.call(this, ...args);
                })
            }
        }

// inside your code for varification

        promisify(jwt.verify)(token, process.env.JWT_SECRET)
        .then(result => {
             console.log(result);

         }).catch(err => {
            console.log(err);
         });

        next();

Rafiq
  • 8,987
  • 4
  • 35
  • 35
  • Actually, this still won't work as next() is called outside of the .then callbacks. The correct way here is that this promise resolving should govern wether to next or not. To achieve this, next() has to be called inside the .then block – Ramy Farid Nov 20 '21 at 23:33