VIBHOR GOYAL
VIBHOR GOYAL

Reputation: 503

404 error on using graph API from c# console app

I am currently trying to authenticate Graph API using my C#. I am able to query this API and receive the token successfully from Postman.But when I call same API, I get 404 error.

My code is as below:

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

So, I recieve this in response: StatusCode:404, ReasonPhrase:Not Found Please help me in knowing that where I am doing it wrong.

Note: API with same data works fine with Postman. Though, I have replaced some values here for security reasons.

Upvotes: 0

Views: 1240

Answers (2)

Jason Johnston
Jason Johnston

Reputation: 17702

You should not post form URL encoded content as JSON (PostAsJsonAsync). The body needs to have a content type of application/x-www-form-urlencoded.

But backing up a second, you don't need to implement the protocol yourself when there are libraries out there that do it for you :). We provide and support the Microsoft Authentication Library (MSAL) which makes this easy.

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

Upvotes: 2

HarryPotter
HarryPotter

Reputation: 119

I ran your code and it is wrongly generating query string as below with spaces.

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

There is space between query parameter name and value, see below line.

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

Remove the space and try again

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

Upvotes: -1

Related Questions