Description
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
The MSAL library interop layer for Blazor seems to be subject to it being trimmed of crucial configuration information from the assembly upon it being published. It seems to be a somewhat known problem (#38082) but the issue hasn't been fixed since .NET 6 previews (this happens to me on .NET 7 Preview 7 too), and the docs make no mention of a workaround either. This issue is related, but not a duplicate.
The problem can be observed by configuring a Blazor WASM application (might be hosted) to use Azure AD B2C.
services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication); // Binding won't work!
options.ProviderOptions.DefaultAccessTokenScopes.Add("api://{domain}/{directory}/{scope}");
options.ProviderOptions.LoginMode = "redirect";
});
The workaround seems to be (I cannot validate it works for all cases, as I have no idea what scenarios are related) to explicitly set the authentication configuration values from the configuration, AS WELL AS providing the following default scopes, like so:
services.AddMsalAuthentication(options =>
{
options.ProviderOptions.Authentication = new() // Need to set the property manually and hard code the config values instead of pulling them from appsettings.json configuration
{
Authority = "https://{b2c-instance}.b2clogin.com/{domain-or-tenantid}/{user-flow}",
ValidateAuthority = true,
ClientId = "{client-id-guid}"
};
options.ProviderOptions.DefaultAccessTokenScopes.Add("openid"); // Need to provide the scopes that are "by default" should be included with the underlying API call
options.ProviderOptions.DefaultAccessTokenScopes.Add("offline_access");
options.ProviderOptions.DefaultAccessTokenScopes.Add("profile");
options.ProviderOptions.DefaultAccessTokenScopes.Add("email");
options.ProviderOptions.DefaultAccessTokenScopes.Add("api://{domain}/{directory}/{scope}");
options.ProviderOptions.LoginMode = "redirect";
});
The symptoms can include in a published app:
- Not connecting/redirecting to the correct IdP instance, including the
common
instance or a different Azure AD instance. - The Client ID not being present when redirected to the IdP.
- Various exception messages during authentication including:
- the client app not being recognized in the Azure AD (not B2C) tenant,
- "
interaction already in progress
" message returned from MSAL.
- The Azure AD reporting either the provided Client ID or
null
as being unable to be resolved in the tenant:AADSTS700016: Application with identifier 'null' was not found in the directory '{Azure AD tenant name}'.
I could only reproduce the behavior when not trimming the app in debug mode by explicitly setting the options.ProviderOptions.Authentication.Authority
to use https://login.microsoftonline.com/common/
. NOT setting this property won't reproduce the problems either, as the MSAL initialization will throw an exception not being able to construct the URL.
Another possible "workaround" seems to be to disable trimming entirely (not the greatest tradeoff) by setting the <PublishTrimmed>false</PublishTrimmed>
MSBuild flag on the Client project (or probably via command line flags) or including the MSAL library in the list of assemblies not to trim (<TrimmerRootAssembly Include="Microsoft.Authentication.WebAssembly.Msal" />
).
I think the problem is during JSON serialization of the options containing the configuration, it is possibly not properly annotated for being trimmed (it is not annotated at all).
More info for those affected or stumped by this like I was for over a week:
I was following the documentation to create a hosted Blazor app with Azure AD B2C MSAL authentication.
The only other modification was that I set up the default login mode to use "redirect" instead of "popup", because the issues were not straightforward to debug at all.
Everything works fine, but only locally. When publishing the app to Azure App Service (on Linux), the following phenomenon was a string of events wholly unexpected:
- First, the app shows NOT the (correctly) configured B2C IdP endpoint with custom providers etc. Instead, the default Microsoft account login page is shown to the user. Upon inspecting the URL which should show the tenant's
{tenant}.b2clogin.com
IdP instance, the user can see the defaulthttps://login.microsoftonline.com/common/
instance instead. Further, the application Client ID is not detected:https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=null&...
- Investigating, I tried to scope the problem down, and hard coded the appsettings.json configuration instead of directly binding it to the MSAL provider
Authentication
object. - Finally, after going through most of the source for Blazor WASM Authentication and MSAL.js, I found the linked issue. The final piece was to also add the scopes that are supposedly included by default.
Expected Behavior
I expect following the tutorial should work when the app is deployed, and authentication should be available as configured.
Steps To Reproduce
- Follow the official tutorial
- Publish the application.
In the resulting app the configuration won't be picked up by the client MSAL.js correctly.
Exceptions (if any)
No response
.NET Version
No response
Anything else?
No response