I am trying to create a RevalidatingServerAuthenticationStateProvider to check if a ClaimsPrincipal has expired.
I have created an implementation of it (TokenExpiryAuthStateProvider) and registered it in ConfigureServices but the constructor is never called nor is ValidateAuthenticationStateAsync. (I also tried the implementation factory to register it, same result)
I injected AuthStateProvider to MainLayout to see what got injected and it is still injecting ServerAuthenticationStateProvider. What am I doing wrong?
My implementation that is never called:
public class TokenExpiryAuthStateProvider : RevalidatingServerAuthenticationStateProvider
{
protected override TimeSpan RevalidationInterval => TimeSpan.FromSeconds(10);
public TokenExpiryAuthStateProvider(ILoggerFactory logger) : base(logger)
{
}
protected override Task<bool> ValidateAuthenticationStateAsync(AuthenticationState authenticationState, CancellationToken cancellationToken)
{
DateTime expiry = authenticationState.User.GetExpiryDate();
if (expiry < DateTime.UtcNow)
return Task.FromResult(false);
else
return Task.FromResult(true);
}
}
Configure Services
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<HttpClientFactory>();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
services.ConfigureApplicationCookie(options =>
{
options.Cookie.HttpOnly = true;
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
});
services.AddScoped<AuthenticationStateProvider, TokenExpiryAuthStateProvider>();
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddHttpContextAccessor();
services.AddCsla().WithBlazorServerSupport();
}
The injected object:
UPDATE: Should have mentioned this before...I am currently working on a server-side project but the layouts are in a shared ui library. The suggestion made by @enet therefore isn't suitable as RevalidatingServerAuthenticationStateProvider will not be available in the shared project and therefore TokenExpiryAuthStateProvider is only in the server project.
Still, following on from the suggestion by enet I declared an interface and updated ConfigureServices so that I could inject it in my shared layout to try it out:
services.AddScoped<TokenExpiryAuthStateProvider>();
services.AddScoped<ITokenExpiryProvider>(provider =>
provider.GetRequiredService<TokenExpiryAuthStateProvider>());
I then injected an ITokenExpiryProvider to MainLayout, and subscribed to the AuthenticationStateChanged event (The only event I could see). After the refresh interval, no state changed has been fired and ValidateAuthenticationStateAsync has not been called. How do I start the Revalidating process and why does AddScoped<AuthenticationStateProvider, TokenExpiryAuthStateProvider> not work?
I think for my simple use case it will be easier to create a timer class which is injected with AuthenticationStateProvider; this class then encapsulates the periodic checking of the auth state and in turn raises an event when the session is expiring. (This is how I was expecting RevalidatingServerAuthenticationStateProvider to work, checking ValidateAuthenticationStateAsync whenever RefreshInterval had elapsed)
I would still like to know why my implementation isn't working and why ValidateAuthenticationStateAsync is never being called; I must be registering the service incorrectly somehow.