Reputation: 86
In the process of writing a desktop .NET Core application that authenticates to a custom API using MSAL, I've encountered a problem with the tokens not containing the necessary scopes for the custom API - but only when retrieving the token using Web Account Manager (WAM).
I'm developing a solution with a desktop app that calls a custom API, both written in .NET Core 6. The app should preferably be able to authenticate with the current user account to the API without requiring user interaction. All the clients are connected to a specific Azure tenant, so my understanding is that using WAM to retrieve the default Microsoft account for the machine should accomplish this.
An alternative solution would be to use Integrated Windows authentication (IWA), but I would prefer to not use this solution as it only supports AD FS-federated users - it would be best for the application to also support cases where this is not so.
I am able to (interactively) retrieve the necessary token for the custom API using the standard method in MSAL.NET:
var scopes = new[] { "User.Read", $"api://{clientId}/access_as_user" };
var client = PublicClientApplicationBuilder.Create(clientId)
.WithAuthority("https://login.microsoftonline.com/organizations/v2.0")
.WithRedirectUri("customApp://auth")
.Build();
var result = await client
.AcquireTokenInteractive(scopes)
.ExecuteAsync();
The token retrieved in this way can be used to authenticate to the API which has been set up to use Microsoft.Identity:
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
The problem occurs when I attempt to instead use WAM for retrieving the token:
var scopes = new[] { "User.Read", $"api://{clientId}/access_as_user" };
var client = PublicClientApplicationBuilder
.Create(clientId)
.WithBroker(true)
.Build();
var result = await client
.AcquireTokenInteractive(scopes)
.WithAccount(PublicClientApplication.OperatingSystemAccount)
.WithParentActivityOrWindow(NativeMethods.GetConsoleWindow())
.ExecuteAsync();
The token is returned without error, but the API does not accept it, giving an error of IDX10511: Signature validation failed.
Attempting to debug the JWT tokens using jwt.ms I can observe several differences:
I've tried explicitly setting the authority and redirect URI on the WAM but this does not seem to make any difference.
If I remove the graph User.Read scope from my request, getting the token from WAM fails with the following error which suggests it is required:
CAA20002: AADST90008: The user or administrator has not consented to use the application with ID xx. This happened because application is misconfigured: it must require access to Microsoft Graph by specifying at least "Sign in and read user profile" permission.
Any help is appreciated!
Upvotes: 0
Views: 1763
Reputation: 576
The issue is related with the scopes you are passing:
var scopes = new[] { "User.Read", $"api://{clientId}/access_as_user" };
Which are scopes for 2 different APIs:
When you make the request to get the accessToken, AAD should be looking only for the first User.Read scope, and returns you back an accessToken for MS Graph, hence the '00000003-0000-0000-c000-000000000000' value you see in your audience.
Try to remove the User.Read permission from the array and see if it works.
Upvotes: 2