使用 HttpClient 向 Microsoft Graph 请求用户列表时出现未经授权的问题
UnAuthorized issue when request user list to Microsoft Graph by using HttpClient
我正在尝试通过以下方式从 Microsoft Graph 获取有关用户的信息
https://graph.microsoft.com/v1.0/users
。
它正在返回 401 - Unauthorized
:
{
"error": {
"code": "InvalidAuthenticationToken",
"message": "Access token validation failure. Invalid audience.",
"innerError": {
"request-id": "3157d513-6f31-4d2d-a3d7-a97eed7207ba",
"date": "2019-12-11T05:39:02"
}
}
}
我的代码:
AuthenticationContext authContext =
new AuthenticationContext(string.Format(CultureInfo.InvariantCulture,
"https://login.microsoftonline.com/{0}", "my-domain name"));
ClientCredential clientCred =
new ClientCredential("Client-id", "Client-Secret-id");
AuthenticationResult authenticationResult = authContext
.AcquireTokenAsync("https://graph.windows.net", clientCred).Result;
var token = authenticationResult.AccessToken;
var client = new HttpClient();
var uri = "https://graph.microsoft.com/v1.0/me/";
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(token);
var response = await client.GetAsync(uri);
我哪里做错了?为什么我没有获得正确的访问令牌?谁能帮我使用 MS Graph?
您使用了错误的资源,您需要获取 Microsoft Graph 的令牌而不是 AAD Graph,
应该是 https://graph.microsoft.com
,而不是 https://graph.windows.net
.
AuthenticationResult authenticationResult = authContext.AcquireTokenAsync("https://graph.microsoft.com",
clientCred).Result;
更新:
确保授予 User.Read.All
应用程序权限。
然后尝试下面的代码,它在我这边有效。
using System;
using System.Net.Http;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
string _authString = "https://login.microsoftonline.com/xxxxxx.onmicrosoft.com";
string _clientId = "<client-id>";
string _clientSecret = "<client-secret>";
AuthenticationContext authenticationContext = new AuthenticationContext(_authString, false);
ClientCredential clientCred = new ClientCredential(_clientId, _clientSecret);
AuthenticationResult authenticationResult;
authenticationResult = authenticationContext.AcquireTokenAsync("https://graph.microsoft.com", clientCred).GetAwaiter().GetResult();
Console.WriteLine(authenticationResult.AccessToken);
var token = authenticationResult.AccessToken;
var client = new HttpClient();
var uri = "https://graph.microsoft.com/v1.0/users";
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
client.DefaultRequestHeaders.Accept.Clear();
//GET Method
HttpResponseMessage response = client.GetAsync(uri).GetAwaiter().GetResult();
Console.WriteLine(response.Content.ReadAsStringAsync().Result.ToString());
}
}
}
我认为如果您调用 Microsoft Graph,资源需要是 https://graph.microsoft.com
而不是 AAD Graph (graph.windows.net)。您可以尝试在 AcquireTokenAsync
通话中更改它吗?
有两个问题:
错误的资源,资源应该是https://graph.microsoft.com
。并确认您已在 Azure AD 门户
中授予正确的 Microsoft Graph 权限
您正在使用客户端凭据流,就像在没有用户的情况下使用 AcquireTokenAsync(String, ClientCredential)
方法一样,因此 https://graph.microsoft.com/v1.0/me/
将不起作用,因为其中没有用户。请改用 GET /users/{id | userPrincipalName}
。此外,您应该在 Azure 门户中授予 Application Permission
,因为您使用的是 M2M 流程。
Permissions (from least to most privileged) :
Application :User.Read.All, User.ReadWrite.All, Directory.Read.All, Directory.ReadWrite.All
确保您在调用 Graph API 时使用的帐户具有必需的权限。当您调用 GET 调用时,
应设置以下权限。
更多关于权限的信息:https://docs.microsoft.com/en-us/graph/permissions-reference
发布的错误清楚地指出您用来调用 GRAPH API 的帐户未经授权。正确设置权限后,将生成访问令牌并将针对您的应用程序进行身份验证。
编辑:尝试使用以下代码获取有效的访问令牌。
static string AppID = "<Your Application ID>";
static string APPKey = "<Your Application Key>";
static string tenantId = "<Your ORG Tenant ID>";
static string RedirectURI = "<Your Application's custom Redirect URI>";
static string GraphApi = "https://graph.microsoft.com/v1.0/"
public static IAuthenticationProvider CreateAuthorizationProvider()
{
var authority = $"https://login.microsoftonline.com/{tenantId}/v2.0";
List<string> scopes = new List<string>();
scopes.Add("https://graph.microsoft.com/.default");
var cca = ConfidentialClientApplicationBuilder.Create(AppID)
.WithAuthority(authority)
.WithRedirectUri(RedirectURI)
.WithClientSecret(APPKey)
.Build();
return new MsalAuthenticationProvider(cca, scopes.ToArray());
}
public static HttpClient GetAuthenticatedHTTPClient()
{
var authenticationProvider = CreateAuthorizationProvider();
_httpClient = new HttpClient(new AuthHandler(authenticationProvider, new HttpClientHandler()));
return _httpClient;
}
private static async Task<User> GetADUserInfo(HttpClient client,string email)
{
User user = new User();
client = GetAuthenticatedHTTPClient();
client.BaseAddress = new Uri(GraphApi);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
WriteToConsole("Call Graph API :: retrieving AD Info for the employee ::" + email);
using (client)
{
try
{
HttpResponseMessage res = await client.GetAsync("users/" + email);
res.EnsureSuccessStatusCode();
if (res.IsSuccessStatusCode)
{
user = await res.Content.ReadAsAsync<User>();
WriteToConsole("Call Graph API :: Call Success for employee ::" + email);
}
}
catch (Exception ex)
{
LogError(ex, "Error in Getting AD User info via Graph API");
return null;
}
return user;
}
}
以上代码使用MSALAuthentication,使用下面的代码:
public class MsalAuthenticationProvider : IAuthenticationProvider
{
private IConfidentialClientApplication _clientApplication;
private string[] _scopes;
public MsalAuthenticationProvider(IConfidentialClientApplication clientApplication, string[] scopes)
{
_clientApplication = clientApplication;
_scopes = scopes;
}
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
var token = await GetTokenAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}
public async Task<string> GetTokenAsync()
{
AuthenticationResult authResult = null;
authResult = await _clientApplication.AcquireTokenForClient(_scopes).ExecuteAsync();
return authResult.AccessToken;
}
}
AuthHandler Class :
public class AuthHandler : DelegatingHandler
{
private IAuthenticationProvider _authenticationProvider;
public AuthHandler(IAuthenticationProvider authenticationProvider, HttpMessageHandler innerHandler)
{
InnerHandler = innerHandler;
_authenticationProvider = authenticationProvider;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
await _authenticationProvider.AuthenticateRequestAsync(request);
return await base.SendAsync(request, cancellationToken);
}
}
您遇到了一些问题:
您应该为 https://graph.microsoft.com
请求令牌,而不是 https://graph.windows.net
。 graph.windows.net
是较旧的 AAD Graph,而不是较新的 Microsoft Graph:
AuthenticationResult authenticationResult = authContext
.AcquireTokenAsync("https://graph.windows.net", clientCred).Result;
您不能将 /me
与客户端凭据授权一起使用。 Graph 将 /me
翻译成 /users/{currently authenticated user id}
。由于您没有对用户进行身份验证,因此 "currently authenticated user id" 是 null
:
var uri = "https://graph.microsoft.com/v1.0/users/user@domain.onmicrosoft.com";
您正在设置授权 header 的值,但不是 scheme。您需要同时设置:
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
从您的问题中不清楚您请求了哪些范围或您是否已获得管理员同意。您需要确保您已请求应用程序范围 User.Read.All
和 从租户管理员那里收到 Admin Consent。
我正在尝试通过以下方式从 Microsoft Graph 获取有关用户的信息
https://graph.microsoft.com/v1.0/users
。
它正在返回 401 - Unauthorized
:
{
"error": {
"code": "InvalidAuthenticationToken",
"message": "Access token validation failure. Invalid audience.",
"innerError": {
"request-id": "3157d513-6f31-4d2d-a3d7-a97eed7207ba",
"date": "2019-12-11T05:39:02"
}
}
}
我的代码:
AuthenticationContext authContext =
new AuthenticationContext(string.Format(CultureInfo.InvariantCulture,
"https://login.microsoftonline.com/{0}", "my-domain name"));
ClientCredential clientCred =
new ClientCredential("Client-id", "Client-Secret-id");
AuthenticationResult authenticationResult = authContext
.AcquireTokenAsync("https://graph.windows.net", clientCred).Result;
var token = authenticationResult.AccessToken;
var client = new HttpClient();
var uri = "https://graph.microsoft.com/v1.0/me/";
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(token);
var response = await client.GetAsync(uri);
我哪里做错了?为什么我没有获得正确的访问令牌?谁能帮我使用 MS Graph?
您使用了错误的资源,您需要获取 Microsoft Graph 的令牌而不是 AAD Graph,
应该是 https://graph.microsoft.com
,而不是 https://graph.windows.net
.
AuthenticationResult authenticationResult = authContext.AcquireTokenAsync("https://graph.microsoft.com",
clientCred).Result;
更新:
确保授予 User.Read.All
应用程序权限。
然后尝试下面的代码,它在我这边有效。
using System;
using System.Net.Http;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
string _authString = "https://login.microsoftonline.com/xxxxxx.onmicrosoft.com";
string _clientId = "<client-id>";
string _clientSecret = "<client-secret>";
AuthenticationContext authenticationContext = new AuthenticationContext(_authString, false);
ClientCredential clientCred = new ClientCredential(_clientId, _clientSecret);
AuthenticationResult authenticationResult;
authenticationResult = authenticationContext.AcquireTokenAsync("https://graph.microsoft.com", clientCred).GetAwaiter().GetResult();
Console.WriteLine(authenticationResult.AccessToken);
var token = authenticationResult.AccessToken;
var client = new HttpClient();
var uri = "https://graph.microsoft.com/v1.0/users";
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
client.DefaultRequestHeaders.Accept.Clear();
//GET Method
HttpResponseMessage response = client.GetAsync(uri).GetAwaiter().GetResult();
Console.WriteLine(response.Content.ReadAsStringAsync().Result.ToString());
}
}
}
我认为如果您调用 Microsoft Graph,资源需要是 https://graph.microsoft.com
而不是 AAD Graph (graph.windows.net)。您可以尝试在 AcquireTokenAsync
通话中更改它吗?
有两个问题:
错误的资源,资源应该是
https://graph.microsoft.com
。并确认您已在 Azure AD 门户 中授予正确的 Microsoft Graph 权限
您正在使用客户端凭据流,就像在没有用户的情况下使用
AcquireTokenAsync(String, ClientCredential)
方法一样,因此https://graph.microsoft.com/v1.0/me/
将不起作用,因为其中没有用户。请改用GET /users/{id | userPrincipalName}
。此外,您应该在 Azure 门户中授予Application Permission
,因为您使用的是 M2M 流程。Permissions (from least to most privileged) :
Application :User.Read.All, User.ReadWrite.All, Directory.Read.All, Directory.ReadWrite.All
确保您在调用 Graph API 时使用的帐户具有必需的权限。当您调用 GET 调用时,
应设置以下权限。
更多关于权限的信息:https://docs.microsoft.com/en-us/graph/permissions-reference
发布的错误清楚地指出您用来调用 GRAPH API 的帐户未经授权。正确设置权限后,将生成访问令牌并将针对您的应用程序进行身份验证。
编辑:尝试使用以下代码获取有效的访问令牌。
static string AppID = "<Your Application ID>";
static string APPKey = "<Your Application Key>";
static string tenantId = "<Your ORG Tenant ID>";
static string RedirectURI = "<Your Application's custom Redirect URI>";
static string GraphApi = "https://graph.microsoft.com/v1.0/"
public static IAuthenticationProvider CreateAuthorizationProvider()
{
var authority = $"https://login.microsoftonline.com/{tenantId}/v2.0";
List<string> scopes = new List<string>();
scopes.Add("https://graph.microsoft.com/.default");
var cca = ConfidentialClientApplicationBuilder.Create(AppID)
.WithAuthority(authority)
.WithRedirectUri(RedirectURI)
.WithClientSecret(APPKey)
.Build();
return new MsalAuthenticationProvider(cca, scopes.ToArray());
}
public static HttpClient GetAuthenticatedHTTPClient()
{
var authenticationProvider = CreateAuthorizationProvider();
_httpClient = new HttpClient(new AuthHandler(authenticationProvider, new HttpClientHandler()));
return _httpClient;
}
private static async Task<User> GetADUserInfo(HttpClient client,string email)
{
User user = new User();
client = GetAuthenticatedHTTPClient();
client.BaseAddress = new Uri(GraphApi);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
WriteToConsole("Call Graph API :: retrieving AD Info for the employee ::" + email);
using (client)
{
try
{
HttpResponseMessage res = await client.GetAsync("users/" + email);
res.EnsureSuccessStatusCode();
if (res.IsSuccessStatusCode)
{
user = await res.Content.ReadAsAsync<User>();
WriteToConsole("Call Graph API :: Call Success for employee ::" + email);
}
}
catch (Exception ex)
{
LogError(ex, "Error in Getting AD User info via Graph API");
return null;
}
return user;
}
}
以上代码使用MSALAuthentication,使用下面的代码:
public class MsalAuthenticationProvider : IAuthenticationProvider
{
private IConfidentialClientApplication _clientApplication;
private string[] _scopes;
public MsalAuthenticationProvider(IConfidentialClientApplication clientApplication, string[] scopes)
{
_clientApplication = clientApplication;
_scopes = scopes;
}
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
var token = await GetTokenAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}
public async Task<string> GetTokenAsync()
{
AuthenticationResult authResult = null;
authResult = await _clientApplication.AcquireTokenForClient(_scopes).ExecuteAsync();
return authResult.AccessToken;
}
}
AuthHandler Class :
public class AuthHandler : DelegatingHandler
{
private IAuthenticationProvider _authenticationProvider;
public AuthHandler(IAuthenticationProvider authenticationProvider, HttpMessageHandler innerHandler)
{
InnerHandler = innerHandler;
_authenticationProvider = authenticationProvider;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
await _authenticationProvider.AuthenticateRequestAsync(request);
return await base.SendAsync(request, cancellationToken);
}
}
您遇到了一些问题:
您应该为
https://graph.microsoft.com
请求令牌,而不是https://graph.windows.net
。graph.windows.net
是较旧的 AAD Graph,而不是较新的 Microsoft Graph:AuthenticationResult authenticationResult = authContext .AcquireTokenAsync("https://graph.windows.net", clientCred).Result;
您不能将
/me
与客户端凭据授权一起使用。 Graph 将/me
翻译成/users/{currently authenticated user id}
。由于您没有对用户进行身份验证,因此 "currently authenticated user id" 是null
:var uri = "https://graph.microsoft.com/v1.0/users/user@domain.onmicrosoft.com";
您正在设置授权 header 的值,但不是 scheme。您需要同时设置:
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
从您的问题中不清楚您请求了哪些范围或您是否已获得管理员同意。您需要确保您已请求应用程序范围
User.Read.All
和 从租户管理员那里收到 Admin Consent。