使用 NodeJS 在 Azure AD B2C 中验证 JWT 的签名

Rom*_*iev 1 node.js azure-active-directory azure-ad-b2c

我正在按照这篇文章在我们的 Node.JS 应用程序中实现 Azure B2C。我取回了 JWT 令牌并尝试验证签名。使用 jsonwebtoken npm 模块来验证我的令牌。此外,我从 OpenID Connect 元数据端点获得了公钥。它们采用 JSON 格式,如下所示:

{   "keys": [{
        "kid": "some kid value",
        "nbf": some number,
        "use": "sig",
        "kty": "RSA",
        "e": "AQAB",
        "n": "some long key"
    }, {
        "kid": "some kid value",
        "nbf": some number,
        "use": "sig",
        "kty": "RSA",
        "e": "AQAB",
        "n": "some long key"
    }, {
        "kid": "some kid value",
        "nbf": some number,
        "use": "sig",
        "kty": "RSA",
        "e": "AQAB",
        "n": "some long key"
    }]
}
Run Code Online (Sandbox Code Playgroud)

因此,当我尝试将“n”值从适当的键传递给

jwt.verify(token, 'my n value go here',  { algorithms: ['RS256'] }, callbackFunction());
Run Code Online (Sandbox Code Playgroud)

我有

错误:PEM_read_bio_PUBKEY 失败

我觉得我传递了错误的密钥,我找不到任何关于如何使用此公钥元数据来验证令牌的解释。文章中唯一有用的一行:

如何执行签名验证的描述超出了本文档的范围。如果您需要,可以使用许多开源库来帮助您。

如何验证签名?

Rom*_*iev 7

于是,我在Passport-Azure-AD的源代码中找到了答案

  1. 转到源代码中的“lib”文件夹并找到 aadutils.js 文件。
  2. 第 142 行。有一个函数 rsaPublicKeyPem(key1, key2)

    exports.rsaPublicKeyPem = (modulusB64, exponentB64) => {
       const modulus = new Buffer(modulusB64, 'base64');
       const exponent = new Buffer(exponentB64, 'base64');
    
       const modulusHex = prepadSigned(modulus.toString('hex'));
       const exponentHex = prepadSigned(exponent.toString('hex'));
    
       const modlen = modulusHex.length / 2;
       const explen = exponentHex.length / 2;
    
       const encodedModlen = encodeLengthHex(modlen);
       const encodedExplen = encodeLengthHex(explen);
       const encodedPubkey = `30${encodeLengthHex(
          modlen +
          explen +
          encodedModlen.length / 2 +
          encodedExplen.length / 2 + 2
          )}02${encodedModlen}${modulusHex}02${encodedExplen}${exponentHex}`;
    
       const derB64 = new Buffer(encodedPubkey,'hex').toString('base64');
    
       const pem = `-----BEGIN RSA PUBLIC KEY-----\n${derB64.match(/.{1,64}/g).join('\n')}\n-----END RSA PUBLIC KEY-----\n`;
    
      return pem;
    };
    
    Run Code Online (Sandbox Code Playgroud)
  3. 我复制了整个 aadutils 库并用键调用了这个函数

    const aadutils = require('./aadutils');
    const jwt = require('jsonwebtoken');
    
    //key is an object from public endpoint. Just follow the tutorial
    const pubKey = aadutils.rsaPublicKeyPem(key.n, key.e);
    jwt.verify(id_token, pubKey, { algorithms: ['RS256'] }, function(err, decoded) {
        //do what you want next
    });
    
    Run Code Online (Sandbox Code Playgroud)

得到我的签名验证。

  • 我不明白为什么微软不记录这一点。现在是 2020 年,他们在教程/文档中只能说有很多开源库可以做到这一点。 (2认同)