Microsoft Graph API:具有应用程序许可权的多租户应用程序获取ErrorAccessDenied

时间:2020-04-13 09:15:39

标签: azure-ad-graph-api office365api msal

我正在为使用Outlook的客户(多个租户)编写一个守护程序。

我正在使用2个需要管理员同意的应用程序权限-Mail.ReadBasic.All User.Read.All。我的应用程序首先需要读取所有用户的ID,然后获取其电子邮件的所有元数据。

我已经用office365创建了一个新的租户来对此进行测试,我们称它为“测试”,并在2位用户之间发送了几封电子邮件。
因此,首先,我将测试组织的管理员重定向到adminconsent端点,在该端点上他/她向我的应用授予应用程序权限。这是我正在使用的URL:

https://login.microsoftonline.com/organizations/v2.0/adminconsent?
client_id=<the app ID>
&state=<some state>
&redirect_uri=<my redirect URL as written in the app configuration>
&scope=https://graph.microsoft.com/.default

调用此终结点之后,我可以看到我的应用程序在“测试”组织中的“企业”应用程序下列出,并且可以看到相关权限是由管理员授予的。

由于我没有从此流程中获取代码(oAuth2身份验证流程所需),因此我需要请管理员再次登录。我为此使用了该URL:

https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize?
client_id=<same app ID>
&response_type=code
&redirect_uri=<same redirect URL>
&scope=https://graph.microsoft.com/.default+offline_access+openid+profile
&state=<some state>

成功登录后,我将获得一个返回到重定向URL的代码,并且在另一个请求后,我获得了一个访问令牌。使用此访问令牌,我尝试访问以下任何API:

但是我收到ErrorAccessDenied的消息:Access is denied. Check credentials and try again.

其他信息: 我正在使用python和MSAL软件包来构建应用程序(使用类-ConfidentialClientApplication)和身份验证流的URL(但不适用于adminconsent端点),因为我找不到执行该操作的方法)

您知道我在做什么错吗?我对此不知所措...:(

1 个答案:

答案 0 :(得分:2)

此页面应描述您需要的一切: https://docs.microsoft.com/graph/auth-v2-service

管理员同意URL应特定于客户的租户。如果您想允许登录任何租户,则可以使用 common 一词。

https://login.microsoftonline.com/{tenant}/adminconsent

您还必须对redirect_uri参数(以及所有其他参数)进行URL编码。由于某种原因,该文档中的示例未经过URL编码,但是此处的值必须经过URL编码。对于该参数,您应该看不到冒号,斜线,&符等。

对于另一个示例,它要求获得管理员同意的特定范围(而不是默认值,该默认范围是您在AAD客户端应用程序注册期间列出的所有范围),请参见https://docs.microsoft.com/azure/active-directory/develop/v2-admin-consent

  1. 您将收到指向重定向URI的回调,以指示一切正常。这包括授予您管理员同意的租户ID。

  2. 此后,您将启动一个单独的令牌请求调用,以调用租户ID,您的应用程序客户端ID和特定的请求范围。然后,这将返回适当范围的访问令牌,您可以直接在所有API调用中使用它。您可以这样操作:https://docs.microsoft.com/azure/active-directory/develop/scenario-daemon-acquire-token?tabs=python#acquiretokenforclient-api

# The pattern to acquire a token looks like this.
result = None

# First, the code looks up a token from the cache.
# Because we're looking for a token for the current app, not for a user,
# use None for the account parameter.
result = app.acquire_token_silent(config["scope"], account=None)

if not result:
    logging.info("No suitable token exists in cache. Let's get a new one from AAD.")
    result = app.acquire_token_for_client(scopes=config["scope"])

if "access_token" in result:
    # Call a protected API with the access token below.
    print(result["token_type"])
else:
    print(result.get("error"))
    print(result.get("error_description"))
    print(result.get("correlation_id"))  # You might need this when reporting a bug.

希望有帮助。上面的文章包含所有详细信息。