2024-09-18 06:00:07 +03:00
using Asp.Versioning ;
using Microsoft.AspNetCore.Authorization ;
2024-06-21 21:52:21 +03:00
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 ;
2024-08-24 04:30:31 +03:00
using Mirea.Api.Endpoint.Common.Attributes ;
2024-08-27 22:52:07 +03:00
using Mirea.Api.Endpoint.Common.Exceptions ;
2024-11-02 01:06:58 +03:00
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 ;
2024-10-31 04:12:02 +03:00
using Mirea.Api.Security.Common.Domain ;
2024-06-21 21:52:21 +03:00
using Mirea.Api.Security.Services ;
using System ;
2024-11-04 02:39:10 +03:00
using System.Collections.Generic ;
2024-12-18 07:23:23 +03:00
using System.Security.Claims ;
2024-06-21 21:52:21 +03:00
using System.Threading.Tasks ;
2024-12-18 07:23:23 +03:00
using OAuthProvider = Mirea . Api . Security . Common . Domain . OAuthProvider ;
2024-06-21 21:52:21 +03:00
namespace Mirea.Api.Endpoint.Controllers.V1 ;
[ApiVersion("1.0")]
2024-10-31 04:12:02 +03:00
public class AuthController ( IOptionsSnapshot < Admin > user , AuthService auth , PasswordHashService passwordService ) : BaseController
2024-06-21 21:52:21 +03:00
{
2024-10-31 04:12:02 +03:00
private CookieOptionsParameters GetCookieParams ( ) = >
new ( )
2024-06-21 21:52:21 +03:00
{
2024-08-10 23:11:43 +03:00
Domain = HttpContext . GetCurrentDomain ( ) ,
2024-10-31 04:12:02 +03:00
Path = UrlHelper . GetSubPathWithoutFirstApiName + "api"
2024-06-21 21:52:21 +03:00
} ;
2024-11-04 03:14:42 +03:00
/// <summary>
2024-12-18 07:23:23 +03:00
/// Initiates the OAuth2 authorization process for the selected provider.
2024-11-04 03:14:42 +03:00
/// </summary>
2024-12-18 07:23:23 +03:00
/// <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
2024-12-18 07:23:23 +03:00
. 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>
2024-06-21 21:52:21 +03:00
[HttpPost("Login")]
2024-10-09 03:00:26 +03:00
[BadRequestResponse]
2024-11-02 00:50:10 +03:00
public async Task < ActionResult < TwoFactorAuthentication > > Login ( [ FromBody ] LoginRequest request )
2024-06-21 21:52:21 +03:00
{
var userEntity = user . Value ;
if ( ! userEntity . Username . Equals ( request . Username , StringComparison . OrdinalIgnoreCase ) & &
2024-10-31 04:12:02 +03:00
! userEntity . Email . Equals ( request . Username , StringComparison . OrdinalIgnoreCase ) )
2024-11-04 03:14:17 +03:00
return Unauthorized ( "Authentication failed. Please check your credentials." ) ;
2024-06-21 21:52:21 +03:00
2024-10-31 04:12:02 +03:00
var tokenResult = await auth . LoginAsync (
GetCookieParams ( ) ,
new User
{
2024-11-02 22:09:40 +03:00
Id = 1. ToString ( ) ,
2024-10-31 04:12:02 +03:00
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
2024-10-31 04:12:02 +03:00
} ,
HttpContext , request . Password ) ;
2024-11-02 01:06:58 +03:00
return Ok ( tokenResult . ConvertToDto ( ) ) ;
2024-10-31 04:12:02 +03:00
}
2024-06-21 21:52:21 +03:00
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>
2024-11-02 01:09:15 +03:00
[HttpPost("2FA")]
2024-10-31 04:12:02 +03:00
[BadRequestResponse]
2024-11-02 22:09:40 +03:00
public async Task < ActionResult < bool > > TwoFactorAuth ( [ FromBody ] TwoFactorAuthRequest request )
2024-10-31 04:12:02 +03:00
{
2024-11-02 01:09:15 +03:00
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-21 21:52:21 +03:00
}
2024-06-28 22:55:18 +03:00
/// <summary>
/// Refreshes the authentication token using the existing refresh token.
/// </summary>
2024-10-09 03:00:26 +03:00
/// <returns>User's AuthRoles.</returns>
2024-06-21 21:52:21 +03:00
[HttpGet("ReLogin")]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
2024-10-09 03:00:26 +03:00
[ProducesResponseType(StatusCodes.Status403Forbidden)]
public async Task < ActionResult < AuthRoles > > ReLogin ( )
2024-06-21 21:52:21 +03:00
{
2024-10-31 04:12:02 +03:00
await auth . RefreshTokenAsync ( GetCookieParams ( ) , HttpContext ) ;
return Ok ( AuthRoles . Admin ) ;
2024-06-21 21:52:21 +03:00
}
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>
2024-06-21 21:52:21 +03:00
[HttpGet("Logout")]
public async Task < ActionResult > Logout ( )
{
2024-10-31 04:12:02 +03:00
await auth . LogoutAsync ( GetCookieParams ( ) , HttpContext ) ;
2024-06-21 21:52:21 +03:00
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>
2024-06-21 21:52:21 +03:00
[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 )
{
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 ) ;
}
2024-06-21 21:52:21 +03:00
}