I've been trying to deploy my Blazor PWA for 2 days without any success so far, if someone has an idea of what I’m doing wrong I would be really grateful. hello I could resolve most of my issues by myself but I'm now stuck on a CORS problem using AAD.
Here's my project setup:
So I’ve created an App registration, added my static web site address as SPA uri and uncheck both implicit. In my blazor client, program.cs, I’ve added the following code to connect to AAD:
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication); //contains clientId, Authority
options.ProviderOptions.DefaultAccessTokenScopes.Add("https://graph.microsoft.com/User.Read");
options.ProviderOptions.LoginMode = "redirect";
});
That works great too, I can login, authorize view works as expected.
The problem is when I try to authenticate Azure functions with «Login with Azure Active Directory», I' tried with both express and advanced configurations (using clientId, tenantId) but when I
Access to fetch at 'https://login.windows.net/tenantid/oauth2/authorize ... (redirected from 'https://myfunctionstorage.azurewebsites.net/api/client/list') from origin 'https://*****9.web.core.windows.net' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
I have of course enabled CORS for my Blazor Client Address on Azure Functions configuration but the problem seems to be about the login windows uri...
Also, if I enable the token id implicit flow in the App registration and access the login url in the browser it works perfectly fine.
All the examples I could find so far are about msal 1.0 and App registration using implicit flow instead of SPA so it doesn't help...
Thank you for your time and your help.
UPDATE: I’ve done more researches since yesterday and I think it could by related to my HTTPClient, currently I use the basic one (with just a base adress).
But I’ve seen on some example that when using the Client using AAD it needs more parameters, for example:
builder.Services.AddHttpClient("companiesAPI", cl => { cl.BaseAddress = new Uri("https://localhost:5001/api/"); }) .AddHttpMessageHandler(sp => { var handler = sp.GetService<AuthorizationMessageHandler>() .ConfigureHandler( authorizedUrls: new[] { "https://localhost:5001" }, scopes: new[] { "companyApi" } ); return handler; });
Is that AuthorizationMessageHandler needed ? Also I see some references to the need of using the «use_impersonation» scope.
Are those changes (on HttpClient and the use_impersonation scope) also required when using msal2/SPA app registration ?
Thank you
If want to call the Azure function projected by Azure AD in Blazor webassembly client, please refer to the following steps If you want to call Azure function projected by Azure AD in angular application, please refer to the following code
Create Azure AD application to protect function
Enable Azure Active Directory in your App Service app
Create Client AD application to access function
Code
AuthorizationMessageHandler
classusing Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
{
public CustomAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "<your function app url>" },
scopes: new[] { "<the function app API scope your define>" });
}
}
Program.cs
. public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
// register CustomAuthorizationMessageHandler
builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
// configure httpclient
// call the following code please add packageMicrosoft.Extensions.Http 3.1.0
builder.Services.AddHttpClient("ServerAPI", client =>
client.BaseAddress = new Uri("<your function app url>"))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
// register the httpclient
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("ServerAPI"));
// configure Azure AD auth
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("<the function app API scope your define>");
});
await builder.Build().RunAsync();
}
@page "/fetchdata"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject HttpClient Http
<h1>Call Azure Function</h1>
<p>This component demonstrates fetching data from the server.</p>
<p>Result: @forecasts</p>
<button class="btn btn-primary" @onclick="CallFun">Call Function</button>
@code {
private string forecasts;
protected async Task CallFun()
{
forecasts = await Http.GetStringAsync("api/http");
}
}