You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python应用中验证Microsoft Access Token?签名验证失败排查

解决Azure AD Access Token签名验证失败问题

问题根源

你遇到的签名验证失败,核心原因是ID Token和Access Token的issuer格式、公钥端点可能不匹配,且示例代码中的issuer是v1版本格式,若你的Access Token是v2版本,就会导致验证不通过;另外Access Token的aud(受众)必须严格匹配API的应用标识符,而非客户端ID。

具体解决步骤

1. 确认Access Token版本

先解码Token的未验证字段,查看iss值:

  • v1版本Access Token的iss格式:https://sts.windows.net/{租户ID}/
  • v2版本Access Token的iss格式:https://login.microsoftonline.com/{租户ID}/v2.0

可以用以下代码快速查看:

from jose import jwt
unverified_claims = jwt.get_unverified_claims("你的Access Token")
print(unverified_claims["iss"])
print(unverified_claims["aud"])

2. 匹配对应版本的公钥端点

根据Token版本选择公钥获取地址:

  • v1 Token:https://login.microsoftonline.com/{租户ID}/discovery/v1.0/keys
  • v2 Token:https://login.microsoftonline.com/{租户ID}/discovery/v2.0/keys

3. 修正验证代码

调整jwt.decode的参数,确保issuer与Token的iss完全一致,audience与Token的aud(即你的API应用ID URI,比如api://{API客户端ID})完全匹配。

完整验证示例代码:

from jose import jwt
from jose.backends import RSAKey
import requests

def validate_access_token(token, api_audience, tenant_id):
    # 获取未验证的头部和claims,用于匹配公钥和确认issuer
    unverified_headers = jwt.get_unverified_headers(token)
    unverified_claims = jwt.get_unverified_claims(token)
    kid = unverified_headers["kid"]
    issuer = unverified_claims["iss"]

    # 根据Token版本获取公钥
    if "/v2.0" in issuer:
        jwks_uri = f"https://login.microsoftonline.com/{tenant_id}/discovery/v2.0/keys"
    else:
        jwks_uri = f"https://login.microsoftonline.com/{tenant_id}/discovery/v1.0/keys"
    
    jwks = requests.get(jwks_uri).json()
    # 匹配对应kid的公钥
    rsa_key = next((key for key in jwks["keys"] if key["kid"] == kid), None)
    if not rsa_key:
        raise ValueError("找不到匹配的签名公钥")
    
    # 验证Token
    payload = jwt.decode(
        token,
        RSAKey(rsa_key, algorithm="RS256"),
        algorithms=["RS256"],
        audience=api_audience,
        issuer=issuer
    )
    return payload

# 使用示例
API_AUDIENCE = "api://你的API客户端ID"  # 必须与Access Token的aud一致
TENANT_ID = "你的租户ID"
access_token = "待验证的Access Token"

try:
    payload = validate_access_token(access_token, API_AUDIENCE, TENANT_ID)
    print("Token验证通过,Payload:", payload)
except Exception as e:
    print("Token验证失败:", str(e))

额外注意事项

  • 确保客户端请求Access Token时,请求的scope包含你的API标识符(比如api://{API客户端ID}/.default),这样返回的Token的aud才会匹配你的API
  • 不要硬编码issuer,通过未验证的claims动态获取,兼容v1/v2版本Token
  • 若你的API允许多租户访问,可将tenant_id替换为common,但需确保公钥端点和issuer对应

内容的提问来源于stack exchange,提问作者Ghasem

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 02:52:38