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

Python开发的Azure Web App通过Azure AD认证获取当前用户邮箱的问题求助

Python开发的Azure Web App通过Azure AD认证获取当前用户邮箱的问题求助

问题描述

我现在在用Python开发一个Azure Web App,用Azure AD来做站点的身份认证。我想显示当前登录用户的邮箱(之后还打算用这个来做权限分层)。我了解到可以通过委托式Graph API来获取用户详情,于是尝试了下面的代码:

authority = f'https://login.microsoftonline.com/{subid}'

scope = ["https://graph.microsoft.com/.default"]

app = ConfidentialClientApplication(
    client_id,
    authority=authority,
    client_credential=client_secret
)

token_response = app.acquire_token_for_client(scopes=scope)
access_token = token_response['access_token']

headers = {
    'Authorization': f'Bearer {access_token}'
}

response = requests.get('https://graph.microsoft.com/v1.0/me', headers=headers)
user_data = response.json()

print("~~~~~~~~~~~~~~~~~~~~~~")
for key, value in user_data.items():
    print(f'{key}: {value}')
print("~~~~~~~~~~~~~~~~~~~~~~")

但运行后返回了错误:

error: {'code': 'BadRequest', 'message': '/me request is only valid with delegated authentication flow.', 'innerError': {'date': '2023-10-20T13:05:47', 'request-id': '9b6d8130-5bf6-4711-ba90-14ccaef0b127', 'client-request-id': '9b6d8130-5bf6-4711-ba90-14ccaef0b127'}}

我本来以为既然用户已经通过Azure AD登录认证了,用委托式应该能行,是不是我哪里理解错了?求帮忙分析或者给个替代方案,另外我的Graph API权限已经配置好了。


解决方案&问题分析

首先得搞清楚你遇到这个错误的核心原因:你用的acquire_token_for_client是客户端凭证流,这个流是让应用以自身的身份去请求Graph API,完全没有当前登录用户的上下文,所以调用/me端点肯定会报错——/me必须绑定到一个具体的用户,只有用委托式认证流(也就是基于当前登录用户的身份去获取令牌)才能正常使用。

你之所以有疑惑,可能是混淆了应用身份和用户身份:虽然用户已经登录了你的Web App,但你代码里拿的是应用自己的令牌,和这个登录用户没关系,自然没法获取到这个用户的信息。

接下来给你几个可行的解决方向:

1. 直接从Web App的认证上下文获取用户邮箱(无需调用Graph API)

其实如果你的Azure Web App已经配置了Azure AD认证,用户登录后,Web App会自动获取到用户的基本信息,包括邮箱,完全不需要调用Graph API!比如在常见的Python Web框架里:

  • 如果用Flask,可以从请求的X-MS-CLIENT-PRINCIPAL-NAME header里拿到用户的邮箱(或者用户主体名):
    from flask import request
    
    user_email = request.headers.get('X-MS-CLIENT-PRINCIPAL-NAME')
    
  • 如果用Django,也可以从认证后的用户对象里获取相关信息,或者从请求的headers中提取。

这个方法最简便,不需要额外调用Graph API,适合只需要邮箱这类基础信息的场景。

2. 用委托式流调用Graph API获取用户详情(适合需要更多用户信息的场景)

如果确实需要调用Graph API获取更多用户数据,那就要用当前登录用户的访问令牌,而不是应用的令牌:

  • 第一步,确保你的Azure AD应用注册已经添加了委托类型的Graph API权限(比如User.Read),并且已经授予了管理员同意(如果是租户级应用的话)。
  • 第二步,在Azure Web App的认证设置里,开启颁发访问令牌(Access Token),这样用户登录后,Web App可以拿到用户的访问令牌用来调用Graph API。
  • 第三步,在代码中从当前请求的上下文里获取用户的访问令牌,然后调用Graph API:
    import requests
    from flask import request
    
    # 从请求的Authorization header中提取用户的访问令牌
    auth_header = request.headers.get('Authorization')
    if auth_header and auth_header.startswith('Bearer '):
        user_access_token = auth_header.split(' ')[1]
    
        # 调用Graph API的/me端点,指定只获取需要的字段
        headers = {'Authorization': f'Bearer {user_access_token}'}
        response = requests.get('https://graph.microsoft.com/v1.0/me?$select=mail,userPrincipalName', headers=headers)
        
        if response.status_code == 200:
            user_data = response.json()
            # 优先取mail字段,没有的话取userPrincipalName
            user_email = user_data.get('mail') or user_data.get('userPrincipalName')
            print(user_email)
    
  • 另外,如果你想更规范地处理令牌的刷新,可以用MSAL的acquire_token_silent方法,基于用户的登录会话来获取委托令牌:
    from msal import ConfidentialClientApplication
    
    # 初始化MSAL应用
    app = ConfidentialClientApplication(
        client_id,
        authority=f'https://login.microsoftonline.com/{tenant_id}',
        client_credential=client_secret
    )
    
    # 从当前登录用户的会话中获取用户主体名
    user_principal_name = request.headers.get('X-MS-CLIENT-PRINCIPAL-NAME')
    accounts = app.get_accounts(username=user_principal_name)
    
    if accounts:
        # 尝试静默获取令牌(自动处理刷新)
        result = app.acquire_token_silent(scopes=["User.Read"], account=accounts[0])
        if not result:
            # 静默失败的话触发交互式登录(Web App中一般不需要,因为用户已登录)
            result = app.acquire_token_interactive(scopes=["User.Read"])
        
        if "access_token" in result:
            headers = {'Authorization': f'Bearer {result["access_token"]}'}
            response = requests.get('https://graph.microsoft.com/v1.0/me', headers=headers)
            user_data = response.json()
    

总结一下,核心就是要区分应用身份和用户身份,你之前的代码用的是应用身份,而要获取当前登录用户的信息,必须用用户身份对应的委托式令牌,或者直接从Web App的认证上下文里提取基础信息,后者更简单。

备注:内容来源于stack exchange,提问作者Metel Stairs

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 10:39:51