'How to get access token in LeftNavigation in Shared folder that was returned through using AddMsalAuthentication in program.cs for authentication

In Blazor WASM, I want to display or hide some of the menu based on user roles. For that I need Access Token once user authenticate and I will pass that token to Graph API to get user details.

I am using

builder.Services.AddMsalAuthentication(options =>
{
    builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
    options.ProviderOptions.DefaultAccessTokenScopes.Add("https://graph.microsoft.com/User.Read");
    options.ProviderOptions.LoginMode = "redirect"; 
});

in program.cs file to authenticate the user.

if i use below code on index.razor page, i am getting the token

    var tokenResult = await AuthenticationService.RequestAccessToken();
          
    if (tokenResult.TryGetToken(out var token))
    {
    -----
    }

but i want that token on LeftNavigation.razor in shared folder, so that i can show or hide the menu as per user access rights.

Error after adding suggesting solution

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: Object reference not set to an instance of an object. System.NullReferenceException: Object reference not set to an instance of an object. at Test.Web.Shared.LeftNavigation.OnInitialized() in C:\Users\pgoel\source\repos\Test\Test.Web\Shared\LeftNavigation.razor.cs:line 78 at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()

Below is Program.cs file


using Blazored.LocalStorage;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Test.SharedServices;
using Test.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.Authentication.WebAssembly.Msal;
using System.Net.Http;
using Test.Web.Data;
using Toolbelt.Blazor.Extensions.DependencyInjection;

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddTelerikBlazor();

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

//graph api
builder.Services.AddHttpClient("GraphAPI",
        client => client.BaseAddress = new Uri("https://graph.microsoft.com"))
    .AddHttpMessageHandler<GraphAPIAuthorizationMessageHandler>();

// adding CustomAuthorizationMessageHandler
builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
builder.Services.AddHttpClient("Test API", (sp, client) =>
{
    client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress);
    client.EnableIntercept(sp);
}).AddHttpMessageHandler<CustomAuthorizationMessageHandler>();

builder.Services.AddScoped(sp =>
sp.GetRequiredService<IHttpClientFactory>().CreateClient("Test API"));
builder.Services.AddHttpClientInterceptor();

builder.Services.AddMsalAuthentication(options =>
{
    builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
    options.ProviderOptions.DefaultAccessTokenScopes.Add("https://graph.microsoft.com/User.Read");
    options.ProviderOptions.LoginMode = "redirect"; 
});
builder.Services.AddGraphClient("https://graph.microsoft.com/User.Read");

builder.Services.AddScoped<HttpInterceptorService>();
builder.Services.AddScoped<GraphAPIAuthorizationMessageHandler>();
builder.Services.AddAuthorizationCore();

builder.Services.AddBlazoredLocalStorage();

var host = builder.Build();
var authenticationService = host.Services.GetRequiredService<UserAuth>();
await authenticationService.Initialize();

await builder.Build().RunAsync();


Solution 1:[1]

You should be able to do something like this in NavMenu to trigger a re-render when the Authentication State changes:

@inject AuthenticationStateProvider authenticationStateProvider
@implements IDisposable

....

@code {
    private ClaimsPrincipal user = new ClaimsPrincipal();

     protected override void OnInitialized()
        =>  this.authenticationStateProvider.AuthenticationStateChanged += this.OnStateChanged;

    private async void OnStateChanged(Task<AuthenticationState> state)
    {
        var authState = await state;
        this.user = authState.User;
        await this.InvokeAsync(this.StateHasChanged);
    }

    public void Dispose()
        =>  this.authenticationStateProvider.AuthenticationStateChanged -= this.OnStateChanged;
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 MrC aka Shaun Curtis