Found it!
Add this to the server project's Program.cs:
builder.Services.AddRazorComponents()
.AddInteractiveWebAssemblyComponents()
.AddAuthenticationStateSerialization(
options => options.SerializeAllClaims = true);
Add this to the client project's Program.cs:
builder.Services.AddAuthorizationCore();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddAuthenticationStateDeserialization();
Wrap the Router component (in Routes.razor) in a CascadingAuthenticationState component. Looks like this:
@using Microsoft.AspNetCore.Components.Authorization
<CascadingAuthenticationState>
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)"/>
<FocusOnNavigate RouteData="routeData" Selector="h1"/>
</Found>
</Router>
</CascadingAuthenticationState>
Test page:
@page "/test"
@using Microsoft.AspNetCore.Components.Authorization
@inject AuthenticationStateProvider AuthenticationStateProvider
<h3>User Claims</h3>
@if (userName is null)
{
<p>Loading...</p>
}
else
{
<p>Hello, @userName!</p>
<ul>
@foreach (var claim in userClaims)
{
<li>@claim.Type: @claim.Value</li>
}
</ul>
}
@code {
private string? userName;
private IEnumerable<System.Security.Claims.Claim> userClaims = Enumerable.Empty<System.Security.Claims.Claim>();
protected override async Task OnInitializedAsync()
{
// Get the current authentication state
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
if (user.Identity is not null && user.Identity.IsAuthenticated)
{
userName = user.Identity.Name;
userClaims = user.Claims;
}
else
{
userName = null;
userClaims = Enumerable.Empty<System.Security.Claims.Claim>();
}
}
}