Blazor Radzen .NET 8 Identity Automatically Logout Timer
Blazor Radzen .NET 8 Identity Automatically Logout Timer
Blazor: Blazor
is a web framework for building interactive client-side web applications using C# instead of JavaScript. It allows developers to write code that runs on the client-side and interacts with the server-side.
Radzen: Radzen
is a low-code development platform that provides tools and components for building web applications. It simplifies the development process by generating code and providing a visual interface for designing UI components.
.NET 8: .NET
is a free, open-source, cross-platform framework for building modern applications. .NET 8 is the latest version of the framework, offering improved performance, new features, and enhanced security.
.NET 8 Identity: .NET Identity
is a membership system that allows developers to add authentication and authorization to their applications. It provides features like user registration, login, and role-based access control.
Program.cs
It sets up authentication
, user management
, password policies
, lockout settings
, and cookie configurations
. Additionally, it registers services required for Identity
management and access to the HTTP context.
Program.cs
...
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<IdentityUserAccessor>();
builder.Services.AddScoped<IdentityRedirectManager>();
builder.Services.AddScoped<AuthenticationStateProvider, IdentityRevalidatingAuthenticationStateProvider>();
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddIdentityCookies();
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseInMemoryDatabase("ConnectionInMemory"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddIdentityCore<ApplicationUser>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders();
builder.Services.Configure<IdentityOptions>(options =>
{
// Default Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// Default Password settings.
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 6;
options.Password.RequiredUniqueChars = 0;
// Default SignIn settings.
options.SignIn.RequireConfirmedEmail = false;
options.SignIn.RequireConfirmedPhoneNumber = false;
// Default User settings.
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
options.User.RequireUniqueEmail = false;
});
builder.Services.ConfigureApplicationCookie(options =>
{
//options.AccessDeniedPath = "/Identity/Account/AccessDenied";
options.Cookie.Name = COOKIE_NAME;
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = EXPIRE_TIMESPAN;
//options.LoginPath = "/Identity/Account/Login";
options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
options.SlidingExpiration = true;
});
builder.Services.Configure<PasswordHasherOptions>(option =>
{
option.IterationCount = 12000;
});
builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();
builder.Services.AddHttpContextAccessor();
builder.Services.AddTransient<AuthCheckService>();
...
AuthCheckService.cs
AuthCheckService
that is responsible for checking the expiration time of the user's authentication
in a Blazor Radzen application using .NET 8 Identity
. The service
retrieves the expiration time of the user's authentication token
and calculates
the remaining time until logout.
AuthCheckService.cs
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Options;
namespace BlazorAppRadzenAuthIdentityAutoLogoutTimer.Services;
public class AuthCheckService
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly CookieAuthenticationOptions _options;
public AuthCheckService(IHttpContextAccessor httpContextAccessor, IOptions<CookieAuthenticationOptions> options)
{
_httpContextAccessor = httpContextAccessor;
_options = options.Value;
}
public Task<TimeSpan?> GetAuthExpiration()
{
TimeSpan? timeSpan = null;
string? cookie = _httpContextAccessor?.HttpContext?.Request.Cookies[Program.COOKIE_NAME];
if (cookie is null)
return Task.FromResult(timeSpan);
IDataProtectionProvider? provider = _options.DataProtectionProvider;
if (provider is null)
return Task.FromResult(timeSpan);
IDataProtector protector = provider.CreateProtector(
"Microsoft.AspNetCore.Authentication.Cookies." +
"CookieAuthenticationMiddleware",
"Identity.Application",
"v2");
TicketDataFormat format = new TicketDataFormat(protector);
AuthenticationTicket? authTicket = format.Unprotect(cookie);
if (authTicket is null)
return Task.FromResult(timeSpan);
AuthenticationProperties property = authTicket.Properties;
DateTimeOffset? expiresUtc = property.ExpiresUtc;
timeSpan = expiresUtc - DateTimeOffset.UtcNow;
return Task.FromResult(timeSpan);
}
}
The AuthCheckService
class has two private fields: _httpContextAccessor
and _options
. The constructor
initializes these fields using dependency injection. The GetAuthExpiration
method retrieves the authentication cookie, decrypts it using the data protector, and calculates the remaining time until expiration.
ComponentAuthTimer.razor
ComponentAuthTimer.razor
, it is responsible for displaying the remaining authentication expiration time and automatically logging the user out when the time expires.
ComponentAuthTimer.razor
@using BlazorAppRadzenAuthIdentityAutoLogoutTimer.Services
@using Microsoft.AspNetCore.Authorization
@using System.Security.Claims
@implements IDisposable
@attribute [Authorize]
@inject AuthCheckService AuthCheckService
@inject NavigationManager? NavigationManager
<AuthorizeView>
@if (NavigationManager is not null && NavigationManager.Uri.Contains("/Account") == true)
{
<span style="margin: 10px;">
Auto logout after <b><span id="logoutTime" val-seconds="@((int)authExpiration?.TotalSeconds)">@(authExpiration is not null ? authExpiration.Value.ToString(@"hh\:mm\:ss") : "")</span></b>
</span>
}
else
{
<span style="margin: 10px;">
Auto logout after <b>@(authExpiration is not null ? authExpiration.Value.ToString(@"hh\:mm\:ss") : "")</b>
</span>
}
</AuthorizeView>
@code {
TimeSpan? authExpiration;
System.Timers.Timer? authExpirationTimer;
protected override async Task OnInitializedAsync()
{
authExpiration = await AuthCheckService.GetAuthExpiration();
if (authExpirationTimer is null && NavigationManager?.Uri.Contains("/Account") != true)
{
authExpirationTimer = new System.Timers.Timer(1000);
authExpirationTimer.Elapsed += AuthExpirationTimer_Elapsed;
authExpirationTimer.Enabled = true;
}
}
private async void AuthExpirationTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
{
if (authExpiration is null || authExpirationTimer is null)
return;
if (authExpiration.Value.TotalSeconds > 0)
{
if (((int)authExpiration.Value.TotalSeconds % 60) == 0)
{
var expiration = await AuthCheckService.GetAuthExpiration();
if (expiration is not null)
authExpiration = expiration;
}
authExpiration -= TimeSpan.FromSeconds(1);
await InvokeAsync(StateHasChanged);
}
else
{
authExpirationTimer.Enabled = false;
authExpirationTimer.Elapsed -= AuthExpirationTimer_Elapsed;
NavigationManager?.Refresh(forceReload: true);
}
}
public void Dispose()
{
if (authExpirationTimer is not null)
authExpirationTimer.Elapsed -= AuthExpirationTimer_Elapsed;
authExpirationTimer?.Dispose();
}
}
The code
block starts with the declaration of two variables: authExpiration of type TimeSpan?
and authExpirationTimer of type System.Timers.Timer?
. The authExpiration
variable stores the authentication expiration time, and the authExpirationTimer
is used to update the countdown every second.
The AuthExpirationTimer_Elapsed
method is the event handler for the timer’s Elapsed event. It checks if the authentication expiration time is null
or the timer is null
and returns if either condition is true
.
If the authentication expiration time
is greater than zero, it checks if the remaining seconds are divisible by 60. If so, it retrieves the updated expiration time from the AuthCheckService
. Then, it subtracts one second from the remaining time and updates the UI using StateHasChanged.
ComponentAuthTimer.razor
component provides an automatic logout timer
functionality in a Blazor application using Radzen and .NET 8 Identity. It displays a countdown timer indicating the time remaining before the user is automatically logged out due to inactivity.
Source
Full source code is available at this repository in GitHub:
https://github.com/akifmt/DotNetCoding/tree/main/src/BlazorAppRadzenAuthIdentityAutoLogoutTimer
comments powered by Disqus