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 _userSignatureHandler; private readonly IJwtSignatureHandler _consumerSignatureHandler; private readonly AuthApiParams _apiParams; private readonly ICryptoFactory _cryptoFactory; private readonly ILogger _logger; private readonly IUserService _userService; private readonly IDirectorySearchService _dirSearchService; private readonly IConsumerService _consumerService; public AuthController(IJwtSignatureHandler userSignatureHandler, IOptions cookieParamsOptions, ICryptoFactory cryptoFactory, ILogger logger, IUserService userService, IDirectorySearchService dirSearchService, IConsumerService consumerService, IJwtSignatureHandler apiSignatureHandler) { _apiParams = cookieParamsOptions.Value; _userSignatureHandler = userSignatureHandler; _cryptoFactory = cryptoFactory; _logger = logger; _userService = userService; _dirSearchService = dirSearchService; _consumerService = consumerService; _consumerSignatureHandler = apiSignatureHandler; } private async Task CreateTokenAsync(UserLogin login, string consumerName, bool cookie = true) { DataResult? 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 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 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 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 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 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(); } }