♻️ No idea, but errors all gone

This commit is contained in:
2025-07-08 23:55:31 +08:00
parent 2c67472894
commit 63b2b989ba
74 changed files with 1551 additions and 1100 deletions

View File

@@ -1,10 +1,10 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using DysonNetwork.Pass.Auth;
using System.ComponentModel.DataAnnotations;
using DysonNetwork.Shared.Models;
using DysonNetwork.Pass.Auth;
using DysonNetwork.Sphere.Developer;
using DysonNetwork.Pass.Auth.OidcProvider.Responses;
namespace DysonNetwork.Sphere.Pages.Auth;

View File

@@ -11,13 +11,13 @@ using Microsoft.EntityFrameworkCore;
namespace DysonNetwork.Sphere.Pages.Auth
{
public class LoginModel(
AppDatabase db,
DysonNetwork.Shared.Services.IAccountService accounts,
DysonNetwork.Pass.Auth.AuthService auth,
GeoIpService geo,
DysonNetwork.Shared.Services.IActionLogService als
) : PageModel
{
[BindProperty] [Required] public string Username { get; set; } = string.Empty;
[BindProperty]
@@ -52,13 +52,7 @@ namespace DysonNetwork.Sphere.Pages.Auth
var userAgent = HttpContext.Request.Headers.UserAgent.ToString();
var now = Instant.FromDateTimeUtc(DateTime.UtcNow);
var existingChallenge = await db.AuthChallenges
.Where(e => e.Account == account)
.Where(e => e.IpAddress == ipAddress)
.Where(e => e.UserAgent == userAgent)
.Where(e => e.StepRemain > 0)
.Where(e => e.ExpiredAt != null && now < e.ExpiredAt)
.FirstOrDefaultAsync();
var existingChallenge = await accounts.GetAuthChallenge(account.Id, ipAddress, userAgent, now);
if (existingChallenge is not null)
{
@@ -79,8 +73,7 @@ namespace DysonNetwork.Sphere.Pages.Auth
AccountId = account.Id
}.Normalize();
await db.AuthChallenges.AddAsync(challenge);
await db.SaveChangesAsync();
await accounts.CreateAuthChallenge(challenge);
// If we have a return URL, pass it to the verify page
if (TempData.TryGetValue("ReturnUrl", out var returnUrl) && returnUrl is string url)

View File

@@ -8,10 +8,8 @@ using DysonNetwork.Pass.Account;
namespace DysonNetwork.Sphere.Pages.Auth;
public class SelectFactorModel(
AppDatabase db,
AccountService accounts
)
: PageModel
DysonNetwork.Shared.Services.IAccountService accounts
) : PageModel
{
[BindProperty(SupportsGet = true)] public Guid Id { get; set; }
[BindProperty(SupportsGet = true)] public string? ReturnUrl { get; set; }
@@ -31,13 +29,11 @@ public class SelectFactorModel(
public async Task<IActionResult> OnPostSelectFactorAsync()
{
var challenge = await db.AuthChallenges
.Include(e => e.Account)
.FirstOrDefaultAsync(e => e.Id == Id);
var challenge = await accounts.GetAuthChallenge(Id);
if (challenge == null) return NotFound();
var factor = await db.AccountAuthFactors.FindAsync(SelectedFactorId);
var factor = await accounts.GetAccountAuthFactor(SelectedFactorId, challenge.Account.Id);
if (factor?.EnabledAt == null || factor.Trustworthy <= 0)
return BadRequest("Invalid authentication method.");
@@ -81,16 +77,11 @@ public class SelectFactorModel(
private async Task LoadChallengeAndFactors()
{
AuthChallenge = await db.AuthChallenges
.Include(e => e.Account)
.FirstOrDefaultAsync(e => e.Id == Id);
AuthChallenge = await accounts.GetAuthChallenge(Id);
if (AuthChallenge != null)
{
AuthFactors = await db.AccountAuthFactors
.Where(e => e.AccountId == AuthChallenge.Account.Id)
.Where(e => e.EnabledAt != null && e.Trustworthy >= 1)
.ToListAsync();
AuthFactors = await accounts.GetAccountAuthFactors(AuthChallenge.Account.Id);
}
}

View File

@@ -1,6 +1,5 @@
@page "/web/auth/challenge/{id:guid}/verify/{factorId:guid}"
@using DysonNetwork.Shared.Models
@using DysonNetwork.Shared.Models
@model DysonNetwork.Sphere.Pages.Auth.VerifyFactorModel
@{
ViewData["Title"] = "Verify Your Identity";

View File

@@ -4,20 +4,17 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using DysonNetwork.Shared.Services;
using DysonNetwork.Shared.Services;
using NodaTime;
namespace DysonNetwork.Sphere.Pages.Auth
{
public class VerifyFactorModel(
AppDatabase db,
DysonNetwork.Shared.Services.IAccountService accountService,
IAccountService accountService,
DysonNetwork.Pass.Auth.AuthService authService,
DysonNetwork.Shared.Services.IActionLogService actionLogService,
IConfiguration configuration,
IHttpClientFactory httpClientFactory
)
: PageModel
IActionLogService actionLogService,
IConfiguration configuration
) : PageModel
{
[BindProperty(SupportsGet = true)] public Guid Id { get; set; }
@@ -55,30 +52,36 @@ namespace DysonNetwork.Sphere.Pages.Auth
try
{
if (await accounts.VerifyFactorCode(Factor, Code))
if (await accountService.VerifyFactorCode(Factor, Code))
{
AuthChallenge.StepRemain -= Factor.Trustworthy;
AuthChallenge.StepRemain = Math.Max(0, AuthChallenge.StepRemain);
AuthChallenge.BlacklistFactors.Add(Factor.Id);
db.Update(AuthChallenge);
als.CreateActionLogFromRequest(ActionLogType.ChallengeSuccess,
await actionLogService.CreateActionLogFromRequest(ActionLogType.ChallengeSuccess,
new Dictionary<string, object>
{
{ "challenge_id", AuthChallenge.Id },
{ "factor_id", Factor?.Id.ToString() ?? string.Empty }
}, Request, AuthChallenge.Account);
},
Request.HttpContext.Connection.RemoteIpAddress?.ToString(),
Request.Headers.UserAgent.ToString(),
AuthChallenge.Account
);
await db.SaveChangesAsync();
if (AuthChallenge.StepRemain == 0)
{
als.CreateActionLogFromRequest(ActionLogType.NewLogin,
await actionLogService.CreateActionLogFromRequest(ActionLogType.NewLogin,
new Dictionary<string, object>
{
{ "challenge_id", AuthChallenge.Id },
{ "account_id", AuthChallenge.AccountId }
}, Request, AuthChallenge.Account);
},
Request.HttpContext.Connection.RemoteIpAddress?.ToString(),
Request.Headers.UserAgent.ToString(),
AuthChallenge.Account
);
return await ExchangeTokenAndRedirect();
}
@@ -98,16 +101,18 @@ namespace DysonNetwork.Sphere.Pages.Auth
{
if (AuthChallenge != null)
{
AuthChallenge.FailedAttempts++;
db.Update(AuthChallenge);
await db.SaveChangesAsync();
als.CreateActionLogFromRequest(ActionLogType.ChallengeFailure,
await actionLogService.CreateActionLogFromRequest(ActionLogType.ChallengeFailure,
new Dictionary<string, object>
{
{ "challenge_id", AuthChallenge.Id },
{ "factor_id", Factor?.Id.ToString() ?? string.Empty }
}, Request, AuthChallenge.Account);
},
Request.HttpContext.Connection.RemoteIpAddress?.ToString(),
Request.Headers.UserAgent.ToString(),
AuthChallenge.Account
);
}
@@ -118,47 +123,30 @@ namespace DysonNetwork.Sphere.Pages.Auth
private async Task LoadChallengeAndFactor()
{
AuthChallenge = await db.AuthChallenges
.Include(e => e.Account)
.FirstOrDefaultAsync(e => e.Id == Id);
AuthChallenge = await accountService.GetAuthChallenge(Id);
if (AuthChallenge?.Account != null)
{
Factor = await db.AccountAuthFactors
.FirstOrDefaultAsync(e => e.Id == FactorId &&
e.AccountId == AuthChallenge.Account.Id &&
e.EnabledAt != null &&
e.Trustworthy > 0);
Factor = await accountService.GetAccountAuthFactor(FactorId, AuthChallenge.Account.Id);
}
}
private async Task<IActionResult> ExchangeTokenAndRedirect()
{
var challenge = await db.AuthChallenges
.Include(e => e.Account)
.FirstOrDefaultAsync(e => e.Id == Id);
var challenge = await accountService.GetAuthChallenge(Id);
if (challenge == null) return BadRequest("Authorization code not found or expired.");
if (challenge.StepRemain != 0) return BadRequest("Challenge not yet completed.");
var session = await db.AuthSessions
.FirstOrDefaultAsync(e => e.ChallengeId == challenge.Id);
var session = await accountService.CreateSession(
Instant.FromDateTimeUtc(DateTime.UtcNow),
Instant.FromDateTimeUtc(DateTime.UtcNow.AddDays(30)),
challenge.Account,
challenge
);
if (session == null)
{
session = new Session
{
LastGrantedAt = Instant.FromDateTimeUtc(DateTime.UtcNow),
ExpiredAt = Instant.FromDateTimeUtc(DateTime.UtcNow.AddDays(30)),
Account = challenge.Account,
Challenge = challenge,
};
db.AuthSessions.Add(session);
await db.SaveChangesAsync();
}
var token = auth.CreateToken(session);
Response.Cookies.Append(AuthConstants.CookieTokenName, token, new CookieOptions
var token = authService.CreateToken(session);
Response.Cookies.Append(accountService.GetAuthCookieTokenName(), token, new CookieOptions
{
HttpOnly = true,
Secure = !configuration.GetValue<bool>("Debug"),