Add authentication methods to access protected resources #15
140
Endpoint/Controllers/V1/AuthController.cs
Normal file
140
Endpoint/Controllers/V1/AuthController.cs
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user