MireaBackend/Endpoint/Controllers/V1/AuthController.cs
Polianin Nikita cd6f25deba 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
2024-10-31 04:12:02 +03:00

126 lines
4.4 KiB
C#

using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Mirea.Api.Dto.Common;
using Mirea.Api.Dto.Requests;
using Mirea.Api.Dto.Responses;
using Mirea.Api.Endpoint.Common.Attributes;
using Mirea.Api.Endpoint.Common.Exceptions;
using Mirea.Api.Endpoint.Common.Services;
using Mirea.Api.Endpoint.Configuration.Model;
using Mirea.Api.Security.Common.Domain;
using Mirea.Api.Security.Services;
using System;
using System.Threading.Tasks;
namespace Mirea.Api.Endpoint.Controllers.V1;
[ApiVersion("1.0")]
public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, PasswordHashService passwordService) : BaseController
{
private CookieOptionsParameters GetCookieParams() =>
new()
{
Domain = HttpContext.GetCurrentDomain(),
Path = UrlHelper.GetSubPathWithoutFirstApiName + "api"
};
[HttpPost("Login")]
[BadRequestResponse]
public async Task<ActionResult<AuthenticationStep>> Login([FromBody] LoginRequest request)
{
var userEntity = user.Value;
if (!userEntity.Username.Equals(request.Username, StringComparison.OrdinalIgnoreCase) &&
!userEntity.Email.Equals(request.Username, StringComparison.OrdinalIgnoreCase))
return BadRequest("Invalid username/email or password");
var tokenResult = await auth.LoginAsync(
GetCookieParams(),
new User
{
Id = 1,
Username = userEntity.Username,
Email = userEntity.Email,
PasswordHash = userEntity.PasswordHash,
Salt = userEntity.Salt,
SecondFactor = userEntity.SecondFactor,
SecondFactorToken = userEntity.Secret
},
HttpContext, request.Password);
return Ok(tokenResult ? AuthenticationStep.None : AuthenticationStep.TotpRequired);
}
[HttpGet("Login")]
[BadRequestResponse]
public async Task<ActionResult<AuthenticationStep>> Login([FromQuery] string code)
{
var tokenResult = await auth.LoginAsync(GetCookieParams(), HttpContext, code);
return Ok(tokenResult ? AuthenticationStep.None : AuthenticationStep.TotpRequired);
}
/// <summary>
/// Refreshes the authentication token using the existing refresh token.
/// </summary>
/// <returns>User's AuthRoles.</returns>
[HttpGet("ReLogin")]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task<ActionResult<AuthRoles>> ReLogin()
{
await auth.RefreshTokenAsync(GetCookieParams(), HttpContext);
return Ok(AuthRoles.Admin);
}
/// <summary>
/// Logs the user out by clearing the refresh token and performing any necessary cleanup.
/// </summary>
/// <returns>An Ok response if the logout was successful.</returns>
[HttpGet("Logout")]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[Authorize]
public async Task<ActionResult> Logout()
{
await auth.LogoutAsync(GetCookieParams(), HttpContext);
return Ok();
}
/// <summary>
/// Retrieves the role of the authenticated user.
/// </summary>
/// <returns>The role of the authenticated user.</returns>
[HttpGet("GetRole")]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[Authorize]
public ActionResult<AuthRoles> GetRole() => Ok(AuthRoles.Admin);
[HttpPost("RenewPassword")]
[ApiExplorerSettings(IgnoreApi = true)]
[Localhost]
[BadRequestResponse]
public ActionResult<string> RenewPassword([FromBody] string? password = null)
{
if (string.IsNullOrEmpty(password))
password = string.Empty;
else if (!PasswordHashService.HasPasswordInPolicySecurity(password))
throw new ControllerArgumentException("The password must be at least 8 characters long and contain at least one uppercase letter and one special character.");
while (!PasswordHashService.HasPasswordInPolicySecurity(password))
password = GeneratorKey.GenerateAlphaNumeric(16, includes: "!@#%^");
var (salt, hash) = passwordService.HashPassword(password);
var admin = user.Value;
admin.Salt = salt;
admin.PasswordHash = hash;
admin.SaveSetting();
return Ok(password);
}
}