How to force a Blazer server application back to the login page when the auth expires
Hi, I have a Blazer server application where I have been tasked with implementing a simple authentication scheme, using our own login page. I've not done this before in a Blazer app, so starting afresh.
I followed this tutorial , and at the top of each page I have a @attribute [Authorize]
.
The login and logout works fine. After the Cookie timeout (e.g. from here)
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
options.LoginPath = Constants.Routes.LOGIN;
options.LogoutPath = Constants.Routes.LOGOUT;
options.Cookie.Name = Constants.AUTH_COOKIE_NAME;
options.ExpireTimeSpan = TimeSpan.FromSeconds(10); // for testing
// options.SlidingExpiration = true;
options.AccessDeniedPath = Constants.Routes.LOGIN;
});
In the above I have just set to 10 seconds for testing.
If I try to navigate to another page, then I am redirected to the login page, which is exactly what I want.
The problem is, if I have a page open, and just leave it sitting there, it does NOT redirect to the login page once the expiration expired.
I Initially just put a timer in MainLayout.razor
to monitor and then manually do the redirect, but to my horror, MainLayout.razor
is actually disposed even though you can still see its DOM elements in devtools! Bizarre!
So I added a scoped service to add the timer, to check for the authorization state, where I constructor inject AuthenticationStateProvider
. IN the timer I call AuthenticationStateProvider.GetAuthenticationStateAsync()
to see when the auth has expired.
First problem was I found that even after my timeout, the user returned from GetAuthenticationStateAsync
was still set to IsAuthorized as true! I tried all sort of schemes to refresh this, but nothing worked.
So I thought I would add the expiry time as one of the Claims, so I have...
DateTime expiry = DateTime.UtcNow.AddSeconds(10);
// We just need to add something, as we are not supporting usernames or roles initially
var claims = new List<Claim>()
{
new(ClaimTypes.Name, Constants.DEFAULT_AUTH_USER),
new(ClaimTypes.Role, Constants.DEFAULT_AUTH_ROLE),
new(ClaimTypes.Expiration, expiry.ToString("o", CultureInfo.InvariantCulture)) // <---- expiry
};
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
// NavigateTo will throw an exception. We need to allow this to bubble up for this to work!
NavigationManager.NavigateTo("/", true);
}
So, that got around that problem, in my service I can now retrieve an react to this expiry time.
The last problem now is how to navigate to the login page from within the service?
I have of course injected NavigationManager
but the NavigationManager.NavigateTo()
just throws an exception. Perhaps as the timer is not on the main thread.
I then tried to inject IJSRuntime
to run some JavaScript to click the logout button.I am not sure if this would have got to the server, but I found that JS methods cannot be called from within a scoped service (the JS function was just not called - and may not have worked anyway).
I then read a post somewhere (there are so many posts I read I have lost track) tried to inject a IServiceProvider
, get a component, and then perhaps call a method on the component to logout out.
Of course I wanted to get something guaranteed to be there as we have no idea what page may be active. Getting the MainLayout
is no good as it is gone (even though it is still in the DOM - bizarre).
As test I then tried to get just the HomePage
while on the home page, and this time I find that IServiceProvider
is disposed!
I tried adding the following...
builder.Services.TryAddEnumerable(
ServiceDescriptor.Scoped<CircuitHandler, UserCircuitHandler>());
as mentioned in some of the MS documentation, injecting the AuthenticationStateProvider
into here, and watching out for the AuthenticationStateChanged
event, but this just never fired, then was not sure what I could do within here anyway.
I am now out of ideas, all I want to do is go to the login page when the auth expires.
Would anyone know how to have this occur either automatically, or how I can manually navigate to the login page?
Thanks in advance for any information