Add authentication methods to access protected resources #15

Merged
Wesser merged 13 commits from feat/auth into release/v1.0.0 2024-06-28 23:14:18 +03:00
Showing only changes of commit 160c7505f0 - Show all commits

View File

@ -0,0 +1,140 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Options;
using Mirea.Api.Dto.Common;
using Mirea.Api.Dto.Requests;
using Mirea.Api.Dto.Responses;
using Mirea.Api.Endpoint.Common.Model;
using Mirea.Api.Security.Common.Dto.Requests;
using Mirea.Api.Security.Services;
using System;
using System.Security;
using System.Threading.Tasks;
namespace Mirea.Api.Endpoint.Controllers.V1;
[ApiVersion("1.0")]
public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, PasswordHashService passwordService) : BaseController, IActionFilter
{
private string Fingerprint { get; set; } = string.Empty;
private string Ip { get; set; } = string.Empty;
private string UserAgent { get; set; } = string.Empty;
private string RefreshToken { get; set; } = string.Empty;
private void SetCookie(string name, string value, DateTimeOffset? expires = null)
{
var cookieOptions = new CookieOptions
{
Expires = expires,
Path = "/api",
Domain = Request.Headers["X-Forwarded-Host"],
Secure = true,
HttpOnly = true
};
Response.Cookies.Append(name, value, cookieOptions);
}
private void SetRefreshToken(string value, DateTimeOffset? expires = null) =>
SetCookie("refresh_token", value, expires);
private void SetFirstToken(string value, DateTimeOffset? expires = null) =>
SetCookie("authentication_token", value, expires);
[ApiExplorerSettings(IgnoreApi = true)]
public void OnActionExecuting(ActionExecutingContext context)
{
Ip = context.HttpContext.Connection.RemoteIpAddress?.ToString()!;
UserAgent = context.HttpContext.Request.Headers.UserAgent.ToString();
Fingerprint = context.HttpContext.Request.Cookies["user_key"] ?? string.Empty;
RefreshToken = Request.Cookies["refresh_token"] ?? string.Empty;
if (!string.IsNullOrWhiteSpace(Fingerprint)) return;
Fingerprint = Guid.NewGuid().ToString().Replace("-", "");
SetCookie("user_key", Fingerprint);
}
[ApiExplorerSettings(IgnoreApi = true)]
public void OnActionExecuted(ActionExecutedContext context) { }
[HttpPost("Login")]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<ActionResult<TokenResponse>> Login([FromBody] LoginRequest request)
{
var userEntity = user.Value;
if (!userEntity.Username.Equals(request.Username, StringComparison.OrdinalIgnoreCase) &&
!userEntity.Email.Equals(request.Username, StringComparison.OrdinalIgnoreCase) ||
!passwordService.VerifyPassword(request.Password, userEntity.Salt, userEntity.PasswordHash))
return Unauthorized("Invalid username/email or password");
var token = await auth.GenerateAuthTokensAsync(new TokenRequest
{
Fingerprint = Fingerprint,
Ip = Ip,
UserAgent = UserAgent
}, "1");
SetRefreshToken(token.RefreshToken, token.RefreshExpiresIn);
return Ok(new TokenResponse
{
AccessToken = token.AccessToken,
ExpiresIn = token.AccessExpiresIn
});
}
[HttpGet("ReLogin")]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task<ActionResult<TokenResponse>> ReLogin()
{
if (string.IsNullOrEmpty(RefreshToken))
return Unauthorized();
try
{
var token = await auth.RefreshTokenAsync(
new TokenRequest
{
Ip = Ip,
UserAgent = UserAgent,
Fingerprint = Fingerprint
},
RefreshToken
);
SetRefreshToken(token.RefreshToken, token.RefreshExpiresIn);
return Ok(new TokenResponse
{
AccessToken = token.AccessToken,
ExpiresIn = token.AccessExpiresIn
});
}
catch (SecurityException)
{
return Unauthorized();
}
}
[HttpGet("Logout")]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[Authorize]
public async Task<ActionResult> Logout()
{
SetRefreshToken("", DateTimeOffset.MinValue);
SetFirstToken("", DateTimeOffset.MinValue);
await auth.LogoutAsync(Fingerprint);
return Ok();
}
[HttpGet("GetRole")]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[Authorize]
public ActionResult<AuthRoles> GetRole() => Ok(AuthRoles.Admin);
}