首页 文章

代表应用程序而不是用户访问Azure Graph API

提问于
浏览
7

继我之前的问题:Group claims with Azure AD and OAuth2 implicit grant in ADAL JS,我've have things set up so users can authenticate using Azure/ADAL JS and then I use the their token to access the Azure Graph API on behalf of that user. This works well and I'能够获得他们的用户和组信息 .

但是,我们现在有一个用例,其他系统将在应用程序中登录我们的应用程序's context rather than as an individual user. I don'知道我是否已获得第二个Azure AD应用程序,其客户端密钥由requesting a token from the 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中有一个"application"(让我们称之为我们的AAD应用程序以避免混淆) . 这是一个多租户AAD应用程序,用户通过AAD使用OAuth2进行身份验证 .

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

  • 应用程序权限:

  • 读取目录数据

  • 委托权限:

  • 以登录用户身份访问目录

  • 读取目录数据

  • 登录并阅读用户 Profiles

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

我们现在要做的是允许我们的客户让另一个下游应用程序(他们的下游应用程序)获得自己的Bearer令牌来使用我们的应用程序 . 这次没有用户,所以我们正在查看client credentials grant flow . 为此,我们的客户现在拥有自己的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(AD中呼叫服务应用程序的客户端ID)
客户端密钥(在AD中的呼叫服务应用程序中配置的密钥)
资源(我们的Web服务的客户端ID)
我们得到一个有效的令牌,其中包含appid,tid,oid,iss等,但没有名称,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(我们的Web服务应用程序客户端ID)
client_secret(我们的Web服务应用程序客户端密钥)
资源https://graph.windows.net
断言(由调用客户端服务发送的令牌,由上面的过程获得)
requested_token_use代表
范围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;
它返回的全部是:{
“错误”:“invalid_grant”,
“error_description”:“AADSTS50034:要登录此应用程序,必须将帐户添加到<客户租户ID>目录 . \ r \ nTrace ID:<已删除> \ r \ n相关ID:<已删除> \ r \ n时间戳:2015 -10-08 06:37:58Z“,
“error_codes”:[50034],
“timestamp”:“2015-10-08 06:37:58Z”,
“trace_id”:“<删除>”,
“correlation_id”:“<删除>”
}
所以我想问题是,如果调用者是Web /本机应用程序而不是实际用户,我们是否可以代表具有承载令牌的调用者获得图形API令牌?

1 回答

  • 1

    有一种方法可以在没有用户凭据的情况下登录Microsoft Graph API . 您可以使用 Client IDClient Secret 设置 ClientCredential . 使用 ClientCredential ,您可以验证 AuthenticationContext 并检索访问令牌 . 通过访问令牌,您可以访问Graph API .

    Code Sample:

    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();
    }
    

    希望这可以帮助!

相关问题