I have a setup a full example project here with a kludge where I have to manually call it to grab the state on every page. The problem is if the user refreshes the page then it will redirect to login because the Authorize will hit before the OnAfterRenderAsync
https://github.com/GregFinzer/Blazor8Auth
I want to use what is built into Blazor 8 but I am not able to figure out the correct configuration. Here is what I have in my program.cs
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
//Authentication
builder.Services.AddAuthorization();
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.Name = "auth_token";
options.LoginPath = "/login";
options.Cookie.MaxAge = TimeSpan.FromHours(24);
options.AccessDeniedPath = "/acessDenied";
});
builder.Services.AddScoped<AuthService>();
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddBlazoredSessionStorage();
builder.Services.AddScoped<ICustomSessionService, CustomSessionService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
Here is my AuthService that I am calling on every page
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
namespace Blazor8Auth.Services
{
public class AuthService
{
const string AuthTokenName = "auth_token";
public event Action<ClaimsPrincipal>? UserChanged;
private ClaimsPrincipal? currentUser;
private readonly ICustomSessionService _sessionService;
private readonly IConfiguration _configuration;
public AuthService(ICustomSessionService sessionService, IConfiguration configuration)
{
_sessionService = sessionService;
_configuration = configuration;
}
public ClaimsPrincipal CurrentUser
{
get { return currentUser ?? new(); }
set
{
currentUser = value;
if (UserChanged is not null)
{
UserChanged(currentUser);
}
}
}
public bool IsLoggedIn => CurrentUser.Identity?.IsAuthenticated ?? false;
public async Task LogoutAsync()
{
CurrentUser = new();
string authToken = await _sessionService.GetItemAsStringAsync(AuthTokenName);
if (!string.IsNullOrEmpty(authToken))
{
await _sessionService.RemoveItemAsync(AuthTokenName);
}
}
public async Task GetStateFromTokenAsync()
{
string authToken = await _sessionService.GetItemAsStringAsync(AuthTokenName);
var identity = new ClaimsIdentity();
if (!string.IsNullOrEmpty(authToken))
{
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var key = System.Text.Encoding.UTF8.GetBytes(_configuration.GetSection("AppSettings:Token").Value);
tokenHandler.ValidateToken(authToken, new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
ClockSkew = TimeSpan.Zero
}, out SecurityToken validatedToken);
var jwtToken = (JwtSecurityToken)validatedToken;
identity = new ClaimsIdentity(jwtToken.Claims, "jwt");
}
catch
{
await _sessionService.RemoveItemAsync(AuthTokenName);
identity = new ClaimsIdentity();
}
}
var user = new ClaimsPrincipal(identity);
CurrentUser = user;
}
public async Task Login(ClaimsPrincipal user)
{
CurrentUser = user;
var tokenEncryptionKey = _configuration.GetSection("AppSettings:Token").Value;
var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8
.GetBytes(tokenEncryptionKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);
var tokenHoursString = _configuration.GetSection("AppSettings:TokenHours").Value;
int.TryParse(tokenHoursString, out int tokenHours);
var token = new JwtSecurityToken(
claims: user.Claims,
expires: DateTime.Now.AddHours(tokenHours),
signingCredentials: creds);
var jwt = new JwtSecurityTokenHandler().WriteToken(token);
await _sessionService.SetItemAsStringAsync(AuthTokenName, jwt);
}
}
}
Here is the code that has to be on every page:
@code {
[Inject] private AuthService AuthService { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (!AuthService.IsLoggedIn)
{
await AuthService.GetStateFromTokenAsync();
}
}
}