♻️ Rebuilt own auth infra
This commit is contained in:
77
DysonNetwork.Sphere/Auth/Auth.cs
Normal file
77
DysonNetwork.Sphere/Auth/Auth.cs
Normal 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;
|
||||
}
|
||||
}
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user