8

继我之前的问题之后:在 ADAL JS 中使用 Azure AD 和 OAuth2 隐式授权进行分组声明,我已经设置了一些东西,以便用户可以使用 Azure/ADAL JS 进行身份验证,然后我使用他们的令牌访问 Azure Graph API代表该用户。这很好用,我可以获取他们的用户和组信息。

但是,我们现在有一个用例,另一个系统将在应用程序的上下文中登录我们的应用程序,而不是作为单个用户。我不知道我做得是否正确,但我有第二个 Azure AD 应用程序,它带有一个客户端密钥,通过从 AAD API 请求一个令牌来进行身份验证。我可以获得一个令牌并将其传递给我们的应用程序。但是,我不能再使用该令牌代表该用户(现在是一个应用程序)访问 Azure Graph API。

首先,这实际上是可能的还是我正在尝试不可能的事情?

如果可能的话,我必须做些什么不同的事情才能让它发挥作用?它只是 Azure 应用程序中的权限,还是我需要在代码中以不同的方式执行?

我用于代表用户访问 Graph API 的代码(删除了异常处理和空检查)是:

var authContext = new AuthenticationContext("https://login.microsoftonline.com/common");
var clientCredential = new ClientCredential("clientId", "key");
var userAssertion = new UserAssertion(((BootstrapContext)identity.BootstrapContext).Token);

var result = await authContext.AcquireTokenAsync("https://graph.windows.net", clientCredential, userAssertion);
return result.AccessToken;

我得到的异常是AADSTS50034: To sign into this application the account must be added to the {directory ID of my main application} directory错误invalid_grant

我似乎无法弄清楚我做错了什么,因为我相信所有 Azure 应用程序都配置正确,它们至少用于用户身份验证。我还向我的第二个 Azure AD(客户端)应用程序应用了相同的应用程序并将权限委派给其他目录进行用户身份验证所需的权限。

任何帮助,将不胜感激。

更新:系统概述/配置

因此,我似乎没有提供足够的有关系统配置方式的上下文,所以让我在这里尝试解决这个问题。

我们有一个在 Azure 中运行的企业 SaaS 应用程序(我们称之为我们的应用程序)。它在 Azure AD 中有一个“应用程序”(我们将其称为AAD 应用程序以避免混淆)。这是一个多租户 AAD 应用程序,用户通过 AAD 使用 OAuth2 进行身份验证。

作为企业应用程序,我们的客户都有自己的 Azure AD(可能会或可能不会同步到本地 AD)(我们称其为 AAD)。在配置他们的系统以使用我们的应用程序时,我们有一个来自他们 AAD的全局管理员授予我们的 AAD 应用程序同意 Windows Azure Active Directory 的以下权限(使用管理员同意授予):

  • 应用权限:
    • 读取目录数据
  • 委托权限:
    • 以登录用户身份访问目录
    • 读取目录数据
    • 登录并阅读用户资料

当用户浏览我们的应用程序时,他们将被重定向到 Azure 进行身份验证。经过身份验证后(无论是通过他们的 AAD还是通过连接到他们的 AAD的本地 AD ),通过 Web 应用程序进行的任何 API 调用都将包含一个不记名令牌。第一次看到每个 Bearer 令牌时,我们使用它来获取 Azure Graph API(代表用户)的新令牌,并查询 Graph API 以获取用户详细信息和组成员身份。当用户使用自己的用户帐户通过 UI 进行身份验证时,这一切都有效。

我们现在要做的就是让我们的客户让另一个下游应用程序(他们的下游应用程序)获得它自己的 Bearer 令牌来使用我们的应用程序。这次没有用户,所以我们正在查看客户端凭据授权流程。为此,我们的客户现在拥有他们自己的 AAD 应用程序(他们的 AAD 应用程序),该应用程序位于他们的 AAD中(已如上所述获得同意)。他们的下游应用程序可以获得一个 Bearer 令牌来访问我们的应用程序。但是,当我们尝试获取 Azure Graph API 的令牌(代表他们的下游应用程序)时,它会失败并显示我在上面粘贴的错误消息。

更新 2

我团队的另一位成员进行了一些调查,这些是他的发现。

我通过查看 SDK 在幕后执行的操作并手动执行所有请求以更好地允许它重现我们正在执行的操作,从而手动完成了该过程。

因此调用服务(这是在 Azure Active Directory 中设置的 Web 应用程序)从 Azure 获取 OAuth 令牌,即

POST https://login.microsoftonline.com/<tenant-id>/oauth2/token

grant_type      client_credentials
client_id       (the client ID of the calling service application in the AD)
client secret   (the key configured in the calling service application in the AD)
resource        (the client ID of our web service)

我们得到一个有效的令牌,其中包含appid, tid, oid,iss等,但不包含name, upn等(因为它不是用户而是应用程序)。

那么我们想从 Graph API 中查找服务主体的详细信息,并代表应用程序请求获取 Graph API 令牌,即

POST https://login.microsoftonline.com/common/oauth2/token

grant_type            urn:ietf:params:oauth:grant-type:jwt-bearer
client_id             (our web service application client ID)
client_secret         (our web service application client key)
resource              https://graph.windows.net
assertion             (the token that was sent by the calling client service, obtained by the process above)
requested_token_use   on_behalf_of
scope                 openid

这基本上是我们调用以下代码时发生的情况(从 SDK 代码中提取)

var authContext = new AuthenticationContext("https://login.microsoftonline.com/common");
var clientCredential = new ClientCredential("clientId", "key");
var userAssertion = new UserAssertion(((BootstrapContext)identity.BootstrapContext).Token);

var result = await authContext.AcquireTokenAsync("https://graph.windows.net", clientCredential, userAssertion);
return result.AccessToken;

它返回的只是:

{
  "error": "invalid_grant",
  "error_description": "AADSTS50034: To sign into this application the account must be added to the <customer tenant id> directory.\r\nTrace ID: <removed>\r\nCorrelation ID: <removed>\r\nTimestamp: 2015-10-08 06:37:58Z",
  "error_codes": [ 50034 ],
  "timestamp": "2015-10-08 06:37:58Z",
  "trace_id": "<removed>",
  "correlation_id": "<removed>"
}

所以我想问题是,如果调用者是 web/native 应用程序而不是实际用户,我们能否代表调用者使用持有者令牌获取图形 API 令牌?

4

1 回答 1

2

有一种无需用户凭据即可登录 Microsoft Graph API 的方法。您可以ClientCredential使用 aClient IDClient Secret. 有了它,ClientCredential您可以验证AuthenticationContext并检索访问令牌。访问令牌使您可以访问 Graph API。

代码示例:

string aadInstance = "https://login.microsoftonline.com/{0}";
string tenant = "<your-tenant>.onmicrosoft.com";
string clientId = "<your-clientId>";
string appKey = "<your-application-key>"; // Or Client Secret
string resourceId = "https://graph.microsoft.com/";
string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);

var authContext = new AuthenticationContext(authority);
var clientCredential = new ClientCredential(clientId, appKey);
var result = await authContext.AcquireTokenAsync(resourceId, clientCredential);
string response;

using (HttpClient client = new HttpClient())
{
    client.BaseAddress = new Uri("https://graph.microsoft.com");
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
    client.DefaultRequestHeaders
        .Accept
        .Add(new MediaTypeWithQualityHeaderValue("application/json"));

    var resultApi = await client.GetAsync("/v1.0/users");
    response = await resultApi.Content.ReadAsStringAsync();
}

希望这可以帮助!

于 2016-08-29T07:27:36.730 回答