MireaBackend/Endpoint/Controllers/V1/AuthController.cs

174 lines
6.8 KiB
C#
Raw Normal View History

2024-09-18 06:00:07 +03:00
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;
2024-11-04 02:59:51 +03:00
using Mirea.Api.Dto.Responses;
using Mirea.Api.Endpoint.Common.Attributes;
2024-08-27 22:52:07 +03:00
using Mirea.Api.Endpoint.Common.Exceptions;
using Mirea.Api.Endpoint.Common.MapperDto;
2024-07-05 01:35:19 +03:00
using Mirea.Api.Endpoint.Common.Services;
2024-10-07 02:13:35 +03:00
using Mirea.Api.Endpoint.Configuration.Model;
using Mirea.Api.Security.Common.Domain;
using Mirea.Api.Security.Services;
using System;
2024-11-04 02:39:10 +03:00
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using OAuthProvider = Mirea.Api.Security.Common.Domain.OAuthProvider;
namespace Mirea.Api.Endpoint.Controllers.V1;
[ApiVersion("1.0")]
public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, PasswordHashService passwordService) : BaseController
{
private CookieOptionsParameters GetCookieParams() =>
new()
{
2024-08-10 23:11:43 +03:00
Domain = HttpContext.GetCurrentDomain(),
Path = UrlHelper.GetSubPathWithoutFirstApiName + "api"
};
2024-11-04 03:14:42 +03:00
/// <summary>
/// Initiates the OAuth2 authorization process for the selected provider.
2024-11-04 03:14:42 +03:00
/// </summary>
/// <remarks>
/// This method generates a redirect URL for the selected provider and redirects the user to it.
/// </remarks>
/// <param name="provider">The identifier of the OAuth provider to authorize with.</param>
/// <returns>A redirect to the OAuth provider's authorization URL.</returns>
/// <exception cref="ControllerArgumentException">Thrown if the specified provider is not valid.</exception>
[HttpGet("AuthorizeOAuth2")]
[MaintenanceModeIgnore]
public ActionResult AuthorizeOAuth2([FromQuery] int provider)
{
if (!Enum.IsDefined(typeof(OAuthProvider), provider))
throw new ControllerArgumentException("There is no selected provider");
return Redirect(oAuthService.GetProviderRedirect(HttpContext, GetCookieParams(), HttpContext.GetApiUrl(Url.Action("OAuth2")!), (OAuthProvider)provider).AbsoluteUri);
}
/// <summary>
/// Retrieves a list of available OAuth providers with their corresponding authorization URLs.
/// </summary>
/// <remarks>
/// This allows the client to fetch all possible OAuth options and the URLs required to initiate authorization.
/// </remarks>
/// <returns>A list of available providers and their redirect URLs.</returns>
[HttpGet("AvailableProviders")]
[MaintenanceModeIgnore]
public ActionResult<List<AvailableOAuthProvidersResponse>> AvailableProviders() =>
2024-11-04 02:59:51 +03:00
Ok(oAuthService
.GetAvailableProviders(HttpContext, HttpContext.GetApiUrl(Url.Action("AuthorizeOAuth2")!))
2024-11-04 02:59:51 +03:00
.ConvertToDto());
2024-11-04 03:14:42 +03:00
/// <summary>
/// Logs in a user using their username or email and password.
/// </summary>
/// <param name="request">The login request containing username/email and password.</param>
/// <returns>A TwoFactorAuthentication token if the login is successful; otherwise, a BadRequest response.</returns>
[HttpPost("Login")]
[BadRequestResponse]
2024-11-02 00:50:10 +03:00
public async Task<ActionResult<TwoFactorAuthentication>> Login([FromBody] LoginRequest request)
{
var userEntity = user.Value;
if (!userEntity.Username.Equals(request.Username, StringComparison.OrdinalIgnoreCase) &&
!userEntity.Email.Equals(request.Username, StringComparison.OrdinalIgnoreCase))
2024-11-04 03:14:17 +03:00
return Unauthorized("Authentication failed. Please check your credentials.");
var tokenResult = await auth.LoginAsync(
GetCookieParams(),
new User
{
2024-11-02 22:09:40 +03:00
Id = 1.ToString(),
Username = userEntity.Username,
Email = userEntity.Email,
PasswordHash = userEntity.PasswordHash,
Salt = userEntity.Salt,
2024-11-02 00:50:10 +03:00
TwoFactorAuthenticator = userEntity.TwoFactorAuthenticator,
2024-11-04 02:39:10 +03:00
SecondFactorToken = userEntity.Secret,
OAuthProviders = userEntity.OAuthProviders
},
HttpContext, request.Password);
return Ok(tokenResult.ConvertToDto());
}
2024-11-04 03:14:42 +03:00
/// <summary>
/// Performs two-factor authentication for the user.
/// </summary>
/// <param name="request">The request containing the method and code for two-factor authentication.</param>
/// <returns>A boolean indicating whether the two-factor authentication was successful.</returns>
[HttpPost("2FA")]
[BadRequestResponse]
2024-11-02 22:09:40 +03:00
public async Task<ActionResult<bool>> TwoFactorAuth([FromBody] TwoFactorAuthRequest request)
{
var tokenResult = await auth.LoginAsync(GetCookieParams(), HttpContext, request.Method.ConvertFromDto(), request.Code);
2024-11-02 22:09:40 +03:00
return Ok(tokenResult);
}
2024-06-28 22:55:18 +03:00
/// <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);
}
2024-06-28 22:55:18 +03:00
/// <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")]
public async Task<ActionResult> Logout()
{
await auth.LogoutAsync(GetCookieParams(), HttpContext);
return Ok();
}
2024-06-28 22:55:18 +03:00
/// <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);
2024-08-27 22:52:07 +03:00
[HttpPost("RenewPassword")]
[ApiExplorerSettings(IgnoreApi = true)]
[Localhost]
[BadRequestResponse]
public ActionResult<string> RenewPassword([FromBody] string? password = null)
{
var passwordPolicy = generalConfig.Value.PasswordPolicy;
var passwordPolicyService = new PasswordPolicyService(passwordPolicy);
2024-08-27 22:52:07 +03:00
if (string.IsNullOrEmpty(password))
password = string.Empty;
else
passwordPolicyService.ValidatePasswordOrThrow(password);
2024-08-27 22:52:07 +03:00
while (!passwordPolicyService.TryValidatePassword(password))
password = GeneratorKey.GenerateAlphaNumeric(passwordPolicy.MinimumLength + 2, includes: "!@#%^");
2024-08-27 22:52:07 +03:00
var (salt, hash) = passwordService.HashPassword(password);
var admin = user.Value;
admin.Salt = salt;
admin.PasswordHash = hash;
admin.SaveSetting();
return Ok(password);
}
}