Files
2026-06-03 20:24:57 -07:00

106 lines
3.5 KiB
C#

using System.Security.Claims;
using lxc_songlist.Components;
using lxc_songlist.Data;
using lxc_songlist.Services;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.EntityFrameworkCore;
using MudBlazor.Services;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/admin/login";
options.AccessDeniedPath = "/admin/login";
options.Cookie.Name = "lxc-songlist-admin";
options.SlidingExpiration = true;
});
builder.Services.AddAuthorization();
builder.Services.AddMudServices();
builder.Services.Configure<AdminAuthOptions>(builder.Configuration.GetSection("Admin"));
builder.Services.Configure<SiteOptions>(builder.Configuration.GetSection("Site"));
builder.Services.AddDbContextFactory<SongListDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<SongCatalogService>();
builder.Services.AddScoped<SiteSettingsService>();
builder.Services.AddSingleton<AdminPasswordService>();
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.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseAntiforgery();
app.MapStaticAssets();
app.MapPost("/admin/sign-in", async (
HttpContext httpContext,
AdminPasswordService passwordService) =>
{
var form = await httpContext.Request.ReadFormAsync();
var password = form["password"].ToString();
var returnUrl = form["returnUrl"].ToString();
if (!passwordService.Verify(password))
{
return Results.Redirect("/admin/login?error=1");
}
var claims = new[]
{
new Claim(ClaimTypes.Name, "admin"),
new Claim(ClaimTypes.Role, "Admin")
};
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);
await httpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
principal,
new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.AddDays(14)
});
return Results.Redirect(IsLocalUrl(returnUrl) ? returnUrl : "/admin");
})
.DisableAntiforgery();
app.MapGet("/admin/logout", async (HttpContext httpContext) =>
{
await httpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Results.Redirect("/");
})
.RequireAuthorization();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
static bool IsLocalUrl(string? url)
{
return !string.IsNullOrWhiteSpace(url)
&& Uri.TryCreate(url, UriKind.Relative, out _)
&& url[0] == '/'
&& (url.Length == 1 || (url[1] != '/' && url[1] != '\\'));
}