How to force a Blazer server application back to the login page when the auth expires

Peterjc 86 Reputation points
2024-09-04T00:39:18.24+00:00

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

Blazor
Blazor
A free and open-source web framework that enables developers to create web apps using C# and HTML being developed by Microsoft.
1,555 questions
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.