sec: move token from responce to cookie
This commit is contained in:
parent
b49df925d4
commit
1f3aaca3cf
@ -1,23 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
|
|
||||||
namespace Mirea.Api.Dto.Responses;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides a JWT and RT token.
|
|
||||||
/// </summary>
|
|
||||||
public class TokenResponse
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A JWT token for accessing protected resources.
|
|
||||||
/// </summary>
|
|
||||||
[Required]
|
|
||||||
public required string AccessToken { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The date and time when the JWT token expires.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>After this date, a new JWT token must be requested.</remarks>
|
|
||||||
[Required]
|
|
||||||
public required DateTime ExpiresIn { get; set; }
|
|
||||||
}
|
|
@ -0,0 +1,17 @@
|
|||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Mirea.Api.Security.Common.Interfaces;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Mirea.Api.Endpoint.Configuration.Core.Middleware;
|
||||||
|
|
||||||
|
public class CookieAuthorizationMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
public const string JwtAuthorizationName = "_ajwt";
|
||||||
|
public async Task InvokeAsync(HttpContext context, IRevokedToken revokedTokenStore)
|
||||||
|
{
|
||||||
|
if (context.Request.Cookies.ContainsKey(JwtAuthorizationName))
|
||||||
|
context.Request.Headers.Authorization = "Bearer " + context.Request.Cookies[JwtAuthorizationName];
|
||||||
|
|
||||||
|
await next(context);
|
||||||
|
}
|
||||||
|
}
|
@ -6,10 +6,10 @@ using Microsoft.AspNetCore.Mvc.Filters;
|
|||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Mirea.Api.Dto.Common;
|
using Mirea.Api.Dto.Common;
|
||||||
using Mirea.Api.Dto.Requests;
|
using Mirea.Api.Dto.Requests;
|
||||||
using Mirea.Api.Dto.Responses;
|
|
||||||
using Mirea.Api.Endpoint.Common.Attributes;
|
using Mirea.Api.Endpoint.Common.Attributes;
|
||||||
using Mirea.Api.Endpoint.Common.Exceptions;
|
using Mirea.Api.Endpoint.Common.Exceptions;
|
||||||
using Mirea.Api.Endpoint.Common.Services;
|
using Mirea.Api.Endpoint.Common.Services;
|
||||||
|
using Mirea.Api.Endpoint.Configuration.Core.Middleware;
|
||||||
using Mirea.Api.Endpoint.Configuration.Model;
|
using Mirea.Api.Endpoint.Configuration.Model;
|
||||||
using Mirea.Api.Security.Common.Dto.Requests;
|
using Mirea.Api.Security.Common.Dto.Requests;
|
||||||
using Mirea.Api.Security.Services;
|
using Mirea.Api.Security.Services;
|
||||||
@ -55,6 +55,12 @@ public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, Pass
|
|||||||
SetCookie("user_key", Fingerprint);
|
SetCookie("user_key", Fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetAuthToken(string value, DateTimeOffset? expires = null)
|
||||||
|
{
|
||||||
|
SetCookie(CookieAuthorizationMiddleware.JwtAuthorizationName, value, expires);
|
||||||
|
SetCookie("user_key", Fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
public void OnActionExecuting(ActionExecutingContext context)
|
public void OnActionExecuting(ActionExecutingContext context)
|
||||||
{
|
{
|
||||||
@ -76,17 +82,17 @@ public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, Pass
|
|||||||
/// then generating and returning an authentication token if successful.
|
/// then generating and returning an authentication token if successful.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request">The login request containing the username/email and password.</param>
|
/// <param name="request">The login request containing the username/email and password.</param>
|
||||||
/// <returns>A TokenResponse containing the access token and its expiry if successful, otherwise an Unauthorized response.</returns>
|
/// <returns>User's AuthRoles.</returns>
|
||||||
[HttpPost("Login")]
|
[HttpPost("Login")]
|
||||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
[BadRequestResponse]
|
||||||
public async Task<ActionResult<TokenResponse>> Login([FromBody] LoginRequest request)
|
public async Task<ActionResult<AuthRoles>> Login([FromBody] LoginRequest request)
|
||||||
{
|
{
|
||||||
var userEntity = user.Value;
|
var userEntity = user.Value;
|
||||||
|
|
||||||
if (!userEntity.Username.Equals(request.Username, StringComparison.OrdinalIgnoreCase) &&
|
if (!userEntity.Username.Equals(request.Username, StringComparison.OrdinalIgnoreCase) &&
|
||||||
!userEntity.Email.Equals(request.Username, StringComparison.OrdinalIgnoreCase) ||
|
!userEntity.Email.Equals(request.Username, StringComparison.OrdinalIgnoreCase) ||
|
||||||
!passwordService.VerifyPassword(request.Password, userEntity.Salt, userEntity.PasswordHash))
|
!passwordService.VerifyPassword(request.Password, userEntity.Salt, userEntity.PasswordHash))
|
||||||
return Unauthorized("Invalid username/email or password");
|
return BadRequest("Invalid username/email or password");
|
||||||
|
|
||||||
var token = await auth.GenerateAuthTokensAsync(new TokenRequest
|
var token = await auth.GenerateAuthTokensAsync(new TokenRequest
|
||||||
{
|
{
|
||||||
@ -96,21 +102,19 @@ public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, Pass
|
|||||||
}, "1");
|
}, "1");
|
||||||
|
|
||||||
SetRefreshToken(token.RefreshToken, token.RefreshExpiresIn);
|
SetRefreshToken(token.RefreshToken, token.RefreshExpiresIn);
|
||||||
|
SetAuthToken(token.AccessToken, token.AccessExpiresIn);
|
||||||
|
|
||||||
return Ok(new TokenResponse
|
return Ok(AuthRoles.Admin);
|
||||||
{
|
|
||||||
AccessToken = token.AccessToken,
|
|
||||||
ExpiresIn = token.AccessExpiresIn
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Refreshes the authentication token using the existing refresh token.
|
/// Refreshes the authentication token using the existing refresh token.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A TokenResponse containing the new access token and its expiry if successful, otherwise an Unauthorized response.</returns>
|
/// <returns>User's AuthRoles.</returns>
|
||||||
[HttpGet("ReLogin")]
|
[HttpGet("ReLogin")]
|
||||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||||
public async Task<ActionResult<TokenResponse>> ReLogin()
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||||
|
public async Task<ActionResult<AuthRoles>> ReLogin()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(RefreshToken))
|
if (string.IsNullOrEmpty(RefreshToken))
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
@ -128,16 +132,13 @@ public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, Pass
|
|||||||
);
|
);
|
||||||
|
|
||||||
SetRefreshToken(token.RefreshToken, token.RefreshExpiresIn);
|
SetRefreshToken(token.RefreshToken, token.RefreshExpiresIn);
|
||||||
|
SetAuthToken(token.AccessToken, token.AccessExpiresIn);
|
||||||
|
|
||||||
return Ok(new TokenResponse
|
return Ok(AuthRoles.Admin);
|
||||||
{
|
|
||||||
AccessToken = token.AccessToken,
|
|
||||||
ExpiresIn = token.AccessExpiresIn
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
catch (SecurityException)
|
catch (SecurityException)
|
||||||
{
|
{
|
||||||
return Unauthorized();
|
return Forbid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,6 +153,7 @@ public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, Pass
|
|||||||
{
|
{
|
||||||
SetRefreshToken("", DateTimeOffset.MinValue);
|
SetRefreshToken("", DateTimeOffset.MinValue);
|
||||||
SetFirstToken("", DateTimeOffset.MinValue);
|
SetFirstToken("", DateTimeOffset.MinValue);
|
||||||
|
SetAuthToken("", DateTimeOffset.MinValue);
|
||||||
|
|
||||||
await auth.LogoutAsync(Fingerprint);
|
await auth.LogoutAsync(Fingerprint);
|
||||||
|
|
||||||
|
@ -128,16 +128,18 @@ public class Program
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.UseCustomSwagger(app.Services);
|
app.UseCustomSwagger(app.Services);
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
app.UseMiddleware<CustomExceptionHandlerMiddleware>();
|
app.UseMiddleware<CustomExceptionHandlerMiddleware>();
|
||||||
app.UseMiddleware<MaintenanceModeMiddleware>();
|
app.UseMiddleware<MaintenanceModeMiddleware>();
|
||||||
|
app.UseMiddleware<CookieAuthorizationMiddleware>();
|
||||||
|
|
||||||
|
app.UseAuthentication();
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.UseMiddleware<JwtRevocationMiddleware>();
|
app.UseMiddleware<JwtRevocationMiddleware>();
|
||||||
app.UseMiddleware<CacheMaxAgeMiddleware>();
|
app.UseMiddleware<CacheMaxAgeMiddleware>();
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
|
||||||
|
|
||||||
app.UseAuthorization();
|
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
Loading…
Reference in New Issue
Block a user