feat: implement identity authentication, authorization policies, and MAUI platform support with Docker orchestration

This commit is contained in:
2026-04-29 20:37:41 +02:00
parent 10efed0369
commit 0210611edf
55 changed files with 2359 additions and 949 deletions
+25 -5
View File
@@ -3,7 +3,6 @@ using NexusReader.Application;
using NexusReader.Infrastructure;
using NexusReader.Application.Abstractions.Services;
using NexusReader.Web.Client.Services;
using NexusReader.Web.New.Services;
using NexusReader.UI.Shared.Services;
using NexusReader.Domain.Entities;
using NexusReader.Infrastructure.Persistence;
@@ -14,6 +13,9 @@ using Microsoft.AspNetCore.Authorization;
using NexusReader.Infrastructure.Identity;
using Microsoft.AspNetCore.Authentication;
using System.Security.Claims;
using NexusReader.Infrastructure.Services;
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
var builder = WebApplication.CreateBuilder(args);
@@ -32,7 +34,7 @@ builder.Services.AddServerSideBlazor()
});
builder.Services.AddScoped<IPlatformService, WebPlatformService>();
builder.Services.AddScoped<INativeStorageService, WebStorageService>();
builder.Services.AddScoped<INativeStorageService, NexusReader.UI.Shared.Services.WebStorageService>();
builder.Services.AddScoped<IThemeService, ThemeService>();
builder.Services.AddScoped<IQuizStateService, QuizStateService>();
builder.Services.AddScoped<IFocusModeService, FocusModeService>();
@@ -63,6 +65,9 @@ builder.Services.AddAuthorization(options =>
options.AddPolicy("HasAvailableTokens", policy => policy.AddRequirements(new TokenLimitRequirement()));
});
// Billing & Stripe
builder.Services.AddScoped<IBillingService, BillingService>();
// Authentication
builder.Services.AddAuthentication(options =>
{
@@ -80,6 +85,7 @@ builder.Services.AddIdentityApiEndpoints<NexusUser>()
builder.Services.ConfigureApplicationCookie(options =>
{
options.LoginPath = "/account/login";
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromDays(30);
options.SlidingExpiration = true;
@@ -220,21 +226,35 @@ app.MapGet("/identity/callback/google", async (
return Results.Redirect("/account/login?error=ProvisioningFailed");
});
app.MapGet("/identity/profile", async (ClaimsPrincipal user, UserManager<NexusUser> userManager) =>
app.MapGet("/identity/profile", async (ClaimsPrincipal user, UserManager<NexusUser> userManager, AppDbContext dbContext) =>
{
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
if (userId == null) return Results.Unauthorized();
var nexusUser = await userManager.FindByIdAsync(userId);
var nexusUser = await dbContext.Users
.Include(u => u.Ebooks)
.Include(u => u.QuizResults)
.FirstOrDefaultAsync(u => u.Id == userId);
if (nexusUser == null) return Results.NotFound();
var avgScore = nexusUser.QuizResults.Any()
? (int)nexusUser.QuizResults.Average(q => q.Percentage)
: 0;
var lastReadBook = nexusUser.Ebooks
.OrderByDescending(e => e.LastReadDate)
.FirstOrDefault()?.Title ?? "None";
return Results.Ok(new
{
nexusUser.Email,
nexusUser.AITokenLimit,
nexusUser.AITokensUsed,
nexusUser.CurrentPlan,
nexusUser.TenantId
nexusUser.TenantId,
AverageQuizScore = avgScore,
LastReadBookTitle = lastReadBook
});
}).RequireAuthorization();