♻️ Rebuilt own auth infra

This commit is contained in:
2025-05-16 23:38:33 +08:00
parent aabe8269f5
commit 88977ccda3
10 changed files with 309 additions and 126 deletions

View File

@ -0,0 +1,77 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
namespace DysonNetwork.Sphere.Auth;
public static class AuthConstants
{
public const string SchemeName = "DysonToken";
public const string TokenQueryParamName = "tk";
}
public class DysonTokenAuthOptions : AuthenticationSchemeOptions;
public class DysonTokenAuthHandler : AuthenticationHandler<DysonTokenAuthOptions>
{
private TokenValidationParameters _tokenValidationParameters;
public DysonTokenAuthHandler(
IOptionsMonitor<DysonTokenAuthOptions> options,
IConfiguration configuration,
ILoggerFactory logger,
UrlEncoder encoder)
: base(options, logger, encoder)
{
var publicKey = File.ReadAllText(configuration["Jwt:PublicKeyPath"]!);
var rsa = RSA.Create();
rsa.ImportFromPem(publicKey);
_tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "solar-network",
IssuerSigningKey = new RsaSecurityKey(rsa)
};
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
var token = _ExtractToken(Request);
if (string.IsNullOrEmpty(token))
return AuthenticateResult.Fail("No token was provided.");
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var principal = tokenHandler.ValidateToken(token, _tokenValidationParameters, out var validatedToken);
var ticket = new AuthenticationTicket(principal, AuthConstants.SchemeName);
return AuthenticateResult.Success(ticket);
}
catch (Exception ex)
{
return AuthenticateResult.Fail($"Authentication failed: {ex.Message}");
}
}
private static string? _ExtractToken(HttpRequest request)
{
if (request.Query.TryGetValue(AuthConstants.TokenQueryParamName, out var queryToken))
return queryToken;
var authHeader = request.Headers.Authorization.ToString();
if (!string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return authHeader["Bearer ".Length..].Trim();
return request.Cookies.TryGetValue(AuthConstants.TokenQueryParamName, out var cookieToken)
? cookieToken
: null;
}
}

View File

@ -66,11 +66,11 @@ public class AuthController(
await db.AuthChallenges.AddAsync(challenge);
await db.SaveChangesAsync();
als.CreateActionLogFromRequest(ActionLogType.ChallengeAttempt,
new Dictionary<string, object> { { "challenge_id", challenge.Id } }, Request
new Dictionary<string, object> { { "challenge_id", challenge.Id } }, Request, account
);
return challenge;
}
@ -115,7 +115,7 @@ public class AuthController(
[FromBody] PerformChallengeRequest request
)
{
var challenge = await db.AuthChallenges.FindAsync(id);
var challenge = await db.AuthChallenges.Include(e => e.Account).FirstOrDefaultAsync(e => e.Id == id);
if (challenge is null) return NotFound("Auth challenge was not found.");
var factor = await db.AccountAuthFactors.FindAsync(request.FactorId);
@ -133,10 +133,11 @@ public class AuthController(
challenge.BlacklistFactors.Add(factor.Id);
db.Update(challenge);
als.CreateActionLogFromRequest(ActionLogType.ChallengeSuccess,
new Dictionary<string, object> {
new Dictionary<string, object>
{
{ "challenge_id", challenge.Id },
{ "factor_id", factor.Id }
}, Request
}, Request, challenge.Account
);
}
else
@ -149,10 +150,11 @@ public class AuthController(
challenge.FailedAttempts++;
db.Update(challenge);
als.CreateActionLogFromRequest(ActionLogType.ChallengeFailure,
new Dictionary<string, object> {
new Dictionary<string, object>
{
{ "challenge_id", challenge.Id },
{ "factor_id", factor.Id }
}, Request
}, Request, challenge.Account
);
await db.SaveChangesAsync();
return BadRequest("Invalid password.");
@ -161,10 +163,11 @@ public class AuthController(
if (challenge.StepRemain == 0)
{
als.CreateActionLogFromRequest(ActionLogType.NewLogin,
new Dictionary<string, object> {
new Dictionary<string, object>
{
{ "challenge_id", challenge.Id },
{ "account_id", challenge.AccountId }
}, Request
}, Request, challenge.Account
);
}