I'm building an application with Blazor Web Assembly hosted in an ASP.NET Core application. Based from scratch and the boilerplate from Visual Studio, I created the solution.
In another solution I have a web API build with ASP.NET Core (I will call them External API). This application has its own URL and registration in the App Registration
in the Azure portal. From Swagger, I can access the APIs and their are protected by the Microsoft authentication.
In the same Tenant ID
, I created 2 new app registrations for the Blazor Server and the Blazor WebAssembly. The UI can connect to the Active Directory in order to authenticate the users and the Web Assembly can call the APIs in the server project. For more details about it, see my previous post.
I define the API like the following code
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
public class CurrenciesController : ControllerBase
{
// omit the constructor and the injection
// AzureAd:Scopes is "User.Read"
[HttpGet]
[Authorize]
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(List<Currency>))]
public ActionResult GetCurrencies()
{
return Ok(repo.GetCurrencies());
}
}
Now, my problem is from the Web Assembly to call the External API. In the Program.cs I added
builder.Services.AddScoped<MiddletierApiAuthorizationMessageHandler>();
builder.Services.AddHttpClient("Middletier.UI.ServerAPI", client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
builder.Services.AddHttpClient("Middletier.API", client =>
client.BaseAddress = new Uri("https://localhost:50441"))
.AddHttpMessageHandler<MiddletierApiAuthorizationMessageHandler>();
builder.Services.AddScoped(sp =>
sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("Middletier.UI.ServerAPI"));
builder.Services.AddScoped(sp =>
sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("Middletier.API"));
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
// this is for the Blazor Server
options.ProviderOptions
.DefaultAccessTokenScopes
.Add("api://544b368d-xxx/user.access");
});
The MiddletierApiAuthorizationMessageHandler
implementation is this one
public class MiddletierApiAuthorizationMessageHandler : AuthorizationMessageHandler
{
public MiddletierApiAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "https://localhost:7575" },
// External API
scopes: new[] { "api://d3de6da4-xxx/User.Read" });
}
}
Then, in a Razor page I added
@inject IHttpClientFactory _clientFactory
@code {
private Currency[]? currencies;
protected override async Task OnInitializedAsync()
{
try
{
var httpClient = _clientFactory.CreateClient("Middletier.API");
currencies = await httpClient
.GetFromJsonAsync<Currency[]>("/api/v1/Currencies");
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
}
}
The call is always 401 (Unauthorized)
. I added CORS. What do I miss?