if needed i will share all the codes which is already present in the blazor application please review and help me to acheave this installed this Microsoft.AspNetCore.Components.WebAssembly.Authentication.1.app.razor:<CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState> 2.login.razor:@page "/login"
@using UserRegistration.BlazorServer.ViewModels
@using UserRegistration.BlazorServer.Data
@using UserRegistration.BlazorServer.Domain.Entities
@using Microsoft.EntityFrameworkCore
@using Microsoft.AspNetCore.Components.Authorization
@inject NavigationManager Nav
@inject AppDbContext Db
@inject Microsoft.AspNetCore.Identity.IPasswordHasher<User> Hasher
<AuthorizeView Context="authState">
<!-- ✅ USER NOT LOGGED IN -->
<NotAuthorized>
<h3>Login</h3>
@if (!string.IsNullOrEmpty(_error))
{
<div class="alert alert-danger">@_error</div>
}
<EditForm Model="_model" OnValidSubmit="HandleLoginAsync">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="mb-3">
<label>Username</label>
<InputText class="form-control" @bind-Value="_model.Username" />
<ValidationMessage For="@(() => _model.Username)" />
</div>
<div class="mb-3">
<label>Password</label>
<InputText class="form-control"
type="password"
@bind-Value="_model.Password" />
<ValidationMessage For="@(() => _model.Password)" />
</div>
<div class="form-check mb-3">
<InputCheckbox class="form-check-input"
@bind-Value="_model.RememberMe" />
<label class="form-check-label">Remember me</label>
</div>
<div class="d-flex gap-2">
<button class="btn btn-primary" type="submit">
Login
</button>
<button class="btn btn-secondary"
type="button"
@onclick="GoRegister">
Register
</button>
</div>
</EditForm>
</NotAuthorized>
<!-- ✅ USER ALREADY LOGGED IN -->
<Authorized>
<p>You are already logged in.</p>
</Authorized>
</AuthorizeView>
@code {
private LoginViewModel _model = new();
private string? _error;
private async Task HandleLoginAsync()
{
_error = null;
try
{
var username = _model.Username.Trim();
var user = await Db.Users
.FirstOrDefaultAsync(u => u.Username == username);
if (user is null)
{
_error = "Invalid username or password.";
return;
}
var verify = Hasher.VerifyHashedPassword(
user, user.PasswordHash, _model.Password);
if (verify == Microsoft.AspNetCore.Identity.PasswordVerificationResult.Failed)
{
_error = "Invalid username or password.";
return;
}
// ✅ SUCCESS → Redirect to dashboard
Nav.NavigateTo("/runbooks", forceLoad: true);
}
catch
{
_error = "Something went wrong while signing in.";
}
}
void GoRegister() => Nav.NavigateTo("/register");
}
`` 3.loginviewmodel.cs:using System.ComponentModel.DataAnnotations;
namespace UserRegistration.BlazorServer.ViewModels
{
public class LoginViewModel
{
[Required(ErrorMessage = "Username is required")]
[MaxLength(100, ErrorMessage = "Username is too long")]
public string Username { get; set; } = string.Empty;
[Required(ErrorMessage = "Password is required")]
[MinLength(6, ErrorMessage = "Password must be at least 6 characters")]
public string Password { get; set; } = string.Empty;
public bool RememberMe { get; set; } = false;
}
} 4.mainlayout.razor:@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4"
style="display:flex; align-items:center; justify-content:space-between;">
<a href="https://learn.microsoft.com/aspnet/core/blazor" target="_blank">
Docs
</a>
<!-- ✅ PROFILE MENU -->
<ProfileMenu />
</div>
<div class="content px-4">
@Body
</div>
</main>
</div> 5.runbookdashboard.razor:@page "/runbooks"
@layout DashboardLayout
@using Microsoft.EntityFrameworkCore
@using Microsoft.AspNetCore.Components.Routing
@using UserRegistration.BlazorServer.Data
@using UserRegistration.BlazorServer.Domain.Entities
@using UserRegistration.BlazorServer.Domain.DTOs
@inject AppDbContext Db
@inject NavigationManager Nav
<!-- ACTION BUTTONS (Use regular buttons for PERFECT navigation) -->
<div style="display:flex; gap:20px; margin-bottom:30px;">
<button type="button"
style="background:#FFD600; padding:10px 20px; border:none;
font-weight:600; color:black; border-radius:5px;"
@onclick="GoCreate">
+ Create Runbook
</button>
<button type="button"
style="background:#FFD600; padding:10px 20px; border:none;
font-weight:600; color:black; border-radius:5px;"
@onclick="GoWorkspace">
My Workspace
</button>
</div>
<!-- MAIN GRID -->
<div style="display:flex; gap:35px;">
<!-- LEFT: LIVE RUNBOOKS -->
<div style="flex:2; background:#111; padding:20px; border-radius:10px;">
<h3 style="color:white; margin:0 0 12px 0;">Live Runbooks</h3>
@if (_runbooks.Count == 0)
{
<div style="color:#bbb; margin-top:10px;">No runbooks scheduled for today.</div>
}
else
{
@foreach (var r in _runbooks)
{
<div style="background:#222; padding:15px; border-radius:10px; margin-top:12px;">
<div style="font-size:18px; font-weight:600;">@r.Name</div>
<div style="color:#bbb; margin:6px 0 10px;">
@(r.StartTimeUtc?.ToLocalTime().ToString("dd MMM yyyy HH:mm") ?? "-")
–
@(r.EndTimeUtc?.ToLocalTime().ToString("dd MMM yyyy HH:mm") ?? "-")
</div>
@{
// Map Active/Completed/Cancelled visually
var label = r.Status == "Active" ? "Active" : r.Status;
var pill = r.Status switch
{
"Active" => "background:#28a745; color:white;",
"Completed" => "background:#6c757d; color:white;",
"Cancelled" => "background:#dc3545; color:white;",
_ => "background:#FFD600; color:black;"
};
}
<span style="@pill padding:4px 10px; border-radius:5px; margin-right:10px;">
@label
</span>
<!-- COMPLETE BUTTON -->
<button type="button"
class="btn btn-sm btn-outline-light"
disabled="@(r.Status == "Completed" || r.Status == "Cancelled")"
@onclick="@(() => MarkCompleteAsync(r.Id))">
Complete
</button>
<!-- CANCEL BUTTON -->
@if (r.Status != "Completed" && r.Status != "Cancelled")
{
<button type="button"
class="btn btn-sm btn-outline-light"
style="margin-left:10px;"
@onclick="@(() => MarkCancelAsync(r.Id))">
Cancel
</button>
}
<!-- DELETE BUTTON -->
<button type="button"
class="btn btn-sm btn-danger"
style="margin-left:10px;"
@onclick="@(() => DeleteAsync(r.Id))">
Delete
</button>
</div>
}
}
</div>
<!-- RIGHT: RUNBOOK ACTIVITY -->
<div style="flex:1; background:#111; padding:20px; border-radius:10px;">
<h3 style="color:white;">Runbook Activity</h3>
<div style="background:#333; padding:12px; border-radius:8px; margin-top:12px;">
@_summary.InProgress IN PROGRESS
</div>
<div style="background:#333; padding:12px; border-radius:8px; margin-top:12px;">
@_summary.Completed COMPLETED
</div>
<div style="background:#333; padding:12px; border-radius:8px; margin-top:12px;">
@_summary.Overdue OVERDUE
</div>
<div style="background:#333; padding:12px; border-radius:8px; margin-top:12px;">
@_summary.Cancelled CANCELLED
</div>
</div>
</div>
@code {
private List<Runbook> _runbooks = new();
private RunbookActivitySummary _summary = new();
protected override async Task OnInitializedAsync()
{
await LoadAsync();
}
private async Task LoadAsync()
{
var now = DateTime.UtcNow;
_runbooks = await Db.Runbooks
.AsNoTracking()
.OrderByDescending(r => r.Id)
.ToListAsync();
_summary = new RunbookActivitySummary
{
InProgress = _runbooks.Count(r => r.Status == "Active"),
Completed = _runbooks.Count(r => r.Status == "Completed"),
Cancelled = _runbooks.Count(r => r.Status == "Cancelled"),
Overdue = _runbooks.Count(r =>
r.EndTimeUtc < now &&
r.Status != "Completed" &&
r.Status != "Cancelled")
};
}
private void GoCreate() => Nav.NavigateTo("/create_runbook", true);
private void GoWorkspace() => Nav.NavigateTo("/workspace", true);
private async Task MarkCompleteAsync(int id)
{
var rb = await Db.Runbooks.FindAsync(id);
if (rb is null || rb.Status == "Completed" || rb.Status == "Cancelled")
return;
rb.Status = "Completed";
await Db.SaveChangesAsync();
await LoadAsync();
}
private async Task MarkCancelAsync(int id)
{
var rb = await Db.Runbooks.FindAsync(id);
if (rb is null || rb.Status == "Completed" || rb.Status == "Cancelled")
return;
rb.Status = "Cancelled";
await Db.SaveChangesAsync();
await LoadAsync();
}
private async Task DeleteAsync(int id)
{
var rb = await Db.Runbooks.FindAsync(id);
if (rb is null) return;
Db.Runbooks.Remove(rb);
await Db.SaveChangesAsync();
await LoadAsync();
}
}
6.dashboardlayout.razor:@inherits LayoutComponentBase
<div style="background:#000; min-height:100vh; color:white;">
<!-- EY HEADER -->
<div style="display:flex; justify-content:space-between; align-items:center;
padding:15px 25px; background:#111;">
<div style="display:flex; align-items:center; gap:15px;">
<img src="ey-logo.png" style="height:30px;" />
<span style="font-size:20px; font-weight:700;">
Cutover Runbook Dashboard
</span>
</div>
<!-- ✅ AUTH‑AWARE PROFILE MENU -->
<ProfileMenu />
</div>
<!-- BODY -->
<div style="padding:20px;">
@Body
</div>
</div>
7.customauthstatprovider.cs:using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;
public class CustomAuthStateProvider : AuthenticationStateProvider
{
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
// TEMPORARY: CHANGE THIS AFTER LOGIN
// This example assumes user is logged in
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, "admin"),
new Claim(ClaimTypes.Role, "Admin")
};
// 🔴 THIS IS THE MOST IMPORTANT LINE
var identity = new ClaimsIdentity(claims, "Cookies");
var user = new ClaimsPrincipal(identity);
return Task.FromResult(new AuthenticationState(user));
}
} 8.profiemenu.razor:@using System.Security.Claims
@using Microsoft.AspNetCore.Components.Authorization
@inject NavigationManager Nav
<AuthorizeView>
<Authorized Context="auth">
@{
var user = auth.User;
var username =
user.FindFirst(ClaimTypes.Name)?.Value
?? user.Identity?.Name
?? "User";
}
<div style="display:flex; align-items:center; gap:12px;">
<!-- USERNAME -->
<span style="color:white; font-weight:600;">
@username
</span>
<!-- LOGOUT BUTTON -->
<button style="background:#FFD600; color:black;
border:none; padding:6px 12px;
border-radius:4px; font-weight:600;"
@onclick="Logout">
Logout
</button>
</div>
</Authorized>
<NotAuthorized>
<button style="background:#8a2be2; color:white;
border:none; padding:6px 12px;
border-radius:4px;"
@onclick="GoLogin">
Login
</button>
</NotAuthorized>
</AuthorizeView>
@code {
private void Logout()
{
// Force full reload so cookie is cleared
Nav.NavigateTo("/auth/signout", forceLoad: true);
}
private void GoLogin()
{
Nav.NavigateTo("/login");
}
}
`` 9.profile.razor:@page "/profile"
@using System.Security.Claims
@using Microsoft.AspNetCore.Components.Authorization
@inject AuthenticationStateProvider AuthStateProvider
<h2 style="color:white;">My Profile</h2>
<div style="background:#111; padding:20px; border-radius:10px;
max-width:400px; margin-top:20px; color:white;">
<div style="margin-bottom:12px;">
<strong>Username</strong><br />
@username
</div>
<div>
<strong>Role</strong><br />
@role
</div>
</div>
@code {
private string username = "";
private string role = "";
protected override async Task OnInitializedAsync()
{
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
username =
user.FindFirst(ClaimTypes.Name)?.Value
?? user.FindFirst(ClaimTypes.Email)?.Value
?? "User";
role =
user.FindFirst(ClaimTypes.Role)?.Value
?? "-";
}
} 10.program.cs:using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Http;
using UserRegistration.BlazorServer.Authorization;
using UserRegistration.BlazorServer.Data;
using UserRegistration.BlazorServer.Domain.Entities;
using UserRegistration.BlazorServer.Services;
using UserRegistration.BlazorServer.ViewModels;
var builder = WebApplication.CreateBuilder(args);
// ------------------------------------
// DB CONFIG
// ------------------------------------
var defaultDataDir = Path.Combine(builder.Environment.ContentRootPath, "App_Data");
Directory.CreateDirectory(defaultDataDir);
var defaultDbPath = Path.Combine(defaultDataDir, "app.db");
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? $"Data Source={defaultDbPath}";
// ------------------------------------
// SERVICES
// ------------------------------------
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlite(connectionString));
builder.Services.AddScoped<IPasswordHasher<User>, PasswordHasher<User>>();
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<RegisterViewModel>();
builder.Services.AddHttpContextAccessor();
// ✅ SAFE HttpClient for Blazor Server
builder.Services.AddScoped(sp =>
{
var ctx = sp.GetRequiredService<IHttpContextAccessor>().HttpContext;
var baseUri = $"{ctx?.Request.Scheme}://{ctx?.Request.Host}";
return new HttpClient { BaseAddress = new Uri(baseUri) };
});
// ------------------------------------
// AUTHENTICATION
// ------------------------------------
builder.Services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/login";
options.AccessDeniedPath = "/access-denied";
options.ExpireTimeSpan = TimeSpan.FromHours(8);
options.SlidingExpiration = true;
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.Lax;
options.Cookie.SecurePolicy = builder.Environment.IsDevelopment()
? CookieSecurePolicy.SameAsRequest
: CookieSecurePolicy.Always;
});
// ------------------------------------
// AUTHORIZATION (PERMISSIONS)
// ------------------------------------
builder.Services.AddAuthorizationBuilder()
.AddPolicy("CanCreateRunbook",
p => p.RequireClaim("perm", AppPermissions.CreateRunbook))
.AddPolicy("CanCreateTask",
p => p.RequireClaim("perm", AppPermissions.CreateTask))
.AddPolicy("CanTaskEdit",
p => p.RequireClaim("perm", AppPermissions.TaskEdit))
.AddPolicy("CanTaskDelay",
p => p.RequireClaim("perm", AppPermissions.TaskDelay))
.AddPolicy("CanTaskComplete",
p => p.RequireClaim("perm", AppPermissions.TaskComplete));
var app = builder.Build();
// ------------------------------------
// DB MIGRATION
// ------------------------------------
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
db.Database.Migrate();
}
// ------------------------------------
// PIPELINE
// ------------------------------------
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
// ------------------------------------
// ✅ LOGIN ENDPOINT (COOKIE ISSUED HERE)
// ------------------------------------
app.MapPost("/auth/signin", async (
IUserService userService,
LoginViewModel model) =>
{
var user = await userService.ValidateLoginAsync(
model.Username,
model.Password);
if (user == null)
return Results.BadRequest("Invalid credentials");
await userService.SignInAsync(user);
return Results.Ok();
}).AllowAnonymous();
// ------------------------------------
// LOGOUT
// ------------------------------------
app.MapPost("/auth/signout", async (IUserService userService) =>
{
await userService.SignOutAsync();
return Results.Redirect("/login");
}).AllowAnonymous();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();