Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Client side Blazor authentication token expired on server side

I'm having a problem with client side token with blazor authentication. I implemented the authentication based on this blog posts, I'm using webassembly project.

https://chrissainty.com/securing-your-blazor-apps-introduction-to-authentication-with-blazor/

https://chrissainty.com/securing-your-blazor-apps-authentication-with-clientside-blazor-using-webapi-aspnet-core-identity/

https://chrissainty.com/securing-your-blazor-apps-configuring-role-based-authorization-with-client-side-blazor/

Almost everything works fine, but I'm having an issue. On server side the authentication token gets expired but on client side, I still have the authentication token on local storage. My function to get local state:

   public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        var savedToken = await _localStorage.GetItemAsync<string>("authToken");            

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

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

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

Well, looking on response header I can see that server tells me that my local token is expired, but I don't know how to get this information on client side. So my client side tells me that I'm authenticated but on server side I'm not. I don't want to make a request to test this every time that my method GetAuthenticationStateAsync runs to clean manually the token of local storage. How is the best way to deal with this behavor? I'm missing someting on my code?

The header response: "www-authenticate: Bearer error="invalid_token", error_description="The token expired at '02/24/2020 11:52:35'""

Thanks.

like image 247
Lucas Luis Baruffi Avatar asked Feb 25 '20 11:02

Lucas Luis Baruffi


People also ask

How do you authenticate in Blazor?

To create a new Blazor WebAssembly project with an authentication mechanism: After choosing the Blazor WebAssembly App template in the Create a new ASP.NET Core Web Application dialog, select Change under Authentication. Select Individual User Accounts to use ASP.NET Core's Identity system.

Is Blazor server-side or client side?

The Blazor web framework allows Razor components to be hosted in different ways. They can run server-side in ASP.NET Core (Blazor Server) and client-side in the browser on a WebAssembly-based . NET runtime (Blazor WebAssembly or Blazor WASM).

How do I get the access token from a Blazor server-side web app?

In order to get the access token you can use the HttpContext object, but since Blazor is SignalR-based, you'll have to do it the only time the HttpContext object is available, when the connection to your application is an HTTP connection, and not a WebSocket connection.


1 Answers

I've followed the same blog posts you did, and it seems we have to do our own expiration check on the client side. In the ApiAuthenticationStateProvider on the client side, I did this:

public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
    var savedToken = await _localStorage.GetItemAsync<string>("authToken");
    var anonymousState = new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));

    // Not authenticated
    if (string.IsNullOrWhiteSpace(savedToken))
    {
        return anonymousState;
    }

    var claims = ParseClaimsFromJwt(savedToken);
    // Checks the exp field of the token
    var expiry = claims.Where(claim => claim.Type.Equals("exp")).FirstOrDefault();
    if (expiry == null)
        return anonymousState;

    // The exp field is in Unix time
    var datetime = DateTimeOffset.FromUnixTimeSeconds(long.Parse(expiry.Value));
    if (datetime.UtcDateTime <= DateTime.UtcNow)
        return anonymousState;

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

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

It's not pretty, but it gets the job done for now.

like image 141
Devwulf Avatar answered Dec 26 '22 15:12

Devwulf