0

I'm struggling with migrating a HAPI function that verifies a JWT token and then makes a database call using the decoded credentials.

The problem is that jwt.verify uses a callback, but Hapi and Hapi.MySQL2 have both been updated to use async functions

The main function is as follows

exports.LoadAuth = (req, h) => {
  let token = req.headers.authorization.split(' ')[1]
  VerifyToken(token, async function (err, decoded) {
    if (!err) {
      let sql = '#SELECT STATEMENT USING decoded.id'
      const [data] = await mfjobs.query(sql, decoded.id)
      let auids = []
      data.forEach(function (ag) {
        auids.push(ag.Name)
      })
      auids = base64(auids.toString())
      return auids
    } else {
      return {message: 'Not Authorised'}
    }
  })
}

The VerifyToken function is as follows:

VerifyToken = (tok, done) => {
    jwt.verify(tok, Buffer.from(secret, 'base64'), function (err, decTok) {
      if (err) {
        done(err)
      } else {
        done(null, decTok)
      }
    })
  }

Debugging everything above works up to the point that the data should be returned to the front end. At which point I get an ERROR 500

I know that the issue is with the VerifyToken function as if I omit this and hard code the decoded.id into the query the correct data reaches the front end.

Any pointers?

Dan Kelly
  • 2,634
  • 5
  • 41
  • 61

1 Answers1

1

You can convert your VerifyToken function to Promises.

let VerifyToken = (tok) => {
    return new Promise((resolve, reject) => {
        jwt.verify(tok, Buffer.from(secret, 'base64'), function (err, decTok) {
            if (err) {
                reject(err)
            } else {
                resolve(decTok)
            }
        })
    });
}

Now you have a function that you can use with async await notation and internally checks jwt validation via callbacks.

Then we can slightly modify your controller as follows.

exports.LoadAuth = async (req, h) => {
    let token = req.headers.authorization.split(' ')[1];
    try {
        let decoded = await VerifyToken(token);
        let sql = '#SELECT STATEMENT USING decoded.id';
        const [data] = await mfjobs.query(sql, decoded.id);
        let auids = [];
        data.forEach(function (ag) {
            auids.push(ag.Name)
        });
        auids = base64(auids.toString());
        return auids
    } catch (e) {
        return {message: 'Not Authorised'}
    }
}

We just converted your handler function to async function, and we already have a VerifyToken function that returns a promise so, we can call it with the await operator.

metoikos
  • 1,315
  • 11
  • 18
  • Exactly what I was looking for. My gut feeling was that I needed to add a Promise but I could't for the life of me spot how. Doesn't help that the jwt docs don't reflect this option. – Dan Kelly Dec 05 '18 at 12:02
  • 1
    I'm glad it helped. By the way, there is a hapi plugin called hapi-auth-jwt2 it is so much easier to integrate jwt authentication to your hapi application, fyi. – metoikos Dec 05 '18 at 13:27
  • We're actually using hapi-auth-jwt2 on the route authentication. I suspect it got added to the project after the bit I'm working on at the moment. I'm at that point of a migration project where I'm trying to get things working as they did on one hand, whilst noting down things that need to be updated / revised on the other... – Dan Kelly Dec 05 '18 at 14:36