尝试读取用户时出现403 Authorization_RequestDenied

时间:2019-07-04 21:32:05

标签: python azure-active-directory microsoft-graph adal permission-denied

我正在尝试使用API​​端点https://graph.microsoft.com/v1.0/users/来获取Exchange目录中所有用户的列表 使用Graph Explorer可以正常工作,我得到一个预览,其中包含100个有效结果的列表。

但是,当从我的Python应用访问同一终结点并以同一用户登录时,我得到以下响应:

{'error': {'code': 'Authorization_RequestDenied', 'message': 'Insufficient privileges to complete the operation.', 'innerError': {'request-id': '6924dba1-83ee-4865-9dcf-76b6cafcb808', 'date': '2019-07-04T21:08:28'}}}

这是我正在使用的Python代码,大部分代码是从python-sample-console-app

复制而来的
CLIENT_ID = '765bdd8d-b33d-489b-a039-3cdf41223aa4'

AUTHORITY_URL = 'https://login.microsoftonline.com/common'
RESOURCE = 'https://graph.microsoft.com'
API_VERSION = 'v1.0'

import base64
import mimetypes
import os
import urllib
import webbrowser

from adal import AuthenticationContext
import pyperclip
import requests

def device_flow_session(client_id, auto=False):
    """Obtain an access token from Azure AD (via device flow) and create
    a Requests session instance ready to make authenticated calls to
    Microsoft Graph.
    client_id = Application ID for registered "Azure AD only" V1-endpoint app
    auto      = whether to copy device code to clipboard and auto-launch browser
    Returns Requests session object if user signed in successfully. The session
    includes the access token in an Authorization header.
    User identity must be an organizational account (ADAL does not support MSAs).
    """
    ctx = AuthenticationContext(AUTHORITY_URL, api_version=None)
    device_code = ctx.acquire_user_code(RESOURCE, client_id)

    # display user instructions
    if auto:
        pyperclip.copy(device_code['user_code']) # copy user code to clipboard
        webbrowser.open(device_code['verification_url']) # open browser
        print(f'The code {device_code["user_code"]} has been copied to your clipboard, '
              f'and your web browser is opening {device_code["verification_url"]}. '
              'Paste the code to sign in.')
    else:
        print(device_code['message'])

    token_response = ctx.acquire_token_with_device_code(RESOURCE,
                                                        device_code,
                                                        client_id)
    if not token_response.get('accessToken', None):
        return None

    session = requests.Session()
    session.headers.update({'Authorization': f'Bearer {token_response["accessToken"]}',
                            'SdkVersion': 'sample-python-adal',
                            'x-client-SKU': 'sample-python-adal'})
    return session

def api_endpoint(url):
    """Convert a relative path such as /me/photo/$value to a full URI based
    on the current RESOURCE and API_VERSION settings in config.py.
    """
    if urllib.parse.urlparse(url).scheme in ['http', 'https']:
        return url # url is already complete
    return urllib.parse.urljoin(f'{RESOURCE}/{API_VERSION}/',
url.lstrip('/'))

session = device_flow_session(CLIENT_ID, True)
users = session.get(api_endpoint('users'))
print(users.json())

我还在Microsoft Azure门户中设置了以下权限: permissions

特别地,User.ReadBasic.All应该足以获得仅具有名称和电子邮件之类的基本属性的所有用户的列表,但仍然无法正常工作。

显然,该问题缺少“用户同意”。这里有些奇怪,因为理论上登录页面应该针对所有已配置的API权限自动请求用户同意。经过一番修补后,我发现了以下解决方案:

解决方案1 ​​

  • 在portal.azure.com上注册新的应用程序
  • 预先添加所需的配置和API权限
  • 更改所需的API权限后,不会再次询问用户
  • 您需要访问新的API并相应地更改应用程序中的应用程序ID时,可以随时在portal.azure.com上重新创建应用程序

解决方案2

  • 检测到“ Authorization_RequestDenied”错误
  • 如果发生这种情况,请打开带有特殊登录URL的浏览器窗口以征求用户同意
  • 要求用户在成功登录并获得同意后重新启动应用程序
  • 这是相关代码
CONSENT_URL = f'https://login.microsoftonline.com/common/oauth2/authorize?client_id={CLIENT_ID}&response_type=code&response_mode=query&resource=https://graph.microsoft.com&state=12345&prompt=consent'
#...
response = session.get(api_endpoint('users')).json()
if 'error' in response and response['error']['code'] == 'Authorization_RequestDenied':
    print("Access denied. Consent page has been opened in the webbrowser. Please give user consent, then start the script again!")
    webbrowser.open(CONSENT_URL)
    sys.exit(1)

1 个答案:

答案 0 :(得分:0)

首次使用图形浏览器时,必须征得您的同意才能使用该浏览器。

因此,当您使用在Azure门户中注册的应用程序登录时,还必须先征得您的同意才能使用该应用程序。但是,如果您在同意后添加/更新权限。您必须再次同意。

enter image description here

您可以通过url请求强制用户同意,并将&prompt =同意附加到身份验证请求URL。

或者您只代表该目录中的所有用户授予同意。向所有用户授予管理员同意意味着使用该应用程序时不会向最终用户显示同意屏幕。

enter image description here