# Blazor Authentication & Authorization ## Authentication Setup ### Blazor Server Setup ```csharp // Program.cs var builder = WebApplication.CreateBuilder(args); // Add authentication builder.Services .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.LoginPath = "/login"; options.LogoutPath = "/logout"; options.AccessDeniedPath = "/unauthorized"; }); builder.Services.AddAuthorizationCore(); // Add Blazor Server builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); var app = builder.Build(); // Add authentication middleware BEFORE MapRazorPages app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapRazorPages(); app.MapBlazorHub(); ``` ### Blazor WebAssembly Setup ```csharp // Program.cs var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("#app"); // Add authentication builder.Services.AddAuthorizationCore(); builder.Services.AddScoped(); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); await builder.Build().RunAsync(); // CustomAuthStateProvider public class CustomAuthStateProvider : AuthenticationStateProvider { private readonly HttpClient httpClient; public CustomAuthStateProvider(HttpClient httpClient) { this.httpClient = httpClient; } public override async Task GetAuthenticationStateAsync() { try { var user = await httpClient.GetJsonAsync("/api/user"); var claims = new[] { new Claim(ClaimTypes.NameIdentifier, user.Id), new Claim(ClaimTypes.Name, user.Name), new Claim(ClaimTypes.Email, user.Email) }; var identity = new ClaimsIdentity(claims, "Custom"); return new AuthenticationState(new ClaimsPrincipal(identity)); } catch { return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); } } } ``` ## AuthorizeView Component AuthorizeView displays content conditionally based on authorization status. ### Basic Authorization Check ```html

Hello, @context.User.Identity?.Name!

Please log in.

``` ### Authorize by Role ```html

This content is only for Admins

User or Moderator content

``` ### Authorize by Policy ```html

Only content editors can see this

``` ### Multiple AuthorizeView States ```html @if (context.User.IsInRole("Admin")) {

Admin dashboard

} else if (context.User.IsInRole("Editor")) {

Editor dashboard

} else {

User dashboard

}

Checking authorization...

Not authorized

``` ### Authorize Multiple Resources ```html

@Auth.User.Identity?.Name

@if (Auth.User.IsInRole("Admin")) { Admin Panel } @if (Auth.User.HasClaim("department", "engineering")) { Engineering }
``` ## Authorize Attribute Apply `[Authorize]` to pages to require authentication. ### Basic Page Authorization ```csharp @page "/admin" @attribute [Authorize]

Admin Page

Only authenticated users can see this.

``` ### Role-Based Authorization ```csharp @page "/admin" @attribute [Authorize(Roles = "Admin")]

Admin Panel

Only admins can access this page.

``` ### Policy-Based Authorization ```csharp @page "/dashboard" @attribute [Authorize(Policy = "RequireAdminRole")]

Dashboard

``` ### Multiple Requirements ```csharp @page "/admin" @attribute [Authorize(Roles = "Admin, Manager")] @attribute [Authorize(Policy = "ActiveSubscription")]

Admin Dashboard

``` ## Authorization Policies Define fine-grained authorization policies. ### Setup Policies ```csharp // Program.cs builder.Services.AddAuthorizationCore(options => { options.AddPolicy("RequireAdminRole", policy => policy.RequireRole("Admin")); options.AddPolicy("ActiveSubscription", policy => policy.Requirements.Add(new ActiveSubscriptionRequirement())); options.AddPolicy("ContentEditor", policy => policy.RequireClaim("department", "engineering", "content")); options.AddPolicy("AdultUser", policy => policy.Requirements.Add(new MinimumAgeRequirement(18))); }); // Add custom policy handler builder.Services.AddSingleton(); ``` ### Custom Policy Handlers ```csharp public class ActiveSubscriptionRequirement : IAuthorizationRequirement { } public class ActiveSubscriptionHandler : AuthorizationHandler { private readonly IUserService userService; public ActiveSubscriptionHandler(IUserService userService) { this.userService = userService; } protected override async Task HandleRequirementAsync( AuthorizationHandlerContext context, ActiveSubscriptionRequirement requirement) { var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (string.IsNullOrEmpty(userId)) { context.Fail(); return; } var user = await userService.GetUserAsync(userId); if (user?.SubscriptionActive == true) { context.Succeed(requirement); } else { context.Fail(); } } } public class MinimumAgeRequirement : IAuthorizationRequirement { public int MinimumAge { get; set; } public MinimumAgeRequirement(int minimumAge) { MinimumAge = minimumAge; } } ``` ## Accessing Authentication State ### In Components ```csharp @page "/user-profile" @if (authState == null) {

Loading...

} else if (authState.User.Identity?.IsAuthenticated == true) {

Welcome, @authState.User.Identity?.Name

} else {

Not authenticated

} @code { [CascadingParameter] private Task? AuthStateTask { get; set; } private AuthenticationState? authState; protected override async Task OnInitializedAsync() { authState = await AuthStateTask!; } } ``` ### Check Claims and Roles ```csharp @code { private async Task CheckUserAsync() { var authState = await AuthStateTask!; var user = authState.User; if (user.Identity?.IsAuthenticated == true) { var name = user.Identity.Name; var email = user.FindFirst(ClaimTypes.Email)?.Value; var isAdmin = user.IsInRole("Admin"); var department = user.FindFirst("department")?.Value; } } } ``` ## Login/Logout Implementation ### Login Page ```csharp @page "/login" @layout BlankLayout @code { [SupplyParameterFromQuery] public string? ReturnUrl { get; set; } @inject AuthenticationStateProvider AuthStateProvider @inject NavigationManager Navigation private LoginModel model = new(); private string? errorMessage; private async Task HandleLoginAsync() { try { var result = await AuthService.LoginAsync(model.Email, model.Password); // Update authentication state if (AuthStateProvider is CustomAuthStateProvider customAuth) { await customAuth.SetUserAsync(result.User); } // Redirect to return URL or home var url = !string.IsNullOrEmpty(ReturnUrl) ? ReturnUrl : "/"; Navigation.NavigateTo(url, forceLoad: true); } catch (Exception ex) { errorMessage = ex.Message; } } } public class LoginModel { [Required] [EmailAddress] public string Email { get; set; } = ""; [Required] public string Password { get; set; } = ""; } ``` ### Logout Endpoint ```csharp // Pages/Logout.cshtml (in Blazor Server) @page "/logout" @using Microsoft.AspNetCore.Identity @inject SignInManager SignInManager @inject NavigationManager Navigation @code { protected override async Task OnInitializedAsync() { await SignInManager.SignOutAsync(); Navigation.NavigateTo("/"); } } ``` ## Claims-Based Authorization Working with claims for fine-grained authorization. ### Add Claims to User ```csharp var claims = new List { new Claim(ClaimTypes.NameIdentifier, user.Id), new Claim(ClaimTypes.Name, user.Name), new Claim(ClaimTypes.Email, user.Email), new Claim("department", "engineering"), new Claim("level", "senior") }; var identity = new ClaimsIdentity(claims, "Custom"); var principal = new ClaimsPrincipal(identity); ``` ### Check Claims in Component ```csharp @code { private async Task CheckDepartmentAsync() { var authState = await AuthStateTask!; var user = authState.User; var department = user.FindFirst("department")?.Value; var level = user.FindFirst("level")?.Value; if (department == "engineering") { // Show engineering-specific UI } } } ``` ## Best Practices ### Use Cascading AuthenticationState ```csharp // App.razor - already cascades AuthenticationState by default ``` ### Always Check firstRender in OnAfterRender ```csharp protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { // Initialize only once authState = await AuthStateTask!; StateHasChanged(); } } ``` ### Use forceLoad for Logout ```csharp private async Task LogoutAsync() { await AuthService.LogoutAsync(); // forceLoad clears client-side state Navigation.NavigateTo("/", forceLoad: true); } ``` ### Validate on Server - Never trust client-side authorization - Always validate authorization on backend API - Check claims/roles on server methods ### Use ReturnUrl After Login ```csharp // Redirect back to originally-requested page Navigation.NavigateTo($"/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}"); ``` --- **Related Resources:** See [routing-navigation.md](routing-navigation.md) for route-based authorization. See [components-lifecycle.md](components-lifecycle.md) for parameter security.