diff --git a/ApiDto/Responses/TokenResponse.cs b/ApiDto/Responses/TokenResponse.cs
deleted file mode 100644
index 9761b87..0000000
--- a/ApiDto/Responses/TokenResponse.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System;
-using System.ComponentModel.DataAnnotations;
-
-namespace Mirea.Api.Dto.Responses;
-
-///
-/// Provides a JWT and RT token.
-///
-public class TokenResponse
-{
- ///
- /// A JWT token for accessing protected resources.
- ///
- [Required]
- public required string AccessToken { get; set; }
-
- ///
- /// The date and time when the JWT token expires.
- ///
- /// After this date, a new JWT token must be requested.
- [Required]
- public required DateTime ExpiresIn { get; set; }
-}
\ No newline at end of file
diff --git a/Endpoint/Configuration/Core/Middleware/CookieAuthorizationMiddleware.cs b/Endpoint/Configuration/Core/Middleware/CookieAuthorizationMiddleware.cs
new file mode 100644
index 0000000..c3e0cf5
--- /dev/null
+++ b/Endpoint/Configuration/Core/Middleware/CookieAuthorizationMiddleware.cs
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/Endpoint/Controllers/V1/AuthController.cs b/Endpoint/Controllers/V1/AuthController.cs
index 6081656..2954985 100644
--- a/Endpoint/Controllers/V1/AuthController.cs
+++ b/Endpoint/Controllers/V1/AuthController.cs
@@ -6,10 +6,10 @@ 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.Attributes;
using Mirea.Api.Endpoint.Common.Exceptions;
using Mirea.Api.Endpoint.Common.Services;
+using Mirea.Api.Endpoint.Configuration.Core.Middleware;
using Mirea.Api.Endpoint.Configuration.Model;
using Mirea.Api.Security.Common.Dto.Requests;
using Mirea.Api.Security.Services;
@@ -55,6 +55,12 @@ public class AuthController(IOptionsSnapshot user, AuthService auth, Pass
SetCookie("user_key", Fingerprint);
}
+ private void SetAuthToken(string value, DateTimeOffset? expires = null)
+ {
+ SetCookie(CookieAuthorizationMiddleware.JwtAuthorizationName, value, expires);
+ SetCookie("user_key", Fingerprint);
+ }
+
[ApiExplorerSettings(IgnoreApi = true)]
public void OnActionExecuting(ActionExecutingContext context)
{
@@ -76,17 +82,17 @@ public class AuthController(IOptionsSnapshot user, AuthService auth, Pass
/// then generating and returning an authentication token if successful.
///
/// The login request containing the username/email and password.
- /// A TokenResponse containing the access token and its expiry if successful, otherwise an Unauthorized response.
+ /// User's AuthRoles.
[HttpPost("Login")]
- [ProducesResponseType(StatusCodes.Status401Unauthorized)]
- public async Task> Login([FromBody] LoginRequest request)
+ [BadRequestResponse]
+ public async Task> 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");
+ return BadRequest("Invalid username/email or password");
var token = await auth.GenerateAuthTokensAsync(new TokenRequest
{
@@ -96,21 +102,19 @@ public class AuthController(IOptionsSnapshot user, AuthService auth, Pass
}, "1");
SetRefreshToken(token.RefreshToken, token.RefreshExpiresIn);
+ SetAuthToken(token.AccessToken, token.AccessExpiresIn);
- return Ok(new TokenResponse
- {
- AccessToken = token.AccessToken,
- ExpiresIn = token.AccessExpiresIn
- });
+ return Ok(AuthRoles.Admin);
}
///
/// Refreshes the authentication token using the existing refresh token.
///
- /// A TokenResponse containing the new access token and its expiry if successful, otherwise an Unauthorized response.
+ /// User's AuthRoles.
[HttpGet("ReLogin")]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
- public async Task> ReLogin()
+ [ProducesResponseType(StatusCodes.Status403Forbidden)]
+ public async Task> ReLogin()
{
if (string.IsNullOrEmpty(RefreshToken))
return Unauthorized();
@@ -128,16 +132,13 @@ public class AuthController(IOptionsSnapshot user, AuthService auth, Pass
);
SetRefreshToken(token.RefreshToken, token.RefreshExpiresIn);
+ SetAuthToken(token.AccessToken, token.AccessExpiresIn);
- return Ok(new TokenResponse
- {
- AccessToken = token.AccessToken,
- ExpiresIn = token.AccessExpiresIn
- });
+ return Ok(AuthRoles.Admin);
}
catch (SecurityException)
{
- return Unauthorized();
+ return Forbid();
}
}
@@ -152,6 +153,7 @@ public class AuthController(IOptionsSnapshot user, AuthService auth, Pass
{
SetRefreshToken("", DateTimeOffset.MinValue);
SetFirstToken("", DateTimeOffset.MinValue);
+ SetAuthToken("", DateTimeOffset.MinValue);
await auth.LogoutAsync(Fingerprint);
diff --git a/Endpoint/Program.cs b/Endpoint/Program.cs
index 7843cd3..0b39b2a 100644
--- a/Endpoint/Program.cs
+++ b/Endpoint/Program.cs
@@ -128,16 +128,18 @@ public class Program
}
app.UseCustomSwagger(app.Services);
+ app.UseHttpsRedirection();
app.UseMiddleware();
app.UseMiddleware();
+ app.UseMiddleware();
+
+ app.UseAuthentication();
+ app.UseAuthorization();
+
app.UseMiddleware();
app.UseMiddleware();
- app.UseHttpsRedirection();
-
- app.UseAuthorization();
-
app.MapControllers();
app.Run();