我有一个 angularjs SPA Web 应用程序,它使用 ADAL-JS (和 adal-angular)。 它被设置为在 MS Azure 中对我们的公司 AD 进行身份验证。登录流程似乎工作正常,并且 SPA 收到 id_token。
接下来,当用户单击按钮时,SPA 会向我在 AWS API Gateway 上托管的 REST API 发出请求。我正在
Authorization: Bearer <id_token>
标头上传递 id_token。
API 网关按预期接收标头,现在必须确定给定的令牌是否有效,以允许或拒绝访问。
我有一个示例令牌,它在 https://jwt.io/ 上正确解析,但到目前为止我还没有找到我应该用来验证签名的公钥或证书。我看过:
我认为我应该使用https://login.microsoftonline.com/common/discovery/keys中密钥的x5c属性值来匹配JWT id_token中的kid和x5t属性(当前为
a3QN0BZS7s4nN-BdrjbF0Y_LdMM
) ,这会导致以“MIIDBTCCAe2gAwIBAgIQY...”开头的 x5c 值)。但是,https://jwt.io/页面报告“无效签名”(我还尝试用“-----BEGIN CERTIFICATE-----”和“-----END”包装密钥值证书-----”)。
另外,是否有一个(可能是Python)库可以像上面的情况一样方便地验证给定的id_token(这样我就不必自己去即时获取签名密钥?)...最好的我发现(ADAL for python)似乎没有提供此功能?
迄今为止我能想到的最佳解决方案:
从 x5c
或
https://login.microsoftonline.com/common/discovery/keys
获取证书(
https://login.microsoftonline.com/common/discovery/v2.0/keys
属性数组中的第一个值),匹配 id_token 中的 kid
和 x5t
。
将证书包装在
-----BEGIN CERTIFICATE-----\n
和\n-----END CERTIFICATE-----
中(换行符似乎很重要),并将结果用作公钥(与 id_token 一起使用,在 https://jwt.io/ 上)。
当然,您的实际用例可能是让某些程序验证传入的 JWT id_tokens,因此您的目标不是简单地通过 https://jwt.io/ 上的 Web UI 获取令牌进行验证.
例如,在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 库的列表,请参阅JWT 站点。 我正在使用 pyjwt 及其 cryptography 依赖项(具有二进制依赖项,因此需要为目标操作系统构建和打包)。
然后,当然,您可以验证其他详细信息,例如此处推荐的声明。
com.nimbusds:numbus-jose-jwt:4.29
是解析和验证签名 RSA256 id_token 的最直接方法。以下 Scala 代码使用 JSON Web Key 解析 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 小时。
另外,是否有一个(可能是Python)库可以像上面的情况一样方便地验证给定的id_token(这样我就不必自己去即时获取签名密钥?)...最好的我发现(ADAL for python)似乎没有提供这个功能?就其价值而言,
MSAL Python(ADAL Python 的后继者)会在后台验证 id 令牌,并为您的应用程序提供 id 令牌中已解码的声明。这样您的应用程序(刚刚通过 MSAL Python 获取了该 id 令牌)就可以在本地使用它。
接下来,当用户单击按钮时,SPA 会向我在 AWS API Gateway 上托管的 REST API 发出请求。我正在通常,id 令牌不会“在
Authorization: Bearer <id_token>
标头上传递 id_token。 API 网关按预期接收标头,现在必须确定给定的令牌是否有效,以允许或拒绝访问。
Authorization: Bearer <id_token>
标头上”用于发送给第 3 方以进行授权。 YMMV.
const config = await fetch("https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration").then((response) => {
return response.json();
});
const jwks = await fetch(config.jwks_uri).then((response) => {
return response.json();
});
const tokenHeader = JSON.parse(atob(token.split(".")[0]));
const publicKey = jwks.keys.find((key) => key.kid === tokenHeader.kid);
if(!publicKey) {
throw new Error("Invalid token");
}
const publicKeyPEM = `-----BEGIN CERTIFICATE-----\n${publicKey.x5c[0]}\n-----END CERTIFICATE-----`;
const data = verify(token, publicKeyPEM);