0

我正在使用图形 API 来检索用户图像和 SharePoint 列表项,并使用托管服务标识进行身份验证。我成功地使用 powershell 为我的服务身份添加了所需的权限。

<#
ERRORS OUT BUT WORKS. YOU CAN VALIDATE BY USING THE GRAPH EXPLORER TO CALL: 
https://graph.microsoft.com/beta/servicePrincipals/{msiObjectId}/appRoleAssignedTo
NOTE: Get-AzureADServiceAppRoleAssignedTo DOES NOT WORK, MUST USE GRAPH EXPLORER FOR VALIDATION
#>

Connect-AzureAD

$msiObjectId = "12345678-1234-1234-1234-123456789101"
$app_name = "Microsoft Graph"
$role_names = @("Sites.Read.All", "User.Read.All", "Directory.Read.All")

$sp = Get-AzureADServicePrincipal -Filter "displayName eq '$app_name'"

$role_names | foreach {
    $role_name = $_
    $appRole = $sp.AppRoles | Where-Object { ($_.DisplayName -eq $role_name) -or ($_.Value -eq $role_name) }
    New-AzureADServiceAppRoleAssignment -ObjectId $msiObjectId -PrincipalId $msiObjectId -ResourceId $sp.ObjectId -Id $appRole.Id
}

Disconnect-AzureAD

当我将应用程序发布到分配给服务标识的天蓝色服务时,此错误但添加了权限并完美运行。

我正在使用 Microsoft.Azure.Services.AppAuthentication 来检索令牌并进行调用。

using Microsoft.Azure.Services.AppAuthentication;
...
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await new AzureServiceTokenProvider().GetAccessTokenAsync("00000003-0000-0000-c000-000000000000"));
...

问题出在本地开发中,这取决于我的凭据。这成功地获得了一个具有一些基本图形访问权限的令牌,例如 /me 和 /user basic 但是当我尝试检索列表或用户图像时,我收到以下错误:

{
  "error": {
    "code": "accessDenied",
    "message": "The caller does not have permission to perform the action.",
    "innerError": {
      "request-id": "55555555-1234-1234-1234-123456789101",
      "date": "2019-06-12T17:05:31"
    }
  }
}

我已经尝试使用我自己的对象 ID 代替$msiObjectIdpowershell 代码,并且还尝试更改New-AzureADServiceAppRoleAssignment为,New-AzureADUserAppRoleAssignment但都没有成功。我还在 UI 中尝试了所有我可以尝试的方法,但找不到添加权限的方法并尝试过https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/authorize?client_id=00000003-0000-0000-c000-000000000000&scope=offline_access%20User.Read.All%20Sites.Read.All,这给了我一个关于没有回复 url 的错误,而且也不起作用。

我想我可能需要为我的帐户添加一些委托权限来代替应用程序角色分配,但无论如何我都找不到这样做。我能够看到使用的权限,$sp.Oauth2Permissions但无法找到授予自己这些权限的方法。

这可能吗?如果可以,怎么做?

基本上,我需要我的凭据(没有应用程序)在使用时至少具有对 Microsoft Graph (GraphAggregatorService) 的“Sites.Read.All”和“User.Read.All”访问权限new AzureServiceTokenProvider().GetAccessTokenAsync())

4

2 回答 2

1

我在制作本地使用 AzureServiceTokenProvider 来调用我自己的 API 的示例时遇到了这个问题。正如您已经注意到的那样,问题是本地获取的令牌是委托令牌这一事实。在 Azure 中,它是一个仅限应用程序的令牌。

您不能将应用程序权限分配给用户,除非 appRoles 允许的成员类型包括用户和应用程序。那将是一件很奇怪的事情。MS Graph API 不这样做。

因此,您需要一个仅限应用程序的令牌。

在本地,您最好的选择可能是使用 MSAL 获取具有客户端凭据的令牌,并仅在 Azure 中使用 AzureServiceTokenProvider。然后,您将获得一个类似于您在 Azure 中获得的令牌,具有应用权限等。

于 2019-06-12T18:39:44.230 回答
0

@juunas 感谢您的确认。我最终为我的本地开发环境注册了一个应用程序,我将使用它在本地获取令牌。我将我的客户端 ID 和客户端机密保存在我的用户机密 (secrets.json) 中,以将其排除在我的部署之外,并使用这两个值的存在来确定是否将应用令牌与这些或服务身份一起使用.

private async Task<string> GetDevTokenAsync()
{
    var clientID = _configuration.GetValue<string>("AppSettings:ClientID") ?? _configuration.GetValue<string>("Authentication:AppSettings:ClientID");
    var clientSecret = _configuration.GetValue<string>("AppSettings:ClientSecret") ?? _configuration.GetValue<string>("Authentication:AppSettings:ClientSecret");
    if (string.IsNullOrWhiteSpace(clientID) || string.IsNullOrWhiteSpace(clientSecret))
        return null;

    using (var RestClient = new HttpClient())
    {
        var content = new FormUrlEncodedContent(
            new[]
                {
                    new KeyValuePair<string, string>("client_id", clientID),
                    new KeyValuePair<string, string>("client_secret", clientSecret),
                    new KeyValuePair<string, string>("scope", "https://graph.microsoft.com/.default"),
                    new KeyValuePair<string, string>("grant_type", "client_credentials")
                }
                );
        var response = await RestClient.PostAsync($"https://login.microsoftonline.com/{_tenantId}/oauth2/v2.0/token", content);
        return !response.IsSuccessStatusCode ? null : JObject.Parse(await response.Content.ReadAsStringAsync())?.Property("access_token")?.Value?.ToString();
    }
}
...

request.Headers.Authorization = new AuthenticationHeaderValue("Bearer",
    await GetDevTokenAsync() ?? 
    await new AzureServiceTokenProvider().GetAccessTokenAsync("00000003-0000-0000-c000-000000000000"));
...
于 2019-06-13T15:32:21.383 回答