dotnet / dotnet/aspnetcore

Enhance documentation for `RevalidatingServerAuthenticationStateProvider` in Blazor Apps

Open
#65,199 2 comments 0 reactions 1 assignee Claimed by @javiercn View on GitHub
area-blazor
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 5h
Merged PRs (30d)
276

Description

I know that you guys planning on improving the authentification docs for Blazor and I think I've found another bit which needs improvement.

I want to check if my current validation state is still valid and I came accross this docs here:

https://learn.microsoft.com/en-us/aspnet/core/blazor/security/?view=aspnetcore-10.0&tabs=visual-studio#additional-security-abstractions

So I implemented my custom `AuthenticationStateProvider` and inherited from `RevalidatingServerAuthenticationStateProvider` because I'm using a Blazor Web App with global Server interactivity. Here is my current implementation:

```cs
public sealed class HSOnlineAuthenticationStateProvider : RevalidatingServerAuthenticationStateProvider
{
private readonly IDbConnectionFactory _dbFactory;
private readonly UserRepository _userRepository;

public HSOnlineAuthenticationStateProvider(IDbConnectionFactory dbFactory, UserRepository userRepository, ILoggerFactory loggerFactory) : base(loggerFactory)
{
_dbFactory = dbFactory;
_userRepository = userRepository;
}

protected override TimeSpan RevalidationInterval => TimeSpan.FromSeconds(5);

protected override async Task ValidateAuthenticationStateAsync(AuthenticationState authenticationState, CancellationToken cancellationToken)
{
Console.WriteLine("Authentification validation");

Claim? claim = authenticationState.User.FindFirst("userId");

if (claim is null)
{
// TODO: LOGOUT
return false;
}

var userId = Convert.ToInt32(claim.Value, CultureInfo.InvariantCulture);
using var connection = await _dbFactory.CreateConnectionAsync(cancellationToken);
User? result = await _userRepository.GetAsync(userId, connection, cancellationToken: cancellationToken);

if (result is null)
{
// User has been deleted from the database
// TODO: LOGOUT
return false;
}

if (!result.IsActive)
{
// User has been deactivated by an admin
// TODO: LOGOUT
return false;
}

return true;
}

// TODO: CHECK STATE ON FIRST PAGE LOAD IN ORDER TO NOT HAVE TO WAIT FOR RevalidationInterval

}
```

I login now into my app and set my user to disabled via SQL. After 10 seconds I can indeed see that I'm being redirected to the Account/Login page but when I go back to my auth required page then I see that I'm still logged in and after 10 seconds I get redirected again.

I'm using a cookie based authentifcation like this:
```cs
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme);

builder.Services.AddScoped();
builder.Services.AddCascadingAuthenticationState();

/* .... */
app.UseAuthentication();
app.UseAuthorization();

/* ... */
var accountGroup = app.MapGroup("/Account");

accountGroup.MapGet("/Logout", async (HttpContext context) =>
{
await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Results.LocalRedirect("/Account/Login");
});
```

My main question know is how can I handle the `TODOs` from my `HSOnlineAuthenticationStateProvider`. Should the Logout be handled by the login page or is there any way todo this via the `HSOnlineAuthenticationStateProvider`. And how can I check if the authentification is still valid when the user first loads the page so I don't have to wait for my TimeSpan to kick in?

And lastly if it's valid how can I reseed the claims in case something has been changed within my user e.g. the roles without the need to logout and log in again?

cc: @guardrex https://github.com/dotnet/AspNetCore.Docs/issues/36662

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.