skip to Main Content

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?

2

Answers


  1. You facing this error because you are trying to access the local storage in the application initial rendering phase.

    In this case OnAfterRenderAsync doesn’t seem like a solution for your issue due insted set a flag once the component is interactive and then invoke the necessary logic to update the authentication state.

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            var token = await localStorage.GetAsync<string>("headerToken");
            await UpdateAuthenticationState(token.Value);
        }
    }
    

    After that create create a method in your CustomAuthenticationStateProviderService that handles the logic to update the authentication state based on the token from local storage. This method can be called from OnAfterRenderAsync

    public async Task UpdateAuthenticationState(string token)
    {
        if (string.IsNullOrWhiteSpace(token))
        {
            NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(anonymous)));
        }
        else
        {
            var claims = JWTService.DecryptToken(token);
            if (claims == null)
            {
                NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(anonymous)));
            }
            else
            {
                var claimsPrincipal = SetClaimPrincipal(claims);
                NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(claimsPrincipal)));
            }
        }
    }
    

    Do not forgot to remove dependency on Constants.JWTToken

    Login or Signup to reply.
  2. 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)" />
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search