diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Areas/Identity/RevalidatingAuthenticationStateProvider.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Areas/Identity/RevalidatingAuthenticationStateProvider.cs
new file mode 100644
index 0000000000..43932f618b
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Areas/Identity/RevalidatingAuthenticationStateProvider.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Security.Claims;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace RazorComponentsWeb_CSharp.Areas.Identity
+{
+ ///
+ /// An service that revalidates the
+ /// authentication state at regular intervals. If a signed-in user's security
+ /// stamp changes, this revalidation mechanism will sign the user out.
+ ///
+ /// The type encapsulating a user.
+ public class RevalidatingAuthenticationStateProvider
+ : AuthenticationStateProvider, IDisposable where TUser : class
+ {
+ private readonly static TimeSpan RevalidationInterval = TimeSpan.FromMinutes(30);
+
+ private readonly CancellationTokenSource _loopCancellationTokenSource = new CancellationTokenSource();
+ private readonly IServiceScopeFactory _scopeFactory;
+ private readonly ILogger _logger;
+ private Task _currentAuthenticationStateTask;
+
+ public RevalidatingAuthenticationStateProvider(
+ IServiceScopeFactory scopeFactory,
+ SignInManager circuitScopeSignInManager,
+ ILogger> logger)
+ {
+ var initialUser = circuitScopeSignInManager.Context.User;
+ _currentAuthenticationStateTask = Task.FromResult(new AuthenticationState(initialUser));
+ _scopeFactory = scopeFactory;
+ _logger = logger;
+
+ if (initialUser.Identity.IsAuthenticated)
+ {
+ _ = RevalidationLoop();
+ }
+ }
+
+ public override Task GetAuthenticationStateAsync()
+ => _currentAuthenticationStateTask;
+
+ private async Task RevalidationLoop()
+ {
+ var cancellationToken = _loopCancellationTokenSource.Token;
+
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ try
+ {
+ await Task.Delay(RevalidationInterval, cancellationToken);
+ }
+ catch (TaskCanceledException)
+ {
+ break;
+ }
+
+ var isValid = await CheckIfAuthenticationStateIsValidAsync();
+ if (!isValid)
+ {
+ // Force sign-out. Also stop the revalidation loop, because the user can
+ // only sign back in by starting a new connection.
+ var anonymousUser = new ClaimsPrincipal();
+ _currentAuthenticationStateTask = Task.FromResult(new AuthenticationState(anonymousUser));
+ NotifyAuthenticationStateChanged(_currentAuthenticationStateTask);
+ _loopCancellationTokenSource.Cancel();
+ }
+ }
+ }
+
+ private async Task CheckIfAuthenticationStateIsValidAsync()
+ {
+ try
+ {
+ // Get the sign-in manager from a new scope to ensure it fetches fresh data
+ using (var scope = _scopeFactory.CreateScope())
+ {
+ var signInManager = scope.ServiceProvider.GetRequiredService>();
+ var authenticationState = await _currentAuthenticationStateTask;
+ var validatedUser = await signInManager.ValidateSecurityStampAsync(authenticationState.User);
+ return validatedUser != null;
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "An error occurred while revalidating authentication state");
+ return false;
+ }
+ }
+
+ void IDisposable.Dispose()
+ => _loopCancellationTokenSource.Cancel();
+ }
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Startup.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Startup.cs
index ff517870d6..306308249e 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Startup.cs
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorComponentsWeb-CSharp/Startup.cs
@@ -36,6 +36,9 @@ using Microsoft.Extensions.Hosting;
#if(MultiOrgAuth)
using Microsoft.IdentityModel.Tokens;
#endif
+#if (IndividualLocalAuth)
+using RazorComponentsWeb_CSharp.Areas.Identity;
+#endif
using RazorComponentsWeb_CSharp.Data;
namespace RazorComponentsWeb_CSharp
@@ -64,6 +67,8 @@ namespace RazorComponentsWeb_CSharp
#endif
services.AddDefaultIdentity()
.AddEntityFrameworkStores();
+
+ services.AddScoped>();
#elif (OrganizationalAuth)
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
diff --git a/src/ProjectTemplates/test/template-baselines.json b/src/ProjectTemplates/test/template-baselines.json
index 5d610cc2f5..b7466194dd 100644
--- a/src/ProjectTemplates/test/template-baselines.json
+++ b/src/ProjectTemplates/test/template-baselines.json
@@ -894,6 +894,7 @@
"_Imports.razor",
"Areas/Identity/Pages/Account/LogOut.cshtml",
"Areas/Identity/Pages/Shared/_LoginPartial.cshtml",
+ "Areas/Identity/RevalidatingAuthenticationStateProvider.cs",
"Data/ApplicationDbContext.cs",
"Data/WeatherForecast.cs",
"Data/WeatherForecastService.cs",