关于python:Azure AD:“范围”属性缺少访问令牌响应正文

Azure AD: “scope” Property Missing from Access Token Response Body

我正在编写一个python脚本来编辑一个位于SharePoint中的Excel电子表格。我们有365办公室。我正在使用Microsoft Graph API。我在Microsoft Azure Active Directory(AD)中注册了python应用程序,并为graph api添加了以下2个(仅限应用程序)权限:(1)读取和写入所有网站集中的文件(预览)和(2)读取目录数据。(我让公司管理员为我注册了应用程序。)

我的python脚本使用请求库发送REST请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import json
import requests

MSGRAPH_URI = 'https://graph.microsoft.com'
VERSION = 'v1.0'

def requestAccessToken(tenant_id, app_id, app_key):
    MSLOGIN_URI = 'https://login.microsoftonline.com'
    ACCESS_TOKEN_PATH = 'oauth2/token'
    GRANT_TYPE = 'client_credentials'
    endpoint = '{0}/{1}/{2}'.format(MSLOGIN_URI, tenant_id, ACCESS_TOKEN_PATH)
    headers = {'Content-Type': 'Application/Json'}
    data = {
        'grant_type': GRANT_TYPE,
        'client_id': app_id,
        'client_secret': app_key,
        'resource': MSGRAPH_URI

    }
    access_token = response.json()['access_token']
    return access_token

def getWorkbookID(access_token, fileName):
    endpoint = '{0}/{1}/me/drive/root/children'.format(MSGRAPH_URI, VERSION)
    headers = {
        'Content-Type': 'Application/Json',
        'Authorization': 'Bearer {}'.format(access_token)
    }
    response = requests.get(endpoint, headers=headers)
    print response.text
    assert response.status_code == 200
    items = response.json()['value']
    workbook_id = None
    for item in items:
        if item['name'] == fileName:
            workbook_id = item['id']
    return workbook_id

access_token = requestAccessToken(TENANT_ID, APP_ID, APP_KEY)
workbook_id = getWorkbookID(access_token, WORKBOOK_FILENAME)

python应用程序成功地请求并接收来自Microsoft服务器的access_token。访问令牌开始如下

1
eyJ0eXAiOiJKV1QiLCJub25jZSI6...

然后它在getWorkbookID()中请求我的文件列表:

1
2
GET https://graph.microsoft.com/v1.0/me/drive/root/children
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6...

这是对该REST请求的响应:

1
2
3
4
5
6
7
8
9
10
{
 "error": {
   "code":"InternalServerError",
   "message":"Object reference not set to an instance of an object.",
   "innerError": {
     "request-id":"aa97a822-7ac5-4986-8ac0-9852146e149a",
     "date":"2016-12-26T22:13:54"
    }
  }
}

请注意,当我通过图形资源管理器(https://graph.microsoft.io/en us/graph explorer)请求时,我成功地获得了一个文件列表。

编辑:

  • 将标题从"Microsoft Graph API返回未设置为对象实例的对象引用"更改为"访问令牌响应中缺少的Azure AD"范围。
  • 阅读以下内容后,将get请求的uri中的"me"更改为"myorganization":graph.microsft.io/en-us/docs/overview/call_api
  • 也就是说,

    1
    2
    GET https://graph.microsoft.com/v1.0/myOrganization/drive/root/children
    Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6...

    现在得到以下响应。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
     "error": {
       "code":"AccessDenied",
       "message":"Either scp or roles claim need to be present in the token.",
       "innerError": {
         "request-id":"bddb8c51-535f-456b-b43e-5cfdf32bd8a5",
         "date":"2016-12-28T22:39:25"
        }
      }
    }

    查看graph.microsoft.io/en-us/docs/authorization/app_authorization中的示例,我发现access token response body包含一个"scope"属性,其中列出了在应用程序注册期间为应用程序授予的权限。但是,我从服务器收到的访问令牌响应没有"scope"属性。这里是我的访问令牌响应的样子。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    "token_type":"Bearer",
    "expires_in":"3599",
    "ext_expires_in":"0",
    "expires_on":"1482968367",
    "not_before":"1482964467",
    "resource":"https://graph.microsoft.com",
    "access_token":"eyJ0eXAiOiJKV..."
    }

    问题:

  • 我让管理员在Azure AD中注册该应用程序,并选中所需的Microsoft Graph API应用程序权限框。显然这还不够。还需要什么?为什么权限不在访问令牌响应正文中?
  • GET请求的正确URI是什么?"myOrganization"是URI中的正确值吗?

  • 感谢大家的回答。经过更多的研究,我找到了我问题的答案。

    原始问题:"Microsoft Graph API返回未设置为对象实例的对象引用"

    请求

    1
    2
    GET https://graph.microsoft.com/v1.0/me/drive/root/children
    Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6...

    获取响应

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
     "error": {
       "code":"InternalServerError",
       "message":"Object reference not set to an instance of an object.",
       "innerError": {
         "request-id":"aa97a822-7ac5-4986-8ac0-9852146e149a",
         "date":"2016-12-26T22:13:54"
        }
      }
    }

    正如@sriramdhanasekaran msft指出的,/me是指当前登录的用户。在我们的例子中,我们没有登录用户,因此不能使用/me。相反,我们可以使用/myOrganization或不使用,这是可选的。

    更新的问题:访问令牌响应中缺少"azure ad"scope"属性"

    更新的(用/myorganization替换/me)请求

    1
    2
    GET https://graph.microsoft.com/v1.0/myOrganization/drive/root/children
    Authorization: Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6...

    获取响应

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
     "error": {
       "code":"AccessDenied",
       "message":"Either scp or roles claim need to be present in the token.",
       "innerError": {
         "request-id":"bddb8c51-535f-456b-b43e-5cfdf32bd8a5",
         "date":"2016-12-28T22:39:25"
        }
      }
    }

    正如@sriramdhanasekaran msft和@dankershaw msft提到的,access_token响应缺少scope属性的原因是管理员没有在Azure AD中"授予"权限。

    但是,@sriramdhanasekaran msft提供的解决方案:

    1
    https://login.microsoftonline.com/common/oauth2/authorize?client_id=&response_type=code&redirect_uri=http://localhost&resource=https://graph.microsoft.com&prompt=consent

    没有帮助,因为我的应用程序没有redirect uri。授予权限的解决方案比这简单:只需让管理员登录到Azure AD并单击"授予权限"链接即可授予权限。

    另外,/myOrganization/driver/root/children列出了管理员驱动器的内容。正如@peterpan msft所指出的,要列出不同用户的驱动器,请将/myOrganization替换为/users/

    成功:

    我的应用程序现在可以在线编辑我的Excel电子表格,而无需人工用户的干预。与@peterpan msft所说的相反,使用graph api可以做到这一点,并且无需下载Excel电子表格并离线编辑。

    总结:

    存在两个问题:(1)使用/me和(2)管理员未在Azure AD中授予应用程序权限。


    正如@juunas所说,下面的第一个错误信息应该是.NET中由服务器端引起的空异常,而不是API错误或权限问题。

    1
    2
    3
    error": {
       "
    code":"InternalServerError",
       "
    message":"Object reference not set to an instance of an object.",

    您可以参考so线程来了解此情况,该情况似乎在为后端更新,请稍后再试。

    为了解释将图形API URL中的选项me更改为myOrganization时的第二个错误,如@sriramdhanasekaran msft,它使用而不是me来访问当前用户或指定用户的文件。

    但是,根据我的理解,您希望为组织编辑一个在SharePoint中使用的Excel电子表格,它似乎不适合通过使用图形API来编辑。根据我的经验,应该通过使用OneDrive for Business的API将Excel文件复制到本地进行编辑和上载以覆盖它,请参阅OneDrive的开发文档并尝试将API用于驱动器资源。

    希望它有帮助。


    由于正在使用客户机的凭证令牌流(即没有经过身份验证的用户上下文),任何带有/me的请求都是无效的,因为/me指的是当前登录的用户。如果要访问用户驱动器中的文件,请尝试使用委派令牌。

    要访问SharePoint中的根驱动器,请求URL是/drive/root/children(myorganization是可选的)。

    对于丢失的索赔,管理员必须同意应用程序。您可以通过请求管理员访问以下URL(替换为应用程序的URL)来强制同意。

    https://login.microsoftonline.com/common/oauth2/authorize?client_id=&;response_type=code&;redirect_uri=http://localhost&resource=https://graph.microsoft.com&prompt=同意