如何验证Azure Ad生成的JWT?

问题描述 投票:0回答:4

问题

当我从 Azure AD 使用 Python 收到 JWK 时,我想对其进行验证和解码。但是,我不断收到错误“签名验证失败”。

我的设置

我有以下设置:

  1. Azure 设置
    在 Azure 中,我创建了一个应用程序注册,设置为“仅限个人 Microsoft 帐户”。
  2. Python 设置
    在 Python 中,我使用 MSAL 包来接收令牌。我使用来自 Azure 的公钥来验证令牌。

代码

使用 Azure 门户中的凭据,我设置了一个用于获取令牌的客户端。

import msal
ad_auth_client = msal.ConfidentialClientApplication(
    client_id = client_id,
    client_credential = client_secret,
    authority = "https://login.microsoftonline.com/consumers"
)
my_token = ad_auth_client.acquire_token_for_client(scopes=['https://graph.microsoft.com/.default'])

如果我将令牌放入像 https://jwt.io/ 这样的网站,一切看起来都很好。接下来,我需要来自 Azure 的公钥来验证令牌。

import requests
response = requests.get("https://login.microsoftonline.com/common/discovery/keys")
keys = response.json()['keys']

为了将公钥与令牌相匹配,我在令牌标头中使用“kid”。我还知道使用哪种算法进行加密。

import jwt
token_headers = jwt.get_unverified_header(my_token['access_token'])
token_alg = token_headers['alg']
token_kid = token_headers['kid']
public_key = None
for key in keys:
    if key['kid'] == token_kid:
        public_key = key

现在我有来自 Azure 的正确公钥来验证我的令牌,但问题是它是 JWT 密钥。在使用它进行解码之前,我需要将其转换为 RSA PEM 密钥。

from cryptography.hazmat.primitives import serialization
rsa_pem_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(public_key))
rsa_pem_key_bytes = rsa_pem_key.public_bytes(
    encoding=serialization.Encoding.PEM, 
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)
    

这就是 Azure 公钥的样子:

b'-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyr3v1uETrFfT17zvOiy0\n1w8nO+1t67cmiZLZxq2ISDdte9dw+IxCR7lPV2wezczIRgcWmYgFnsk2j6m10H4t\nKzcqZM0JJ/NigY29pFimxlL7/qXMB1PorFJdlAKvp5SgjSTwLrXjkr1AqWwbpzG2\nyZUNN3GE8GvmTeo4yweQbNCd+yO/Zpozx0J34wHBEMuaw+ZfCUk7mdKKsg+EcE4Z\nv0Xgl9wP2MpKPx0V8gLazxe6UQ9ShzNuruSOncpLYJN/oQ4aKf5ptOp1rsfDY2IK\n9frtmRTKOdQ+MEmSdjGL/88IQcvCs7jqVz53XKoXRlXB8tMIGOcg+ICer6yxe2it\nIQIDAQAB\n-----END PUBLIC KEY-----\n'

我需要做的最后一件事是使用公钥验证令牌。

decoded_token = jwt.decode(
    my_token['access_token'], 
    key=rsa_pem_key_bytes,
    verify=True,
    algorithms=[token_alg],
    audience=[client_id],
    issuer="https://login.microsoftonline.com/consumers"
)

我得到的结果是:

jwt.exceptions.InvalidSignatureError: Signature verification failed

我也尝试过

我也尝试遵循这个流行的指南:How to verify JWT id_token generated by MS Azure AD? 将 x5c 放入证书前缀和后缀中只会生成无效格式的错误。

接下来是什么?

你们能看到任何明显的错误吗?我的主要猜测是 audienceissuer 有问题,但我无法确定它是什么,而且微软的文档一如既往地糟糕。另外,Azure 中的应用程序注册中有一个密钥,但它似乎也不起作用。

更新

所以事实证明我的验证码是正确的,但我试图验证错误的令牌。创建轻微修改后,我现在收到一个 id_token,可以对其进行解码和验证。

python azure-active-directory jwt decode public-key-encryption
4个回答
1
投票

您获取了范围

https://graph.microsoft.com/.default
的令牌,这意味着访问令牌用于 MS Graph API。 您不应该验证这一点,这是 API 的工作目的。 Graph API 也很特殊,它使用与其他 API 不同的签名方法。


0
投票

尝试改变你的

scopes=['https://graph.microsoft.com/.default']

scopes:['[YOUR_CLIENT_ID]/.default']

0
投票

该问题似乎与您生成的 JWT 用于 MS Graph API 有关,结果 JWT 将在 JWT 标头中包含“随机数”,并且不打算进行验证。

调整是生成具有范围 != https://graph.microsoft.com/.default 的 JWT,但格式为 = "scope": [ "api://client-id/.default" ]。

之后,您可以验证 JWT 标头在 JWT 标头中不再具有“nonce”,并且像上面的代码这样的正常验证将起作用。


0
投票

至少有 2 个选项可用于解码 Microsoft Azure AD ID 令牌:

选项 1:使用
jwt

OP提供的代码给了我例外

InvalidIssuerError
。即使用
issuer
替换
https://login.microsoftonline.com/{your-tenant-id}
参数对我来说也不起作用。然而,忽略这个参数让我能够解码 ID 令牌。这是完整的代码:

# Get the public keys from Microsoft
import requests
response = requests.get("https://login.microsoftonline.com/common/discovery/keys")
keys = response.json()['keys']

# Format keys as PEM
from cryptography.hazmat.primitives import serialization
rsa_pem_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(public_key))
rsa_pem_key_bytes = rsa_pem_key.public_bytes(
    encoding=serialization.Encoding.PEM, 
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)

# Get algorithm from token header
alg = jwt.get_unverified_header(your-id-token)['alg']

# Decode token
jwt.decode(
    your-id-token,
    key=rsa_pem_key_bytes,
    algorithms=[alg],
    verify=True,
    audience=[your-client-id],
    options={"verify_signature": True}
)

选项 2:使用
msal
decode_id_token

微软的包

msal
提供了解码id令牌的功能。代码简单地变成:

from msal.oauth2cli.oidc import decode_id_token 
decode_id_token(id_token=your-token-id, client_id=your-client-id)
© www.soinside.com 2019 - 2024. All rights reserved.