Azzure AD JWT 验证:签名验证失败

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

我遇到了一个问题,我有这个 JWT 验证码:

def validate_access_token(self, access_token):
        jwks_url = f"{self.authority}/discovery/v2.0/keys"

        # Fetch keys from the JWKS endpoint
        response = requests.get(jwks_url)
        jwks_data = response.json()

        # Extract and store Azure AD public keys
        azure_ad_keys = {}
        for key in jwks_data["keys"]:
            key_id = key["kid"]
            # Extract the x509 certificate
            x5c_cert = key["x5c"][0]
            # Convert x509 certificate to PEM format
            pem_cert = f"-----BEGIN CERTIFICATE-----\n{x5c_cert}\n-----END CERTIFICATE-----"
            azure_ad_keys[key_id] = pem_cert
        # Decode the access token
        print(azure_ad_keys)    
        try:
            token_header = jwt.get_unverified_header(access_token)
            logging.debug(f"Token header: {token_header}")
            token_kid = token_header["kid"]
            token_alg = token_header["alg"]
            
            public_key = azure_ad_keys[token_kid]
            logging.debug(f"Token kid: {token_kid}, Token alg: {token_alg}, public key: {public_key}")
            payload = jwt.decode(
                token=access_token, 
                key=public_key, 
                algorithms=[token_alg])
            logging.debug(f"Token payload: {payload}")
        except JWTError as e:
            raise HTTPException(status_code=401, detail=f"Invalid access token: {e}")

        # Validate token claims
        if payload["iss"] != f"{self.authority}/v2.0":
            raise HTTPException(status_code=401, detail="Invalid token issuer")

        if payload["aud"] != self.client_id:
            raise HTTPException(status_code=401, detail="Invalid token audience")

        if payload["exp"] < time.time():
            raise HTTPException(status_code=401, detail="Access token expired")

        return payload

它返回“无效的访问令牌:签名验证失败”

这是我的智威汤逊:

eyJ0eXAiOiJKV1QiLCJub25jZSI6IjVETWxPSjV4T1BrZUZ4bnE3QVJVZDl2Uy1taGYwc09Qb290NGRaTlViVkkiLCJhbGciOiJSUzI1NiIsIng1dCI6ImtXYmthYTZxczh3c1RuQndpaU5ZT2hIYm5BdyIsImtpZCI6ImtXYmthYTZxczh3c1RuQndpaU5ZT2hIYm5BdyJ9.eyJhdWQiOiIwMDAwMDAwMy0wMDAwLTAwMDAtYzAwMC0wMDAwMDAwMDAwMDAiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC81NzA2ZTg5Ny02NWYyLTQ4YjMtODY2ZS0wZGY3YTE4M2Y0NjEvIiwiaWF0IjoxNzA3ODMxODcwLCJuYmYiOjE3MDc4MzE4NzAsImV4cCI6MTcwNzgzNjYyMCwiYWNjdCI6MCwiYWNyIjoiMSIsImFpbyI6IkUyVmdZSEQ0K1o1VHE0L2ZXZEJCNU5GSlE0YnlYRlBGTzJHRjZUK3JsVVNtNTk3S0NNNmZWeSt0SU5GVFhYcENVRnowWC9jdkFBPT0iLCJhbXIiOlsicHdkIl0sImFwcF9kaXNwbGF5bmFtZSI6IkF1dGggU2VydmljZSIsImFwcGlkIjoiODVmMmEzMTktMGUzOC00NTU0LWIwM2UtZjVkMzcyY2EzZjI5IiwiYXBwaWRhY3IiOiIxIiwiZmFtaWx5X25hbWUiOiJBZG1pbmlzdHJhdG9yIiwiZ2l2ZW5fbmFtZSI6Ik1PRCIsImlkdHlwIjoidXNlciIsImlwYWRkciI6IjExNi4wLjEuODEiLCJuYW1lIjoiTU9EIEFkbWluaXN0cmF0b3IiLCJvaWQiOiI4MGMxMzJkMi1iOTQzLTRlNGMtYjA4OS02MzY3ZmNlNjVkNDEiLCJwbGF0ZiI6IjUiLCJwdWlkIjoiMTAwMzIwMDM0ODBDMTQ4RSIsInJoIjoiMC5BU3NBbC1nR1ZfSmxzMGlHYmczM29ZUDBZUU1BQUFBQUFBQUF3QUFBQUFBQUFBRENBRjAuIiwic2NwIjoiRGlyZWN0b3J5LlJlYWQuQWxsIGVtYWlsIG9wZW5pZCBwcm9maWxlIFVzZXIuUmVhZCBVc2VyLlJlYWQuQWxsIFVzZXIuUmVhZEJhc2ljLkFsbCIsInNpZ25pbl9zdGF0ZSI6WyJrbXNpIl0sInN1YiI6InV2TG90Q2dERFF0eGtpdGE4UHU0V3hBd0FpYWFEOG5rOV91eXVQX2E3NG8iLCJ0ZW5hbnRfcmVnaW9uX3Njb3BlIjoiQVMiLCJ0aWQiOiI1NzA2ZTg5Ny02NWYyLTQ4YjMtODY2ZS0wZGY3YTE4M2Y0NjEiLCJ1bmlxdWVfbmFtZSI6ImFkbWluQE0zNjV4NzI2NTQ3NzYub25taWNyb3NvZnQuY29tIiwidXBuIjoiYWRtaW5ATTM2NXg3MjY1NDc3Ni5vbm1pY3Jvc29mdC5jb20iLCJ1dGkiOiIzcWtySm1XRWowYTRBQ0ZzS2l0b0FBIiwidmVyIjoiMS4wIiwid2lkcyI6WyI2MmU5MDM5NC02OWY1LTQyMzctOTE5MC0wMTIxNzcxNDVlMTAiLCJiNzlmYmY0ZC0zZWY5LTQ2ODktODE0My03NmIxOTRlODU1MDkiXSwieG1zX3N0Ijp7InN1YiI6Ik1zdTFwRFVQc3g0SGlaODItVDBTZlB3a29NSmNDN3hkNWVJRGRpU3FWY1UifSwieG1zX3RjZHQiOjE3MDYwODE5NTF9.sQKGfmYPFCarHwx1hMKbgJsTmlSMt8-Sg491w0DWWXE1ZjXBLdOSbuEJjTEpNj0VG-YSuN1SR-fs44D969bGaKD4UvZBAAm8gJIm5inkGp6-x_3WL3VMmUz3nMthgFKocNcxp7HwOqn7Gsj_oiTzNMveD5YgT3SXvrgXQQFzG1eNNT8WsN1NCa1nanFO789yaqXdjaDQbo5HKqyg9xwrKp-n6fpsFQjWXesr3GFZLHNt1tTMAnjOY_hzQ9xSVGdd7z7FAacM_NlLQZVhvD0iDp2D4nFXQWHAy9qDuk8u-6U9jcweQYubw68W2oP3gYvMqacZKTr8z70iE9sF0uujrQ

这是我的调试日志:

DEBUG:root:Token header: {'typ': 'JWT', 'nonce': '5DMlOJ5xOPkeFxnq7ARUd9vS-mhf0sOPoot4dZNUbVI', 'alg': 'RS256', 'x5t': 'kWbkaa6qs8wsTnBwiiNYOhHbnAw', 'kid': 'kWbkaa6qs8wsTnBwiiNYOhHbnAw'}
DEBUG:root:Token kid: kWbkaa6qs8wsTnBwiiNYOhHbnAw, Token alg: RS256, public key: -----BEGIN CERTIFICATE-----
MIIC/TCCAeWgAwIBAgIICHb5qy8hKKgwDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDAeFw0yNDAxMTUxODA0MTRaFw0yOTAxMTUxODA0MTRaMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3pDZdJ5acwD/5ysfZRt+19LTVAMCoeg9AWqxG1WTEbh7Jqac2VOXcNrTtBCSOk8Rsugu2C0wjY+vSmU7vFT1/3iaFt8r9QnpjQpxbGHAoyQKCfNwU5AXh4f8AgIcs4pry8+2G1yms1wKaNuSxblNgFmLq4uEUvD8eyMY7GErRVoNadaLM1V6q/NUHSO31V2Z+GzpmiHL/VvZa6x1p3U2ZIrOELvggOOUhoWiKT9kkl20s6CgjA5lMtbQzVQqFGta2PsCNUKcT/MGKWgAKbUisgz8/KYTXRwknpYXPb16niDtfrnEIRTrMnmggWJu+TpwopwU0HsUWNt6FhWnDkHFVAgMBAAGjITAfMB0GA1UdDgQWBBQLGQYqt7pRrKWQ25XWSi6lGN818DANBgkqhkiG9w0BAQsFAAOCAQEAtky1EYTKZvbTAveLmL3VCi+bJMjY5wyDO4Yulpv0VP1RS3dksmEALOsa1Bfz2BXVpIKPUJLdvFFoFhDqReAqRRqxylhI+oMwTeAsZ1lYCV4hTWDrN/MML9SYyeQ441Xp7xHIzu1ih4rSkNwrsx231GTfzo6dHMsi12oEdyn6mXavWehBDbzVDxbeqR+0ymhCgeYjIfCX6z2SrSMGYiG2hzs/xzypnIPnv6cBMQQDS4sdquoCsvIqJRWmF9ow79oHhzSTwGJj4+jEQi7QMTDR30rYiPTIdE63bnuARdgNF/dqB7n4ZJv566jvbzHpfCTqrJyj7Guvjr9i56NpLmz2DA==
-----END CERTIFICATE-----

虽然我有匹配的公钥和算法,但它仍然无法验证签名。 对此有什么想法吗?

python azure-active-directory jwt fastapi
1个回答
0
投票

如果您尝试验证 Microsoft Graph API 访问令牌,通常会出现错误 “访问令牌无效:签名验证失败”

  • Microsoft Graph API 访问令牌为 aud
    00000003-0000-0000-c000-000000000000
    https://graph.mircosoft.com
    并不意味着要进行验证,因为它不适用于应用程序。
  • 因此,无需验证 Microsoft Graph API 的访问令牌,因为它在 JWT 标头中不包含“随机数”,并且不打算进行验证。
  • 您可以验证访问令牌您自己的 API 或应用程序的访问令牌。

当我尝试验证访问令牌时,我收到了相同的错误

enter image description here

aud 是 Microsoft Graph:

enter image description here

我尝试验证我自己的 API 的访问令牌并且成功了:

我使用

scope: api://ClientID/.default
来生成访问令牌。

import jwt
from cryptography.hazmat.primitives import serialization
import json
import requests

access_token = 'YourAccessToken'
response = requests.get(f"https://login.microsoftonline.com/TenantID/discovery/keys")
keys = response.json()['keys']

token_headers = jwt.get_unverified_header(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 

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
) 

decoded_token = jwt.decode(
    access_token, 
    key=rsa_pem_key_bytes,
    verify=True,
    options={"verify_signature": True},
    algorithms=['RS256'],
    audience="ClientID",
    issuer=f"https://sts.windows.net/TenantID/"
)
s = json.dumps(decoded_token)
q = json.dumps(json.loads(s), indent=2)
print(q)

访问令牌验证成功:

enter image description here

参考:

spring security - 使用 Azure AD 验证签名 - 堆栈内存溢出,作者:junnas

© www.soinside.com 2019 - 2024. All rights reserved.