Skip to content

Blazor WASM MSAL authentication not working when app is trimmed #43293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
1 task done
yugabe opened this issue Aug 15, 2022 · 9 comments
Closed
1 task done

Blazor WASM MSAL authentication not working when app is trimmed #43293

yugabe opened this issue Aug 15, 2022 · 9 comments
Assignees
Labels
area-blazor Includes: Blazor, Razor Components bug This issue describes a behavior which is not expected - a bug. feature-blazor-msal This issue is related to MSAL usage in Blazor feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly
Milestone

Comments

@yugabe
Copy link

yugabe commented Aug 15, 2022

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 default https://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

  1. Follow the official tutorial
  2. 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

@javiercn javiercn added area-blazor Includes: Blazor, Razor Components feature-blazor-msal This issue is related to MSAL usage in Blazor feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly labels Aug 15, 2022
@MackinnonBuck MackinnonBuck added this to the .NET 7 Planning milestone Aug 15, 2022
@ghost
Copy link

ghost commented Aug 15, 2022

Thanks for contacting us.
We're moving this issue to the .NET 7 Planning milestone for future evaluation / consideration. Because it's not immediately obvious that this is a bug in our framework, we would like to keep this around to collect more feedback, which can later help us determine the impact of it. We will re-evaluate this issue, during our next planning meeting(s).
If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues.
To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

@javiercn
Copy link
Member

@yugabe thanks for contacting us.

We think we addressed this as part of #43954 a fix will be available in RC2. Let us know if you still have issues after updating to RC2.

@javiercn javiercn modified the milestones: 7.0.0, 7.0-rc2 Sep 15, 2022
@yugabe
Copy link
Author

yugabe commented Sep 15, 2022

Thanks, @javiercn! In the meantime, I was investigating, and found what you already know, that the ProviderOptions specifically was being incorrectly trimmed. Will try out as soon as rc2 drops!

@sethgdobbins
Copy link

@yugabe, Thank you so much for submitting this issue and the workarounds. This is incredibly helpful. I spent a lot of time trying to figure it out and I was stuck until I saw your comments.

@danroth27 danroth27 added the bug This issue describes a behavior which is not expected - a bug. label Oct 7, 2022
@AlbertoPa
Copy link

This seems to still be present in RC2 (publish without disabling trimming entirely, and the problem shows up for me).

@javiercn
Copy link
Member

javiercn commented Nov 7, 2022

@AlbertoPa I think we had some build issues and this ended up not making it to the RC2 build, but into the RTM build

@josephatkuvio
Copy link

josephatkuvio commented Nov 7, 2022

@javiercn just FYI, I've been chasing a very similar bug to this all weekend, again with WASM MSAL authentication, but slightly different. Initially I saw the same error as the original poster, but after upgrading to RTM, that error disappeared. However, another error arose, and it ended up also being related to trimming, and the issue is still present in 7.0.0-rtm.22512.1.

I dug as much as I could, and here's what seems to be happening:

When the app is trimmed, on the initial LoginCallback after a successful login, AuthenticationService.ts never gets a valid AuthenticationContext from the LoginComplete delegate for some reason (I'm guessing a trimmed-out DTO). So, when it tries to stringify the JSON state into browser storage, it's just stringifying "undefined" into a string "undefined" and storing that. The whole process breaks after that, because it's all relying on that state to be there and in JSON format.

Then the RemoteAuthenticatorView reports the error as part of the LoginFailed fragment:

"undefined" is not valid JSON

This also only occurs on the initial login flow -- on page refresh, the login completes successfully.

I finally solved this issue by removing Microsoft.AspNetCore.Components.WebAssembly.Authentication from trimming as well (i.e. removing Microsoft.Authentication.WebAssembly.Msal wasn't enough).

<TrimmerRootAssembly Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" />

So something is still wonky with trimming and the underlying DTOs that the Interop works with, and this time it's with WebAssembly.Authentication or one of its dependencies. Apologies that I haven't been able to figure out exactly what the difference is yet, but hopefully this helps.

@yugabe
Copy link
Author

yugabe commented Nov 7, 2022

@josephatkuvio, @javiercn, possibly related: #39839, #44845

It might help pinpointing these issues that at least one of the JSInterop APIs is incorrectly annotated (or rather, it's not).

I haven't had a chance to see if this still occurs or not, will bring word in a day or two with the final release.

@AlbertoPa
Copy link

AlbertoPa commented Nov 9, 2022

@AlbertoPa I think we had some build issues and this ended up not making it to the RC2 build, but into the RTM build

Thank you @javiercn

I have just tested with RTM. While now trimming does not lead to an exception (no message in the console at all), it still seems to change the authentication behavior with AD B2C. The message I got is now

There was an error trying to log you in: '"undefined" is not valid JSON'"

which does not happen if <PublishTrimmed>false</PublishTrimmed> is set.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components bug This issue describes a behavior which is not expected - a bug. feature-blazor-msal This issue is related to MSAL usage in Blazor feature-blazor-wasm This issue is related to and / or impacts Blazor WebAssembly
Projects
None yet
Development

No branches or pull requests

8 participants