7

My first ever attempt at using MSAL Authorization and it fails for me in Blazor. Any Clues (it's going to be a simple answer I think?)

Small repo available here

Client File: Program.cs

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");

builder.Services.AddMsalAuthentication(options => //THROWS EXCEPTION!!!!!
{
    options.ProviderOptions.AdditionalScopesToConsent.Add($"https://graph.microsoft.com/User.Read");
});

var baseAddress = builder.HostEnvironment.BaseAddress;
builder.Services.AddHttpClient(baseAddress, client => client.BaseAddress = new Uri(baseAddress))
        .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

Unexpected Result: Exception thrown

crit:

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: Cannot read property 'join' of undefined TypeError: Cannot read property 'join' of undefined at Function.createUserManager (https://localhost:44391/_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js:1:6020) at Function.initializeCore (https://localhost:44391/_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js:1:5035) at Function.init (https://localhost:44391/_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js:1:4575) at https://localhost:44391/_framework/blazor.webassembly.js:1:9873 at new Promise () at Object.beginInvokeJSFromDotNet (https://localhost:44391/_framework/blazor.webassembly.js:1:9841) at _mono_wasm_invoke_js_marshalled (https://localhost:44391/_framework/wasm/dotnet.3.2.0.js:1:171294) at do_icall (wasm-function[6049]:0x10f8b1) at do_icall_wrapper (wasm-function[1896]:0x50b6a) at interp_exec_method (wasm-function[1120]:0x2588e)

2 Answers 2

6

Check that the MSAL script is in your index page:

<script src="_content/Microsoft.Authentication.WebAssembly.Msal/AuthenticationService.js"></script>

This fixed the same issue for me, when I was retro-fitting MSAL into an existing Blazor WASM app that was referencing the JS file:

<script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>

If this JS ref exists, remove it and replace with the MSAL version.

Sign up to request clarification or add additional context in comments.

4 Comments

Also, make sure the old index file is not cached in the browser. It's an old one, but still catches me out.
For those who wonder how to avoid old index file, hold ctrl + click refresh on edge to refresh the page and the cache.
Great, just needed this to fix my issue. I followed this article to setup auth with msal medium.com/@mariekie/…
how come there is no elegant solution and we have to include JS files like peasants??
3

Try something like this...

builder.Services.AddMsalAuthentication(options =>
{
    ...

    options.ProviderOptions.AdditionalScopesToConsent.Add(
        "https://graph.microsoft.com/Mail.Send");
    options.ProviderOptions.AdditionalScopesToConsent.Add(
        "https://graph.microsoft.com/User.Read");
}

https://learn.microsoft.com/en-us/aspnet/core/security/blazor/webassembly/additional-scenarios?view=aspnetcore-3.1#request-additional-access-tokens

Hope it helps!

EDIT 1: My client Program.cs

using System.Net.Http; using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

Add the below reference in Index.html page

<script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>
public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            builder.RootComponents.Add<App>("app");

            builder.Services.AddHttpClient("BlazorWasmAADMsal.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
                .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

            // Supply HttpClient instances that include access tokens when making requests to the server project
            builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("BlazorWasmAADMsal.ServerAPI"));

            builder.Services.AddMsalAuthentication(options =>
            {
                builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
                options.ProviderOptions.DefaultAccessTokenScopes.Add("api://XXXXXXXXXXXXXXXXXX/API.Access");
            });

            await builder.Build().RunAsync();
        }

EDIT 2: Did some minor changes. The working project has been uploaded here. Successful login screen shot added in answer for reference. :-)

enter image description here

EDIT 3:

Azure Active Directory (AAD) Microsoft Graph API scopes are required by an app to read user data and send mail. After adding the Microsoft Graph API permissions in the Azure AAD portal, the additional scopes are configured in the Client app.

Last time i missed to enabled below lines, I have enabled it in Program.cs

  builder.Services.AddMsalAuthentication(options =>
       {
options.ProviderOptions.DefaultAccessTokenScopes.Add("https://graph.microsoft.com/User.Read");
     }

It was a bizarre error, never expected and had seen it.

The reference of AuthenticationService.js in Index.html was not correct. I have corrected it to be...

<script src="_content/Microsoft.Authentication.WebAssembly.Msal/AuthenticationService.js"></script>

I have also uploaded the latest code here

The IAccessTokenProvider.RequestToken method provides an overload that allows an app to provision an access token with a given set of scopes.

In a Razor component, you can write something like below:

@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject IAccessTokenProvider TokenProvider

...

var tokenResult = await TokenProvider.RequestAccessToken(
    new AccessTokenRequestOptions
    {
        Scopes = new[] { "https://graph.microsoft.com/Mail.Send", 
            "https://graph.microsoft.com/User.Read" }
    });

if (tokenResult.TryGetToken(out var token))
{
    ...
}

AccessTokenResult.TryGetToken returns:

true with the token for use. false if the token isn't retrieved.

Hope it helps you.

13 Comments

The exception is thrown by AddMsalAuthentication, it never reaches the call to AdditionalScopesToConsent method. In my project I hold the scopes in appsettings.json and iterate through them, but this iteration is never started because AddMsalAuthentication throws an exception even if there are no scopes to add.
Does it work if you remove MsalAuth code? Hope you don't have this line duplicate in your Program.cs builder.RootComponents.Add<App>("app"); A simple minimal repro project will help in understanding the root cause.
Yes, works perfectly without the builder.Services.AddMsalAuthentication line. Only invoked builder.RootComponents.Add<App>("app") the one time only. Thank you for the help. The project is quite large, but fails very early on in the program.cs start up file.
I think I am using the AddMsalAuthentication from the wrong library. Any chance of a list of Nuget packages and using statements that provide the AddMsalAuthentication extension method? I tried a brand new clean WebAssembly project and the method was not known, adding these packages and usings made it compile, but it still throws same exception.
Which type of Auth are you using? Please refer this. It has explained, how to configure, step by steps. I'm using this- "Microsoft.Authentication.WebAssembly.Msal" Version="3.2.0" and it's working fine for me.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.