Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Blazor Custom Authentication Provider Error

I am creating a Blazor UI application to talk to an API. Currently, I am attempting to get a custom provider for this working and I am getting a few errors.

This is the customer AuthenticationStateProvider

public class APIAuthenticationStateProvider : AuthenticationStateProvider
{
    private readonly HttpClient _httpClient;
    private readonly ILocalStorageService _localStorage;
    public APIAuthenticationStateProvider(HttpClient httpClient, ILocalStorageService localStorage)
    {
        _httpClient = httpClient;
        _localStorage = localStorage;
    }
    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        var token = await _localStorage.GetItemAsync<string>("authToken");

        if (string.IsNullOrWhiteSpace(token))
        {
            return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
        }

        _httpClient.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("bearer", token);

        var claims = ParseClaims(token);

        return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(claims,"jwt")));
    }
}

My Configure Services function in Startup.cs file looks like this

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddServerSideBlazor();
    services.AddBlazoredLocalStorage();
    services.AddScoped<AuthenticationStateProvider, APIAuthenticationStateProvider>();
    services.AddTransient<IAuthRepository, AuthRepository>();
    services.AddHttpClient();
}

When I executed I get the following exception

InvalidOperationException: JavaScript interop calls cannot be issued at this time. This is because the component is being statically rendererd. When prerendering is enabled, JavaScript interop calls can only be performed during the OnAfterRenderAsync lifecycle method.

Microsoft.AspNetCore.Components.Server.Circuits.RemoteJSRuntime.BeginInvokeJS(long asyncHandle, string identifier, string argsJson)
Microsoft.JSInterop.JSRuntime.InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object[] args)
Microsoft.JSInterop.JSRuntime.InvokeWithDefaultCancellation<T>(string identifier, object[] args)
System.Threading.Tasks.ValueTask<TResult>.get_Result()
System.Runtime.CompilerServices.ValueTaskAwaiter<TResult>.GetResult()
Blazored.LocalStorage.LocalStorageService.GetItemAsync<T>(string key)
BookStore_UI.Providers.APIAuthenticationStateProvider.GetAuthenticationStateAsync() in APIAuthenticationStateProvider.cs

I am not sure if the error is being clear and that is where the error really is, or if there is some other underlying step that I am missing. I think it might have to do with the use of Local Storage.

like image 516
trevoirwilliams Avatar asked Aug 26 '26 17:08

trevoirwilliams


2 Answers

in the _Host.cshtml, change the render-mode

render-mode="ServerPrerendered"

to render-mode="Server"

like image 116
EMASTER Max Avatar answered Aug 29 '26 08:08

EMASTER Max


This code: var token = await _localStorage.GetItemAsync<string>("authToken"); is executed while JavaScript is not available yet. Look up the docs how to deal with such cases when you try to access the local storage. They specifically show how to do such a task.

Your ConfigureServices should look something like this:

services.AddRazorPages();
services.AddServerSideBlazor();
services.AddBlazoredLocalStorage();
services.AddHttpClient();
services.AddScoped<APIAuthenticationStateProvider>();
services.AddScoped<AuthenticationStateProvider>(provider => 
       provider.GetRequiredService<APIAuthenticationStateProvider>());
services.AddTransient<IAuthRepository, AuthRepository>();

_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token);

The GetAuthenticationStateAsync method should have a single objective: Returning the Authentication State object, which is why you shouldn't use it to configure the HttpClient object. You should do it (reading the Jwt token from the local storage and assigning it to the request' Authorization header) from the very location you are going to call a method of the HttpClient service.

Hope this helps...

like image 23
enet Avatar answered Aug 29 '26 08:08

enet



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!