refactor: transfer logic

All logic related to token manipulation has been transferred to the AuthService. Also added TOTP 2FA and rethought the logic of logging into the application
This commit is contained in:
2024-10-31 04:12:02 +03:00
parent 0f47a98ad9
commit cd6f25deba
19 changed files with 371 additions and 278 deletions

View File

@ -0,0 +1,8 @@
namespace Mirea.Api.Security.Common;
public class CookieNames
{
public const string AccessToken = "access_token";
public const string RefreshToken = "refresh_token";
public const string FingerprintToken = "fingerprint";
}

View File

@ -2,11 +2,25 @@
namespace Mirea.Api.Security.Common.Domain;
public class AuthToken
internal class AuthToken
{
public required string RefreshToken { get; set; }
public required string UserAgent { get; set; }
public required string Ip { get; set; }
public AuthToken(RequestContextInfo context)
{
UserAgent = context.UserAgent;
Ip = context.Ip;
Fingerprint = context.Fingerprint;
RefreshToken = context.RefreshToken;
}
public AuthToken()
{
}
public string UserAgent { get; set; } = null!;
public string Ip { get; set; } = null!;
public string Fingerprint { get; set; } = null!;
public string RefreshToken { get; set; } = null!;
public required string UserId { get; set; }
public required string AccessToken { get; set; }
public DateTime CreatedAt { get; set; }

View File

@ -0,0 +1,29 @@
using Microsoft.AspNetCore.Http;
using System;
namespace Mirea.Api.Security.Common.Domain;
public class CookieOptionsParameters
{
public required string Domain { get; set; }
public required string Path { get; set; }
internal void SetCookie(HttpContext context, string name, string value, DateTimeOffset? expires = null)
{
var cookieOptions = new CookieOptions
{
Expires = expires,
Path = Path,
Domain = Domain,
HttpOnly = true,
#if !DEBUG
Secure = true
#endif
};
context.Response.Cookies.Append(name, value, cookieOptions);
}
internal void DropCookie(HttpContext context, string name) =>
SetCookie(context, name, "", DateTimeOffset.MinValue);
}

View File

@ -0,0 +1,22 @@
namespace Mirea.Api.Security.Common.Domain;
internal class FirstAuthToken
{
public FirstAuthToken(RequestContextInfo context)
{
UserAgent = context.UserAgent;
Ip = context.Ip;
Fingerprint = context.Fingerprint;
}
public FirstAuthToken()
{
}
public string UserAgent { get; set; } = null!;
public string Ip { get; set; } = null!;
public string Fingerprint { get; set; } = null!;
public required string UserId { get; set; }
public required SecondFactor SecondFactor { get; set; }
public string? Secret { get; set; }
}

View File

@ -1,10 +0,0 @@
namespace Mirea.Api.Security.Common.Domain;
public class PreAuthToken
{
public required string Fingerprint { get; set; }
public required string UserAgent { get; set; }
public required string UserId { get; set; }
public required string Ip { get; set; }
public required string Token { get; set; }
}

View File

@ -0,0 +1,38 @@
using Microsoft.AspNetCore.Http;
using Mirea.Api.Security.Services;
using System;
using System.Security;
namespace Mirea.Api.Security.Common.Domain;
internal class RequestContextInfo
{
public RequestContextInfo(HttpContext context, CookieOptionsParameters cookieOptions)
{
var ipEntity = context.Connection.RemoteIpAddress;
if (string.IsNullOrEmpty(ipEntity?.ToString()))
throw new SecurityException("Ip is required for authorization.");
var ip = ipEntity.MapToIPv4().ToString();
var userAgent = context.Request.Headers.UserAgent.ToString();
var fingerprint = context.Request.Cookies[CookieNames.FingerprintToken];
if (string.IsNullOrEmpty(fingerprint))
{
fingerprint = Guid.NewGuid().ToString().Replace("-", "") + GeneratorKey.GenerateString(32);
cookieOptions.SetCookie(context, CookieNames.FingerprintToken, fingerprint);
}
UserAgent = userAgent;
Fingerprint = fingerprint;
Ip = ip;
RefreshToken = context.Request.Cookies["refresh_token"] ?? string.Empty;
}
public string UserAgent { get; private set; }
public string Ip { get; private set; }
public string Fingerprint { get; private set; }
public string RefreshToken { get; private set; }
}

View File

@ -0,0 +1,18 @@
namespace Mirea.Api.Security.Common.Domain;
public enum SecondFactor
{
None,
Totp
}
public class User
{
public required int Id { get; set; }
public required string Username { get; set; }
public required string Email { get; set; }
public required string PasswordHash { get; set; }
public required string Salt { get; set; }
public required SecondFactor SecondFactor { get; set; }
public string? SecondFactorToken { get; set; }
}

View File

@ -1,8 +0,0 @@
namespace Mirea.Api.Security.Common.Dto.Requests;
public class TokenRequest
{
public required string Fingerprint { get; set; }
public required string UserAgent { get; set; }
public required string Ip { get; set; }
}

View File

@ -1,11 +0,0 @@
using System;
namespace Mirea.Api.Security.Common.Dto.Responses;
public class AuthTokenResponse
{
public required string AccessToken { get; set; }
public DateTime AccessExpiresIn { get; set; }
public required string RefreshToken { get; set; }
public DateTime RefreshExpiresIn { get; set; }
}

View File

@ -1,9 +0,0 @@
using System;
namespace Mirea.Api.Security.Common.Dto.Responses;
public class PreAuthTokenResponse
{
public required string Token { get; set; }
public DateTime ExpiresIn { get; set; }
}