Refactor authentication system to use MongoDB
Some checks failed
Publish Container / publish (push) Has been cancelled

- Removed Entity Framework Core identity schema and related migrations.
- Introduced MongoDB-based authentication service with user seeding functionality.
- Updated Program.cs to configure authentication and authorization using cookies.
- Created new Login.razor component for user login interface.
- Added RedirectToLogin component for handling unauthorized access.
- Updated Dockerfile and docker-compose files for development and production environments.
- Removed SQLite connection strings and related configurations.
- Added MongoDB connection settings in appsettings.json and Docker configurations.
- Implemented IMongoAuthService interface and MongoAuthService class for user management.
- Created MongoAuthUser model for MongoDB user representation.
This commit is contained in:
MaddoScientisto 2026-03-16 21:54:44 +01:00
commit 7029e374cc
64 changed files with 338 additions and 3556 deletions

View file

@ -1,14 +1,13 @@
using System.Globalization;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Localization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using WorkTracker.Components;
using WorkTracker.Components.Account;
using WorkTracker.Configuration;
using WorkTracker.Data;
using WorkTracker.Services.Auth;
using WorkTracker.Services.Festivities;
using WorkTracker.Services.Settings;
@ -20,28 +19,18 @@ builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<IdentityUserAccessor>();
builder.Services.AddScoped<IdentityRedirectManager>();
builder.Services.AddScoped<AuthenticationStateProvider, IdentityRevalidatingAuthenticationStateProvider>();
builder.Services.AddAuthorization();
builder.Services.AddAuthentication(options =>
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.DefaultScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddIdentityCookies();
options.LoginPath = "/login";
options.LogoutPath = "/logout";
options.AccessDeniedPath = "/login";
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromDays(14);
});
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = false)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddSignInManager()
.AddDefaultTokenProviders();
builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();
builder.Services.AddLocalization();
builder.Services.Configure<MongoDbOptions>(builder.Configuration.GetSection(MongoDbOptions.SectionName));
@ -61,6 +50,7 @@ builder.Services.AddSingleton(sp =>
});
builder.Services.AddScoped<IAppSettingsService, MongoAppSettingsService>();
builder.Services.AddSingleton<IMongoAuthService, MongoAuthService>();
builder.Services.AddSingleton<IItalianFestivitySource, ItalianFestivitySource>();
builder.Services.AddHostedService<SingleUserSeedService>();
@ -78,11 +68,7 @@ var localizationOptions = new RequestLocalizationOptions
};
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
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.
@ -97,13 +83,53 @@ if (useHttpsRedirection)
app.UseRequestLocalization(localizationOptions);
app.UseAuthentication();
app.UseAuthorization();
app.UseAntiforgery();
app.MapPost("/login", async (HttpContext context, IMongoAuthService authService) =>
{
var form = await context.Request.ReadFormAsync();
var email = form["email"].ToString();
var password = form["password"].ToString();
var returnUrl = form["returnUrl"].ToString();
if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(password))
{
return TypedResults.LocalRedirect($"/login?error=Missing%20credentials&returnUrl={Uri.EscapeDataString(returnUrl)}");
}
var user = await authService.ValidateCredentialsAsync(email, password, context.RequestAborted);
if (user is null)
{
return TypedResults.LocalRedirect($"/login?error=Invalid%20credentials&returnUrl={Uri.EscapeDataString(returnUrl)}");
}
var claims = new List<Claim>
{
new(ClaimTypes.NameIdentifier, user.Id),
new(ClaimTypes.Name, user.Email)
};
var principal = new ClaimsPrincipal(new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme));
await context.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
var safeReturnUrl = string.IsNullOrWhiteSpace(returnUrl) || !Uri.IsWellFormedUriString(returnUrl, UriKind.Relative)
? "/"
: returnUrl;
return TypedResults.LocalRedirect(safeReturnUrl);
}).DisableAntiforgery();
app.MapPost("/logout", async (HttpContext context) =>
{
await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return TypedResults.LocalRedirect("/login");
}).DisableAntiforgery();
app.MapStaticAssets();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
// Add additional endpoints required by the Identity /Account Razor components.
app.MapAdditionalIdentityEndpoints();
app.Run();