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.
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.
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).
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With