'WebAPI and Azure AD - works fine with obsolete setup, but not when updated to new setup

I have an existing Web API project, which uses Azure AD for authentication.

Prior to my joining the organisation, I understand that this was an ASP.Net 3.1 project, that has since been upgraded to ASP.Net 6

Authentication is set up with the following code:

services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
    .AddAzureADBearer(options => Configuration.Bind("AzureAd", options));

However, this gives warnings that the methods being used are obsolete, and I'm trying to remove those warnings. The problem that I'm having is that, after making what I believe are the correct changes, the code works perfectly from the Swagger UI, but client software is no longer able to connect.

The relevant code, and the changes I've made, are:

startup.cs:

// The two lines shown above are replaced with:
services.AddMicrosoftIdentityWebApiAuthentication(Configuration, "AzureAd");

appsettings.json:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "<my-microsoft-domain>",
    "TenantId": "<my-microsoft-tenant-id>",
    "ClientId": "api://<my-client-id>",   // I removed "api://", otherwise it didn't work in Swagger UI
    "ClientSecret": "<my secret>",
    "Scope": "api://<my-client-id>/<my-scope>"
  }
}

Example client code:

var content = new FormUrlEncodedContent(new[]
{
    new KeyValuePair<string, string>("grant_type", "client_credentials"),
    new KeyValuePair<string, string>("client_id", "<my-client-id>"),
    new KeyValuePair<string, string>("client_secret", "<my-secret>"),
    new KeyValuePair<string, string>("scope", "api://<my-client-id>/.default")
});

using var httpClient = new HttpClient();
var requestResult = await httpClient.PostAsync("https://login.microsoftonline.com/<my-microsoft-domain>/oauth2/v2.0/token", content);
var resultText = await requestResult.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<AuthResponse>(resultText);

var apiHttpClient = new HttpClient();
apiHttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.access_token);

HttpResponseMessage result = await apiHttpClient.GetAsync("https://localhost:5001/api/<my-api-route>");
Assert.True(result.IsSuccessStatusCode); // Works with the old server code. Fails with the new code because the status code is 401

In the above code, the client does successfully get a token, but it seems that the token it’s getting is invalid for the application, and I don’t understand why.

I've read on various StackOverflow and other posts that changing the AD Application's manifest in Azure and setting "accessTokenAcceptedVersion": 2 can be required for the newer APIs, so I made this change to my AD Application, but it had no effect on the 401.

Does anyone have any clues that could point me in the right direction to fix this? Thanks!



Solution 1:[1]

  1. Please try by removing api:// from scope "api://<my-client-id>/.default" , as sometimes the backend already has this prefix depending on the application configuration
  2. Also when token is decoded ,make note of values ..like "iss" "aud" etc.; If iss has v1 endpoint "accessTokenAcceptedVersion" must be null or 1 .
  3. Also please check if api permissions are given in the portal and granted admin consent for the scope exposed in "exposed an api" blade.

Solution 2:[2]

I managed to fix this after a lot of trial+error.

It turns out that I needed to add a new application to AD for my client, and give that application access to the API exposed by the original application.

Apparently, having both the client and the server log in using the same application was allowed with the old way of registering the relevant services, but not with the new version.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 kavyasaraboju-MT
Solution 2 DeanD