为啥这个经过验证的 JSON Web Token (JWT) 输出未定义?

Posted

技术标签:

【中文标题】为啥这个经过验证的 JSON Web Token (JWT) 输出未定义?【英文标题】:Why does this verified JSON Web Token (JWT) output as undefined?为什么这个经过验证的 JSON Web Token (JWT) 输出未定义? 【发布时间】:2021-06-03 20:49:26 【问题描述】:

我正在尝试使用 jwks-rsa 和 jsonwebtoken 解码 JWT id_token,但结果返回为 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 方法返回一个 Promise 如果你不传递回调方法。

如果您在do_thing 函数中使用return jwt.verify(id_token, getKey, algorithms: ['RS256'] ) 并像这样调用它do_thing().then((decodedToken) => console.log(decodedToken)),它应该可以按预期工作。

【讨论】:

这现在给出了一个错误,说 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 我已经更新了 repl.it 上的代码。它使用 getSigningKey 的异步版本,并在匿名自调用函数中调用 verifyToken 方法 如果您阅读Promises,这真的有助于清除问题。在高层次上,它们允许您使用异步函数而无需传递回调。这使您的代码更加干净和可读。返回Promise 的函数可以与await 一起使用或使用.then()【参考方案2】:

你需要处理 jwt.verify 返回的 promise。要么使用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);

【讨论】:

这不起作用,因为const decodedToken = await do_thing(); 不在异步函数内。因此它会引发错误。 你必须处理你的调用函数才能让它工作。【参考方案3】:

您可以使用 Promise 通过 JWK 回调和 Promise 验证 JWT,如下所示。您需要将以下内容包装在 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 result;
await verify_jwks()
    .then(decoded => result = decoded)
    .catch(error => console.log(error));
console.log(result);

【讨论】:

以上是关于为啥这个经过验证的 JSON Web Token (JWT) 输出未定义?的主要内容,如果未能解决你的问题,请参考以下文章

在吗?认识一下JWT(JSON Web Token) ?

为啥 JSON Web Token (JWT) 在不知道密钥的情况下解密

使用 REST 和 JSON Web Token 上传/下载文件

使用 JSON Web Token (JWT) 进行身份验证和授权

json web based token在服务器端的验证流程

通过 Flutter App 和 JSON Web Token 在 Django 中验证用户