对 Microsoft Entra ID 访问令牌进行故障排除

本文介绍如何排查获取 Microsoft Entra ID 访问令牌时可能会遇到的错误,以及如何验证访问令牌。

无法使用用户名和密码获取令牌

错误消息

The user or administrator has not consented to use the application with ID <client-id>.
Send an interactive authorization request for this user and resource.

解决方案

  1. 如果未将 AzureDatabricks 资源添加到应用程序,请让管理员用户添加它。
  2. 使用 交互式方法 获取令牌。 该网页将指导你向应用程序授予权限。 或者,单击应用程序配置中所述的 “授予权限 ”按钮。 授予权限后,可以使用编程方法获取令牌。

重定向 URI 不匹配

错误消息

The reply URL specified in the request does not match the reply URLs configured for the application: '<application-id>'

解决方案

检查请求中的重定向 URI 是否与应用程序中的重定向 URI 匹配。

验证访问令牌

如果有Microsoft Entra ID 访问令牌,可以验证它是否包含正确的信息(请参阅 验证令牌)。

应验证以下字段是否与记录匹配:

  • aud:Azure Databricks 资源 ID: 2ff814a6-3304-4ab8-85cb-cd0e6f879c1d
  • iss:应为 https://sts.windows.net/<tenant-id>/
  • tid:应是工作区的租户(可通过组织 ID 或工作区设备 ID 查找)
  • nbf/exp:当前时间应介于 nbfexp
  • unique_name:应是 Databricks 工作区中的用户,除非该用户是工作区设备资源的贡献者

使用来自 OIDC 终结点 的公共证书来验证令牌的签名。

下面是显示令牌有效负载的代码片段。 必须先使用pip install pyjwt安装 PyJWT 库,然后使用pip install cryptography安装加密库。

import jwt

def decode_token(token):
  algorithm = jwt.get_unverified_header(token)['alg']

  decoded = jwt.decode(
    token,
    algorithms = [algorithm],
    options = {"verify_signature": False}
  )

  for key in decoded.keys():
     print(f"{key}: {str(decoded[key])}")

如果想要对令牌进行完整解码(包括签名验证),可以使用以下代码片段。 必须首先使用 pip install pyjwt 安装 PyJWT 库,并使用 pip install cryptography 安装加密库。 此外,请确保在以下代码中替换 <databricks-resource-id>

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

PEMSTART = '-----BEGIN CERTIFICATE-----\n'
PEMEND = '\n-----END CERTIFICATE-----\n'

# Get the Microsoft Azure public key.
def get_public_key_for_token(kid):
  response = requests.get(
    'https://login.microsoftonline.com/common/.well-known/openid-configuration',
  ).json()

  jwt_uri = response['jwks_uri']
  response_keys = requests.get(jwt_uri).json()
  pubkeys = response_keys['keys']

  public_key = ''

  for key in pubkeys:
    # Find the key that matches the kid in the token's header.
      if key['kid'] == kid:
        # Construct the public key object.
        mspubkey = str(key['x5c'][0])
        cert_str = PEMSTART + mspubkey + PEMEND
        cert_obj = load_pem_x509_certificate(bytes(cert_str, 'ascii'), default_backend())
        public_key = cert_obj.public_key()

  return public_key

# Decode the given <ms-entra-id> token.
def aad_access_token_decoder(access_token):
  header = jwt.get_unverified_header(access_token)
  public_key = get_public_key_for_token(header['kid'])
  # The value of the databricks_resource_id is as defined previously,
  # for example, databricks_resource_id = "2ff814a6-3304-4ab8-85cb-cd0e6f879c1d"
  decoded = jwt.decode(
    access_token,
    key = public_key,
    algorithms = 'RS256',
    audience = '<databricks-resource-id>')

  for key in decoded.keys():
    print(f"{key}: {str(decoded[key])}")

还可以通过在线 JWT 解码器查看已解码的令牌(如果它们不敏感)。 联机解码器的示例包括 jwt.msjwt.io