从 C# 控制台应用程序使用图形 API 时出现 404 错误

404 error on using graph API from c# console app

我目前正在尝试使用我的 C# 对 Graph API 进行身份验证。当我调用相同的 API 时,我能够查询此 API 并从 Postman.But 成功接收令牌,我收到 404 错误。

我的代码如下:

using System;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace GraphTest
{
    public class AuthenticationModel
    {
        public string grantType { get; set; } = "client_credentials";
        public string clientId { get; set; } = "my_ad_app_id";
        public string clientSecret { get; set; } = "client_secret";
        public string scope { get; set; } = "https://graph.microsoft.com/.default";
    } 
    public class Authentication
    {
        private static string tenantId = "tenant_id";
        private static readonly HttpClient Client = new HttpClient();
        public Authentication()
        {
           var authenticationModel = new AuthenticationModel();
           RunAsync().GetAwaiter().GetResult();
        }

        private static async Task RunAsync()
        {
            Client.BaseAddress = new Uri("https://login.microsoftonline.com/");
            Client.DefaultRequestHeaders.Accept.Clear();
            Client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("*/*"));
            Client.DefaultRequestHeaders.Add("Host", "login.microsoftonline.com");
            Client.DefaultRequestHeaders.Add("Cache-Control", "no-cache");

            try
            {
                var authenticationModel = new AuthenticationModel();
                var url = await GetTokenAsync(authenticationModel);
                Console.WriteLine($"Created at {url}");
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception.Message);
            }
        }

        private static async Task<Uri> GetTokenAsync(AuthenticationModel authenticationModel)
        {
            var keyValues = authenticationModel.GetType().GetProperties()
                .ToList()
                .Select(p => $"{p.Name} = {p.GetValue(authenticationModel)}")
                .ToArray();
            var xUrlEncodedBody = string.Join('&', keyValues);
            var response = await Client.PostAsJsonAsync(
                $"{tenantId}/oauth2/v2.0/token", xUrlEncodedBody);
            response.EnsureSuccessStatusCode();

            return response;
        }
    }
}

所以,我收到了这样的回复:StatusCode:404, ReasonPhrase:Not Found 请帮助我知道我哪里做错了。

注意:API 具有相同数据的 Postman 工作正常。不过,出于安全原因,我在这里替换了一些值。

我 运行 你的代码,它错误地生成了如下 spaces 的查询字符串。

xUrlEncodedBody => grantType = client_credentials&clientId = my_ad_app_id&clientSecret = client_secret&scope = https://graph.microsoft.com/.default

查询参数名称和值之间有 space,见下行。

.Select(p => $"{p.Name} = {p.GetValue(authenticationModel)}")

删除 space 并重试

.Select(p => $"{p.Name}={p.GetValue(authenticationModel)}")

您不应 post 将 URL 编码内容形成为 JSON (PostAsJsonAsync)。正文需要具有 application/x-www-form-urlencoded.

的内容类型

但退一步说,当有库可以为您完成协议时,您不需要自己实现协议 :)。我们提供并支持 Microsoft Authentication Library (MSAL) 让这一切变得简单。

var app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
              .WithTenantId("{tenantID}")
              .WithClientSecret(config.ClientSecret)
              .Build();

string[] scopes = new string[] { "https://graph.microsoft.com/.default" };

AuthenticationResult result = null;

try
{
    result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
}
catch (MsalServiceException ex)
{
    Console.WriteLine($"Error getting token: {ex.Message}");
}

Console.WriteLine($"Token: {result.AccessToken}");