I am getting an invalid token when trying to get it from a C# code (console app). Let me explain.
Scenario 1 (from a Frontend application):
- User enters his/ her credentials in a React app.
- A call is made to https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize endpoint.
- A token id is retrieved (so far so good)
- When the authenticated user makes a request to any endpoint to a private Web API, a second call to the following endpoint: https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token is made attaching the token id from step 3.
- A new JWT token is retrieved
- This JWT token is attached to the headers of the request made to the Web API
- A response is return to the React app.
This is the regular flow we are using when interacting between the React app and the Web API. Here is a visual flow from MS docs
Now, here comes the a new scenario:
Scenario 2 (try to do/ simulate the same from a c# code):
I am trying to get the JWT token to be attached to the header from C# code, I am using MSAL.NET for that. Oficial doc
For my testing I am using a console application:
private static async Task RunAsync()
{
string clientId = "client Id of the application that I have registered using azure app registration in Azure B2C";
string clientSecret = "client secret of the application that I have registered using azure app registration in Azure B2C";
string instance = "https://login.microsoftonline.com/{0}/";
string tenantId = "Tenant Id that I can see when I open the application that I have registered using azure app registration in Azure B2C";
string webAppUri = "web app domain";
// For Web applications that use OpenID Connect Authorization Code flow, use IConfidentialClientApplication
IConfidentialClientApplication app;
app = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri($"https://login.microsoftonline.com/{tenantId}"))
.WithLegacyCacheCompatibility(false)
.Build();
// For confidential clients, this value should use a format similar to {Application ID URI}/.default.
// https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-v2-netcore-daemon#requesting-tokens
string[] scopes = new string[] { $"{webAppUri}/.default" };
AuthenticationResult result = null;
try
{
result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Token acquired");
Console.WriteLine($"{result.AccessToken}");
Console.ResetColor();
}
catch (MsalServiceException ex) when (ex.Message.Contains("AADSTS70011"))
{
// Invalid scope. The scope has to be of the form "https://resourceurl/.default"
// Mitigation: change the scope to be as expected
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Scope provided is not supported");
Console.ResetColor();
}
if (result != null)
{
var httpClient = new HttpClient();
var apiCaller = new ProtectedApiCallHelper(httpClient);
string webApiUrl = "http://localhost:12345/mycustomwebapi/list";
var defaultRequetHeaders = httpClient.DefaultRequestHeaders;
if (defaultRequetHeaders.Accept == null || !defaultRequetHeaders.Accept.Any(m => m.MediaType == "application/json"))
{
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
defaultRequetHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = await httpClient.GetAsync(webApiUrl);
if (response.IsSuccessStatusCode)
{
string json = await response.Content.ReadAsStringAsync();
var jsonResult = JsonConvert.DeserializeObject<List<JObject>>(json);
}
else
{
Console.WriteLine($"Failed to call the Web Api: {response.StatusCode}");
string content = await response.Content.ReadAsStringAsync();
}
Console.ResetColor();
}
}
The problem I am having with the above code is that I am getting a well formatted JWT token and attaching it to the headers. But when making the call to the custom/ secure web api, I am getting a 401 Unauthorized response.
So, I am having a different thoughts on this:
- I am not quite sure if I am missing a step in this C# code vs the steps in Scenario 1.
- Or if I need to configure any special access/ permission in Azure for the web api which is already registered in Azure AD.
- Should I try to do the same steps in C#? call first: https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize endpoint and the https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token to get a valid JWT token?
One additional thing, is that when comparing the JWT tokens (https://jwt.io) from C# vs the one obtain in the Frontend App, there are some different attributes.