javascriptc#asp.net-corejwtblazor

Custom AuthenticationStateProviderService error on localstorage


I have a CustomAuthenticationStateProviderService. At first I was using a constant for storing the JWTToken but now I want to switch to using local storage.

public class Constants
{
    public static string JWTToken { get; set; } = "";
}

public class CustomAuthenticationStateProviderService : AuthenticationStateProvider
{
    private readonly ClaimsPrincipal anonymous = new(new ClaimsIdentity());
    private readonly ProtectedLocalStorage localStorage;

    public CustomAuthenticationStateProviderService(ProtectedLocalStorage localStorage)
    {
        this.localStorage = localStorage;
    }

    public async override Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        try
        {
            var token = await localStorage.GetAsync<string>("headerToken");

            
            if (string.IsNullOrWhiteSpace(Constants.JWTToken))
                return await Task.FromResult(new AuthenticationState(anonymous));

            var claims = JWTService.DecryptToken(Constants.JWTToken);
            if (claims == null) return await Task.FromResult(new AuthenticationState(anonymous));

            var claimsPrincipal = SetClaimPrincipal(claims);
            return await Task.FromResult(new AuthenticationState(claimsPrincipal));
        }
        catch (Exception ex)
        {
            return await Task.FromResult(new AuthenticationState(anonymous));
        }
    }
}

I have other methods inside the class but this is the only one I started using the localstorage with so far. An error is thrown before it ever gets to the first page of the application which is

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

I understand it wants me to use the OnAfterRenderAsync method but this is happening before it even goes to any of my pages so that would not help. Plus I need it to be used in theOnInitialized for logic reason which is why my pages are using @rendermode @(new InteractiveServerRenderMode(false))

I surrounded the get local storage with a try catch which fixes. I found an answer that says that this behavior is caused by the builder.Services.AddCascadingAuthenticationState(); that I have in the program.cs

try
{
    var token = await localStorage.GetAsync<string>("headerToken");
}
catch (InvalidOperationException)
{
}

That fixes the first issue but then I get a new error: JavaScript interop calls cannot be issued at this time. This is because the circuit has disconnected and is being disposed.

How should I fix these errors to make the local storage work as intended?


Solution

  • Try to set Prerender of pages off.

    In your App.razor Add in your <head> section :

    <HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />
    

    And in your <body> section add:

    <Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />