Почему этот проверенный веб-токен JSON (JWT) выводится как неопределенный?

#javascript #node.js #jwt

#javascript #node.js #jwt

Вопрос:

Я пытаюсь декодировать JWT id_token с помощью jwks-rsa и jsonwebtoken, но результат возвращается как undefined .

Я знаю, что это как-то связано с обратными вызовами и тем фактом, что мне нужно ждать ответа от getKey функции, но я не могу понять, как структурировать код, чтобы это произошло.

Это то, что у меня есть до сих пор…

 function do_thing(properties, context) {

  const id_token = "REDACTED";

  // Verify using getKey callback
  var jwksClient = require('jwks-rsa');
  var client = jwksClient({
    jwksUri: 'https://REDACTED.com/.well-known/jwks.json'
  });

  function getKey(header, callback) {
    client.getSigningKey(header.kid, function(err, key) {
      var signingKey = key.publicKey || key.rsaPublicKey;
      callback(null, signingKey);
    });
  }

  var jwt = require('jsonwebtoken');
  jwt.verify(id_token, getKey, { algorithms: ['RS256'] }, function(err, decoded) {
    if (err) {
      console.log(err);
    } else {
      return decoded;
    }
  });
const bubble_obj = do_thing();
console.log(bubble_obj); //This is `undefined`
 

console.log(bubble_obj); Выводится как undefined .

Я знаю, что проблема с приведенным выше кодом связана с характером обратных вызовов и асинхронного кода, потому что, если я перемещу console.log внутренний jwt.verify вызов, он покажет правильно декодированный токен.

Этот пример смотрите здесь…

 function do_thing(properties, context) {

  const id_token = "REDACTED";

  // Verify using getKey callback
  var jwksClient = require('jwks-rsa');
  var client = jwksClient({
    jwksUri: 'https://REDACTED.com/.well-known/jwks.json'
  });

  function getKey(header, callback) {
    client.getSigningKey(header.kid, function(err, key) {
      var signingKey = key.publicKey || key.rsaPublicKey;
      callback(null, signingKey);
    });
  }

  var jwt = require('jsonwebtoken');
  jwt.verify(id_token, getKey, { algorithms: ['RS256'] }, function(err, decoded) {
    if (err) {
      console.log(err);
    } else {
      console.log(decoded); //When moved here, it prints the correctly decoded token
      return decoded;
    }
  });
const bubble_obj = do_thing();
 

Итак, как мне заставить его вернуть правильно декодированный токен?

Ответ №1:

Вы неправильно обрабатываете асинхронный код. jwt.verify Метод возвращает обещание, если вы не передаете ему метод обратного вызова.

Если вы используете return jwt.verify(id_token, getKey, { algorithms: ['RS256'] }) внутри do_thing функции и вызываете ее таким do_thing().then((decodedToken) => console.log(decodedToken)) образом, она должна работать так, как ожидалось.

Комментарии:

1. Теперь это выдает сообщение об ошибке, JsonWebTokenError: verify must be called asynchronous if secret or public key is provided as a callback" поэтому я попытался превратить do_thing() функцию в асинхронную функцию и сделал это return await jwt.verify(id_token, getKey, { algorithms: ['RS256'] }) , но это не устранило ошибку. Смотрите рабочий пример здесь: repl.it/join/fqcsbnck-twistedsizzler

2. Я обновил код на repl.it . Он использует асинхронную версию getSigningKey , и вызывает метод verifyToken в анонимной самозапускающейся функции

3. Это действительно помогло бы прояснить ситуацию, если бы вы прочитали об обещаниях . На высоком уровне они позволяют вам работать с асинхронными функциями без необходимости передавать обратные вызовы. Это делает ваш код намного более чистым и читаемым. Функция, которая возвращает a Promise , может использоваться либо с await его помощью, либо с помощью .then()

Ответ №2:

вам нужно обработать обещание, возвращенное jwt.verify . либо используйте promise.then . или используйте async / await.

  async function do_thing(properties, context) {

  const id_token = "REDACTED";

  // Verify using getKey callback
  var jwksClient = require('jwks-rsa');
  var client = jwksClient({
    jwksUri: 'https://REDACTED.com/.well-known/jwks.json'
  });

  function getKey(header, callback) {
    client.getSigningKey(header.kid, function(err, key) {
      var signingKey = key.publicKey || key.rsaPublicKey;
      callback(null, signingKey);
    });
  }

  const jwt = require('jsonwebtoken');
  return jwt.verify(id_token, getKey, { algorithms: ['RS256'] });
  
}

const decodedToken = await do_thing();
console.log("decoded token:", decodedToken);
 

Комментарии:

1. Это не работает, потому const decodedToken = await do_thing(); что не находится внутри асинхронной функции. Поэтому он выдает ошибку.

2. вы должны обработать свою вызывающую функцию, чтобы заставить ее работать.

Ответ №3:

Вы можете использовать обещание для проверки JWT с обратным вызовом JWK и обещать следующим образом. Вам нужно будет обернуть следующее в async функцию, чтобы использовать результат verify_jwks() функции:

 const token = "REDACTED";
var jwksClient = require('jwks-rsa');

// Creates a JWKS Client with a rate limit that
// limits the number of calls to our JWKS endpoint
var client = new JwksClient({
    jwksUri: 'https://REDACTED.com/.well-known/jwks.json',
    rateLimit: true,
    jwksRequestsPerMinute: 10, // Default Value
    cache: true, // Default Value
    cacheMaxEntries: 5, // Default value
    cacheMaxAge: 600000, // Defaults to 10m
});

// Verifies the JWKS asynchronously, returns Promise
async function verify_jwks() {
    function getKey(header, callback) {
        // Callback that returns the key the corresponding key[kid]
        client.getSigningKey(header.kid, function(err, key) {
            const signingKey = key.getPublicKey() || key.publicKey || key.rsaPublicKey;
            callback(null, signingKey);
        });
    }

    // Returns a Promise with verification result or error
    return new Promise((resolve,reject) =>
        jsonwebtoken.verify(token,getKey, {
            algorithms: ["HS256", "RS256"]
        },
        function(err,decoded) {
            return err ? reject(err) : resolve(decoded);
        }
    ));
}
            
let resu<
await verify_jwks()
    .then(decoded => result = decoded)
    .catch(error => console.log(error));
console.log(result);