如何验证 MS Azure AD 生成的 JWT id_token?

How to verify JWT id_token produced by MS Azure AD?

我有一个 angularjs SPA 网络应用程序,它使用 ADAL-JS(和 adal-angular)。 它被设置为与我们在 MS Azure 中的企业 AD 进行身份验证。 log-in 流程似乎正常工作,SPA 收到 id_token.

接下来,当用户单击一个按钮时,SPA 向我在 AWS API 网关上托管的 REST API 发出请求。我在 Authorization: Bearer <id_token> header 上传递 id_token。 API 网关按预期接收 header,现在必须确定给定的令牌是否良好,以允许或拒绝访问。

我有一个示例令牌,它在 https://jwt.io/ 上正确解析,但到目前为止我未能找到我应该用来验证签名的 Public 密钥或证书。我看过:

认为 我应该使用 https://login.microsoftonline.com/common/discovery/keys 中键的 x5c 属性 的值匹配来自 JWT 的 kid 和 x5t 属性 id_token(当前为 a3QN0BZS7s4nN-BdrjbF0Y_LdMM,这导致 x5c 值以 "MIIDBTCCAe2gAwIBAgIQY..." 开头)。但是,https://jwt.io/ 页面报告 "Invalid Signature"(我还尝试用“-----BEGIN CERTIFICATE-----”和“-----END CERTIFICATE---”包装键值--").

此外,是否有一个(可能是python)库可以像上面的情况一样促进给定id_token的验证(这样我就不必去抢签名了我自己在飞行中钥匙?)...我能找到的最好的(ADAL for python)似乎没有提供这个功能?

到目前为止我能想到的最佳解决方案:

https://login.microsoftonline.com/common/discovery/keyshttps://login.microsoftonline.com/common/discovery/v2.0/keys 中获取证书x5c 属性 数组中的第一个值),从 id_token.

匹配 kidx5t

将证书包装在-----BEGIN CERTIFICATE-----\n\n-----END CERTIFICATE-----中(换行符似乎很重要),并将结果用作Public密钥(结合 id_token,在 https://jwt.io/ 上)。

当然,您的实际用例可能是让一些程序验证传入的 JWT id_tokens,因此您的目标不会是简单地获取令牌以通过网络进行验证 UI 在 https://jwt.io/.

例如,在python,我需要这样的东西:

#!/usr/bin/env python

import jwt
from cryptography.x509 import load_pem_x509_certificate
from cryptography.hazmat.backends import default_backend

PEMSTART = "-----BEGIN CERTIFICATE-----\n"
PEMEND = "\n-----END CERTIFICATE-----\n"

mspubkey = "The value from the x5c property"
IDTOKEN = "the id_token to be validated"
tenant_id = "your tenant id"

cert_str = PEMSTART + mspubkey + PEMEND
cert_obj = load_pem_x509_certificate(cert_str, default_backend())
public_key = cert_obj.public_key()

decoded = jwt.decode(IDTOKEN, public_key, algorithms=['RS256'], audience=tenant_id)
if decoded:
    print "Decoded!"
else:
    print "Could not decode token."

有关各种语言的 JWT 库列表,请参阅 the JWT Site。 我正在使用 pyjwt, and its cryptography 依赖项(它具有二进制依赖项,因此需要为目标 OS 构建和打包)。

然后,当然,您可以验证其他详细信息,例如声明recommended here

对于 JVM 解决方案,使用 com.nimbusds:numbus-jose-jwt:4.29 是解析和验证签名 RSA256 id_token 的最直接方法。以下 Scala 代码使用 JSON Web 密钥解析 JWT 令牌:

    val jwt = SignedJWT.parse(token)

    val n = new Base64URL("Your Modulus Component of RSA Key")
    val e = new Base64URL("AQAB")
    val rsaKey = new RSAKey.Builder(n, e).keyUse(KeyUse.SIGNATURE).algorithm(JWSAlgorithm.RS256).build()

    val verified = jwt.verify(new RSASSAVerifier(rsaKey))

您的应用程序仍需要从 Azure Active Directory B2C discovery/v2.0/key 动态获取 JSON Web 密钥集,以获取 AAD B2C 可能使用的密钥集。这可能应该被缓存并且 TTL 不超过 24 小时以提高效率。

Also, is there a (possibly python) library that can facilitate the verification of a given id_token as in the case above (so that I won't have to go grab the signing key on the fly myself?)... The best I could find (ADAL for python) doesn't seem to provide this feature?

物有所值,MSAL Python, which is ADAL Python's successor, validates the id token under the hood, and provides you the decoded claims inside an id token 适合您的应用。这样您的应用程序 - 刚刚通过 MSAL Python 获得了该 ID 令牌 - 可以在本地使用它。

Next, when the user clicks a button, the SPA makes a request to a REST API I am hosting on AWS API Gateway. I am passing the id_token on the Authorization: Bearer <id_token> header. The API Gateway receives the header as intended, and now has to determine if the given token is good or not to either allow or deny access.

通常不使用 id 令牌 "on the Authorization: Bearer <id_token> header" 发送给第 3 方以进行授权。 YMMV.