Files
DigitalData.Auth/src/DigitalData.Auth.API/Controllers/AuthController.cs
2025-03-10 17:05:05 +01:00

201 lines
7.4 KiB
C#

using DigitalData.Auth.API.Config;
using DigitalData.Core.Abstractions.Security;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using DigitalData.UserManager.Application.Contracts;
using DigitalData.UserManager.Application.DTOs.User;
using DigitalData.Core.Abstractions.Application;
using DigitalData.Auth.API.Dto;
using DigitalData.Auth.API.Services.Contracts;
using DigitalData.Auth.API.Entities;
using DigitalData.Core.DTO;
namespace DigitalData.Auth.API.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly IJwtSignatureHandler<UserReadDto> _userSignatureHandler;
private readonly IJwtSignatureHandler<Consumer> _consumerSignatureHandler;
private readonly AuthApiParams _apiParams;
private readonly ICryptoFactory _cryptoFactory;
private readonly ILogger<AuthController> _logger;
private readonly IUserService _userService;
private readonly IDirectorySearchService _dirSearchService;
private readonly IConsumerService _consumerService;
public AuthController(IJwtSignatureHandler<UserReadDto> userSignatureHandler, IOptions<AuthApiParams> cookieParamsOptions, ICryptoFactory cryptoFactory, ILogger<AuthController> logger, IUserService userService, IDirectorySearchService dirSearchService, IConsumerService consumerService, IJwtSignatureHandler<Consumer> apiSignatureHandler)
{
_apiParams = cookieParamsOptions.Value;
_userSignatureHandler = userSignatureHandler;
_cryptoFactory = cryptoFactory;
_logger = logger;
_userService = userService;
_dirSearchService = dirSearchService;
_consumerService = consumerService;
_consumerSignatureHandler = apiSignatureHandler;
}
private async Task<IActionResult> CreateTokenAsync(UserLogin login, string consumerName, bool cookie = true)
{
DataResult<UserReadDto>? uRes;
if(login.Username is not null && login.UserId is not null)
return BadRequest("Both user ID and username cannot be provided.");
if (login.Username is not null)
{
bool isValid = await _dirSearchService.ValidateCredentialsAsync(login.Username, login.Password);
if (!isValid)
return Unauthorized();
uRes = await _userService.ReadByUsernameAsync(login.Username);
if (uRes.IsFailed)
return Unauthorized();
}
else if(login.UserId is int userId)
{
uRes = await _userService.ReadByIdAsync(userId);
if (uRes.IsFailed)
return Unauthorized();
bool isValid = await _dirSearchService.ValidateCredentialsAsync(uRes.Data.Username, login.Password);
if (!isValid)
return Unauthorized();
}
else
{
return BadRequest("User ID or username should be provided.");
}
//find the user
var consumer = await _consumerService.ReadByNameAsync(consumerName);
if (consumer is null)
return Unauthorized();
if (!_cryptoFactory.TokenDescriptors.TryGet(_apiParams.Issuer, consumer.Audience, out var descriptor))
return StatusCode(StatusCodes.Status500InternalServerError);
var token = _userSignatureHandler.WriteToken(uRes!.Data, descriptor);
//set cookie
if (cookie)
{
var cookieOptions = consumer.CookieOptions ?? _apiParams.DefaultCookieOptions;
Response.Cookies.Append(_apiParams.DefaultCookieName, token, cookieOptions.Create(lifetime: descriptor.Lifetime));
return Ok();
}
else
return Ok(token);
}
private async Task<IActionResult> CreateTokenAsync(ConsumerLogin login, bool cookie = true)
{
var consumer = await _consumerService.ReadByNameAsync(login.Name);
if (consumer is null || consumer.Password != login.Password)
return Unauthorized();
if (!_cryptoFactory.TokenDescriptors.TryGet(_apiParams.Issuer, _apiParams.LocalConsumer.Audience, out var descriptor))
return StatusCode(StatusCodes.Status500InternalServerError);
var token = _consumerSignatureHandler.WriteToken(consumer, descriptor);
//set cookie
if (cookie)
{
var cookieOptions = _apiParams.LocalConsumer.CookieOptions ?? _apiParams.DefaultCookieOptions;
Response.Cookies.Append(_apiParams.DefaultCookieName, token, cookieOptions.Create(lifetime: descriptor.Lifetime));
return Ok();
}
else
return Ok(token);
}
//TODO: Add role depends on group name
[HttpPost("{consumerName}/login")]
[AllowAnonymous]
public async Task<IActionResult> Login([FromForm] UserLogin login, [FromRoute] string consumerName)
{
try
{
return await CreateTokenAsync(login, consumerName, true);
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpPost("login")]
[AllowAnonymous]
public async Task<IActionResult> Login([FromForm] ConsumerLogin login)
{
try
{
return await CreateTokenAsync(login, true);
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpPost("logout")]
public IActionResult Logout()
{
try
{
Response.Cookies.Delete(_apiParams.DefaultCookieName);
return Ok();
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpPost("{consumerName}")]
public async Task<IActionResult> CreateTokenViaBody([FromBody] UserLogin login, [FromRoute] string consumerName, [FromQuery] bool cookie = false)
{
try
{
return await CreateTokenAsync(login, consumerName, cookie);
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpPost]
public async Task<IActionResult> CreateTokenViaBody([FromBody] ConsumerLogin login, [FromQuery] bool cookie = false)
{
try
{
return await CreateTokenAsync(login, cookie);
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("check")]
[Authorize]
public IActionResult Check() => Ok();
}
}