4
votes

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.

2
Having the same problem too, have you found the solution to this? - Mark Malabanan

2 Answers

3
votes

Solution of Richard Holmes worked for me fine with a little modification: at comparsion (datetime.UtcDateTime <= DateTime.Now) DateTime.Now needs to be change to DateTime.UtcNow

3
votes

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.