I have the similar code working in previous version like .NET 7, but I get "HTTP ERROR 401" error, when I add @attribute [Authorize] to the page.
Here is the sample project in GIT hub.
https://github.com/sbakula/BlazorServerCustomAuthTest
My Program.cs code;
using BlazorServerCustomAuthTest.Auth;
using BlazorServerCustomAuthTest.Components;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = "XXXX",
ValidIssuer = "XXXX",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("XXXX"))
};
});
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddScoped<AuthStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider, AuthStateProvider>(
provider => provider.GetRequiredService<AuthStateProvider>()
);
builder.Services.AddScoped<ILoginService, AuthStateProvider>(
provider => provider.GetRequiredService<AuthStateProvider>()
);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseAntiforgery();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
Routes.razor code:
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)">
<NotAuthorized>
<text>Custom not authorized...</text>
</NotAuthorized>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>
My AuthStateProvider.cs:
using Microsoft.AspNetCore.Components.Authorization;
using System.Net.Http.Headers;
using System.Security.Claims;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using BlazorServerCustomAuthTest.Models;
namespace BlazorServerCustomAuthTest.Auth
{
public class AuthStateProvider : AuthenticationStateProvider, ILoginService
{
private ProtectedSessionStorage ProtectedSessionStore;
static AuthenticationState Anonymous =>
new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
public AuthStateProvider(ProtectedSessionStorage ProtectedSessionStore)
{
this.ProtectedSessionStore = ProtectedSessionStore;
}
public async override Task<AuthenticationState> GetAuthenticationStateAsync()
{
UserProfile? user;
var result = await ProtectedSessionStore.GetAsync<UserProfile>("UserProfile");
if (result.Success)
{
user = result.Value;
return await Task.FromResult(BuildAuthenticationState(user));
}
else
{
return Anonymous;
}
}
public async Task Login(UserProfile user)
{
await ProtectedSessionStore.SetAsync("UserProfile", user);
var authState = BuildAuthenticationState(user);
NotifyAuthenticationStateChanged(Task.FromResult(authState));
}
public async Task Logout()
{
await ProtectedSessionStore.DeleteAsync("UserProfile");
NotifyAuthenticationStateChanged(Task.FromResult(Anonymous));
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
}
static AuthenticationState BuildAuthenticationState(UserProfile? userProfile)
{
if (userProfile is null)
{
return Anonymous;
}
else
{
var claims = new List<Claim> { };
claims.Add(new Claim(ClaimTypes.Name, userProfile.UserName));
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(claims, "jwt")));
}
}
}
}
ILoginService.cs:
using BlazorServerCustomAuthTest.Models;
namespace BlazorServerCustomAuthTest.Auth
{
public interface ILoginService
{
Task Login(UserProfile user);
Task Logout();
}
}
_Imports.razor:
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using BlazorServerCustomAuthTest
@using BlazorServerCustomAuthTest.Components
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
App.razor:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="BlazorServerCustomAuthTest.styles.css" />
<HeadOutlet @rendermode="new InteractiveServerRenderMode( prerender: false )" />
</head>
<body>
<Routes @rendermode="new InteractiveServerRenderMode( prerender: false )" />
<script src="_framework/blazor.web.js"></script>
</body>
</html>
