从 AAD Azure 验证 JWT access_token 失败

Posted

技术标签:

【中文标题】从 AAD Azure 验证 JWT access_token 失败【英文标题】:Validate JWT access_token from AAD Azure Fails 【发布时间】:2019-04-29 18:57:35 【问题描述】:

我需要从我正在处理的服务中验证一个 azure jwt access_token。我们目前正在向https://graph.microsoft.com/beta/me 提出请求。如果请求成功,则令牌有效。很遗憾,我们将无法继续这样做。

我为此尝试了各种想法。他们都没有成功。即使 jwt.io 无法识别签名,即使 jwt kid 和 jwk_uri 中可用签名之一中的 kid 匹配。

基于此博客post,我创建了以下解决方案(also available on github)。

我的实现与博客文章非常相似,但有一些变化:

#!/usr/bin/env python2

import jwt
import requests
import sys

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


def get_public_key(access_token):
    """ Retrieve public key for access token """
    token_header = jwt.get_unverified_header(access_token)

    res = requests.get('https://login.microsoftonline.com/common/.well-known/openid-configuration')
    jwk_uri = res.json()['jwks_uri']

    res = requests.get(jwk_uri)
    jwk_keys = res.json()

    x5c = None
    # Iterate JWK keys and extract matching x5c chain
    for key in jwk_keys['keys']:
        if key['kid'] == token_header['kid']:
            x5c = key['x5c']
            break
    else:
        raise Exception('Certificate not found in '.format(jwk_uri))

    cert = ''.join([
        '-----BEGIN CERTIFICATE-----\n',
        x5c[0],
        '\n-----END CERTIFICATE-----\n',
    ])
    try:
        public_key =  load_pem_x509_certificate(cert.encode(), default_backend()).public_key()
    except Exception as error:
        raise Exception('Failed to load public key:', error)

    return public_key, key['kid']

def main():
    print '\n'
    if len(sys.argv) < 2 or '-h' in sys.argv:
        print 'Run it again passing acces token:\n\tpython jwt_validation.py <access_token>'
        sys.exit(1)

    access_token = sys.argv[1]
    audience = 'https://graph.microsoft.com'

    public_key, kid = get_public_key(access_token)

    try:
        jwt.decode(
            access_token,
            public_key,
            algorithms='RS256',
            audience=audience,
        )
    except Exception as error:
        print 'key  did not worked, error:'.format(kid), error
        sys.exit(1)

    print('Key worked!')

if __name__ == '__main__':
    main()

此解决方案返回 Invalid Signature 以获得具有以下回溯的有效 access_token:

Traceback (most recent call last):
File "jwt_validation/jwt_validation.py", line 63, in <module>
    main()
File "jwt_validation/jwt_validation.py", line 57, in main 
    audience=audience,
File "~/.pyenv/versions/jwt-validation-tool/lib/python2.7/site-packages/jwt/api_jwt.py", line 93, in decode
    jwt, key=key, algorithms=algorithms, options=options, **kwargs
File "~/.pyenv/versions/jwt-validation-tool/lib/python2.7/site-packages/jwt/api_jws.py", line 157, in decode
    key, algorithms)
File "~/.pyenv/versions/jwt-validation-tool/lib/python2.7/site-packages/jwt/api_jws.py", line 224, in _verify_signature
    raise InvalidSignatureError('Signature verification failed')
jwt.exceptions.InvalidSignatureError: Signature verification failed

任何关于我可能错的想法都会有所帮助。

【问题讨论】:

你能把错误信息也包括在这里吗?在jwt 文档中,他们期望algorithms 值作为一个列表。不知道这是否是这里的问题。 感谢@RakihthaRR 的建议。添加了回溯。值 algorithm='RS256' 可以是您提到的列表,但它也可以用作字符串。 【参考方案1】:

我遇到了类似的问题,经过几天的调查,我发现在我的案例中我正在请求具有内置范围(openid 和配置文件)并且没有任何自定义范围的令牌,这会导致发出具有不同受众的令牌(MS Graph),因此使用不同的公钥签名的令牌(因为我认为颁发的 access_token 只是委托的 MS Graph 范围的转发)。

我通过在我的应用注册(公开 API 部分)中添加自定义范围解决了这个问题,现在我的 access_tokens 发出了有效的受众,我可以使用我的应用公钥检查签名。

【讨论】:

【参考方案2】:

通常audience 声明用于指向您在 Azure/Microsoft 中注册的客户端应用程序的 client_id。在我看来,错误是由于jwt(可能是观众)中的声明不匹配而发生的。检查您是否为受众变量设置了正确的client_id

【讨论】:

这个建议很有道理,我也试过了,但没有成功。查看我确认的调试器。 PyJWT 先验证 RSA 签名。这是 RSA 签名验证步骤失败。 您是否尝试使用模数n 和指数e 值而不是使用x5c web signature 创建公钥? 我做了一个基于 this blog post 的实现,它使用了你可以在这里找到的模块和指数:gist.github.com/tmpapageorgiou/0c1188283f5ee450b517ffe2f8912c98 @Thiago 你明白了吗?我也遇到了同样的问题。 @BrandonSmith 不幸的是我还没有。不过,我仍在研究它。如果我弄明白了,我一定会在这里发帖。

以上是关于从 AAD Azure 验证 JWT access_token 失败的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 AAD/Microsoft-Identity id_token 上的 System.IdentityModel.Tokens.Jwt 库验证签名

.net 核心 2.2 Azure Ad Jwt 令牌

无法在VSTS版本中获取Azure Vault密钥 - 无法获取JWT

401 Unauthorized while access azure function with bearer token |功能应用受 AAD 保护

从 PowerApp(或逻辑应用)调用受 AAD 保护的 Azure 函数

Azure 点到站点 - AAD 身份验证