Cleaned up the architecture

This commit is contained in:
OlgunR 2024-10-01 11:45:17 +02:00
parent 3df98fb399
commit 64f3dc2875
4 changed files with 83 additions and 59 deletions

View File

@ -1,9 +1,6 @@
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Annotations;
using System.Security.Claims;
using UserManagement.Application.Dtos.Auth; using UserManagement.Application.Dtos.Auth;
using UserManagement.Application.Interfaces; using UserManagement.Application.Interfaces;
@ -16,75 +13,57 @@ namespace UserManagement.API.Controllers
// CTOR // CTOR
private readonly IUserService _userService; private readonly IUserService _userService;
private readonly IAuthService _authService; private readonly IAuthService _authService;
public AuthController(IUserService userService, IAuthService authService) private readonly ILogger<AuthController> _logger;
public AuthController(IUserService userService, IAuthService authService, ILogger<AuthController> logger)
{ {
_userService = userService; _userService = userService;
_authService = authService; _authService = authService;
_logger = logger;
} }
// LOGIN // SIGN IN
[AllowAnonymous]
[HttpPost("login")] [HttpPost("login")]
[SwaggerOperation(Summary = "Login")] [SwaggerOperation(Summary = "Login")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> Login([FromBody] LoginDto login) public async Task<IActionResult> Login([FromBody] LoginDto login)
{ {
// Validate user try
var user = await _userService.GetUserByUsernameAsync(login.Username, includeRoles: true);
if (user is null)
{ {
return Unauthorized("Benutzername und Passwort stimmen nicht überein!"); await _authService.SignInAsync(login.Username, login.Password, HttpContext);
return Ok();
} }
catch (UnauthorizedAccessException ex)
// Validate login credentials
var isValid = await _authService.ValidateAsync(login.Username, login.Password);
if (!isValid)
{ {
return Unauthorized("Benutzername und Passwort stimmen nicht überein!"); _logger.LogError(ex, ex.Message);
return Unauthorized(ex.Message);
} }
catch (Exception ex)
// Create claims based on the user information
var claims = new List<Claim>
{ {
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), _logger.LogError(ex, ex.Message);
new Claim(ClaimTypes.Name, user.UserName), return StatusCode(StatusCodes.Status500InternalServerError);
new Claim(ClaimTypes.Surname, user.LastName ?? ""),
new Claim(ClaimTypes.GivenName, user.FirstName ?? ""),
};
foreach (var userRole in user.UserRoles)
{
claims.Add(new Claim(ClaimTypes.Role, userRole!.Name));
} }
// Create a ClaimsIdentity based on the created claims
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
// Set the authentication properties
var authProperties = new AuthenticationProperties
{
IsPersistent = true,
AllowRefresh = true,
ExpiresUtc = DateTime.UtcNow.AddMinutes(60)
};
// Sign in user using cookie-based authentication
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties
);
return Ok();
} }
// LOGOUT // LOGOUT
[HttpPost("logout")] [HttpPost("logout")]
[SwaggerOperation(Summary = "Logout")] [SwaggerOperation(Summary = "Logout")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> Logout() public async Task<IActionResult> Logout()
{ {
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); try
{
return Ok(); await _authService.SignOutAsync(HttpContext);
return Ok();
}
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
} }
// AUTH CHECK // AUTH CHECK

View File

@ -8,7 +8,7 @@ namespace UserManagement.API.Controllers
{ {
[Route("api/[controller]")] [Route("api/[controller]")]
[ApiController] [ApiController]
//[Authorize(Roles = "Admin")] [Authorize(Roles = "Admin")]
public class UserController : Controller public class UserController : Controller
{ {
// CTOR // CTOR

View File

@ -1,8 +1,15 @@
namespace UserManagement.Application.Interfaces using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
namespace UserManagement.Application.Interfaces
{ {
public interface IAuthService public interface IAuthService
{ {
// AUTHENTICATE // SIGN IN
Task<bool> ValidateAsync(string username, string password); Task<ClaimsPrincipal> SignInAsync(string username, string password, HttpContext httpContext);
// SIGN OUT
Task SignOutAsync(HttpContext httpContext);
} }
} }

View File

@ -1,4 +1,8 @@
using UserManagement.Application.Interfaces; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using System.Security.Claims;
using UserManagement.Application.Interfaces;
using UserManagement.Infrastructure.Interfaces; using UserManagement.Infrastructure.Interfaces;
namespace UserManagement.Application.Services namespace UserManagement.Application.Services
@ -12,12 +16,46 @@ namespace UserManagement.Application.Services
_userRepository = userRepository; _userRepository = userRepository;
} }
// AUTHENTICATE // LOGIN
public async Task<bool> ValidateAsync(string username, string password) public async Task<ClaimsPrincipal> SignInAsync(string username, string password, HttpContext httpContext)
{ {
var user = await _userRepository.GetByUsernameAsync(username, includeRoles: true); var user = await _userRepository.GetByUsernameAsync(username, includeRoles: true);
return BCrypt.Net.BCrypt.Verify(password, user!.PasswordHash); if (user == null || !BCrypt.Net.BCrypt.Verify(password, user.PasswordHash))
{
throw new UnauthorizedAccessException("Benutzername und Passwort stimmen nicht überein!");
}
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.Surname, user.LastName ?? ""),
new Claim(ClaimTypes.GivenName, user.FirstName ?? "")
};
claims.AddRange(user.UserRoles.Select(role => new Claim(ClaimTypes.Role, role.Role.Name)));
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var authProperties = new AuthenticationProperties
{
IsPersistent = true,
AllowRefresh = true,
ExpiresUtc = DateTime.UtcNow.AddMinutes(60)
};
var principal = new ClaimsPrincipal(claimsIdentity);
await httpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, authProperties);
return principal;
}
// LOGOUT
public async Task SignOutAsync(HttpContext httpContext)
{
await httpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
} }
} }
} }