diff --git a/src/DigitalData.Auth.API/Controllers/AuthController.cs b/src/DigitalData.Auth.API/Controllers/AuthController.cs new file mode 100644 index 0000000..2d56599 --- /dev/null +++ b/src/DigitalData.Auth.API/Controllers/AuthController.cs @@ -0,0 +1,123 @@ +using DigitalData.Auth.API.Config; +using DigitalData.Core.Abstractions.Security; +using DigitalData.UserManager.Domain.Entities; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using System.Security.Claims; +using System.IdentityModel.Tokens.Jwt; +using DigitalData.UserManager.Application.DTOs.Auth; +using DigitalData.UserManager.Application.Contracts; +using DigitalData.UserManager.Application.DTOs.User; +using DigitalData.Core.Abstractions.Application; +using System.Net; + +namespace DigitalData.Auth.API.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class AuthController : ControllerBase + { + private readonly IJwtSignatureHandler _userSignatureHandler; + + private readonly AuthApiParams _apiParams; + + private readonly ICryptoFactory _cryptoFactory; + + private readonly ILogger _logger; + + private readonly IUserService _userService; + + private readonly IDirectorySearchService _dirSearchService; + + public AuthController(IJwtSignatureHandler userSignatureHandler, IOptions cookieParamsOptions, ICryptoFactory cryptoFactory, ILogger logger, IUserService userService, IDirectorySearchService dirSearchService) + { + _apiParams = cookieParamsOptions.Value; + _userSignatureHandler = userSignatureHandler; + _cryptoFactory = cryptoFactory; + _logger = logger; + _userService = userService; + _dirSearchService = dirSearchService; + } + + private async Task CreateTokenAsync(LogInDto login, string consumerRoute, bool cookie = true) + { + bool isValid = _dirSearchService.ValidateCredentials(login.Username, login.Password); + + if (!isValid) + return Unauthorized(); + + //find the user + var uRes = await _userService.ReadByUsernameAsync(login.Username); + if (!uRes.IsSuccess || uRes.Data is null) + { + return Unauthorized(); + } + + if (!_apiParams.Consumers.TryGetByRoute(consumerRoute, out var consumer)) + return Unauthorized(); + + _cryptoFactory.TokenDescriptors.TryGet(_apiParams.Issuer, consumer.Audience, out var descriptor); + + var token = _userSignatureHandler.WriteToken(uRes.Data, descriptor); + + //set cookie + if (cookie) + { + Response.Cookies.Append(_apiParams.CookieName, token, consumer.CookieOptions.Create(lifetime: descriptor.Lifetime)); + return Ok(); + } + else + return Ok(token); + } + + //TODO: Add role depends on group name + [HttpPost("~/{consumerRoute}/login")] + [AllowAnonymous] + public async Task Login([FromBody] LogInDto login, string consumerRoute) + { + try + { + return await CreateTokenAsync(login, consumerRoute, true); + } + catch (Exception ex) + { + _logger.LogError(ex, "{Message}", ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("logout")] + public IActionResult Logout() + { + try + { + Response.Cookies.Delete(_apiParams.CookieName); + return Ok(); + } + catch (Exception ex) + { + _logger.LogError(ex, "{Message}", ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpPost("{consumerRoute}")] + public async Task CreateTokenViaBody([FromBody] LogInDto login, [FromRoute] string consumerRoute, [FromQuery] bool cookie = false) + { + try + { + return await CreateTokenAsync(login, consumerRoute, cookie); + } + catch (Exception ex) + { + _logger.LogError(ex, "{Message}", ex.Message); + return StatusCode(StatusCodes.Status500InternalServerError); + } + } + + [HttpGet("check")] + [Authorize] + public IActionResult Check() => Ok(); + } +} \ No newline at end of file diff --git a/src/DigitalData.Auth.API/DigitalData.Auth.API.csproj b/src/DigitalData.Auth.API/DigitalData.Auth.API.csproj index 13c8bb4..a364375 100644 --- a/src/DigitalData.Auth.API/DigitalData.Auth.API.csproj +++ b/src/DigitalData.Auth.API/DigitalData.Auth.API.csproj @@ -12,11 +12,9 @@ - - - - - + + + diff --git a/src/DigitalData.Auth.API/Program.cs b/src/DigitalData.Auth.API/Program.cs index fb0a070..db2e58a 100644 --- a/src/DigitalData.Auth.API/Program.cs +++ b/src/DigitalData.Auth.API/Program.cs @@ -3,7 +3,8 @@ using DigitalData.Auth.API.Dto; using DigitalData.Auth.API.Services; using DigitalData.Core.Abstractions.Security; using DigitalData.Core.Security; -using DigitalData.UserManager.Domain.Entities; +using DigitalData.UserManager.Application; +using DigitalData.UserManager.Application.DTOs.User; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Tokens; @@ -27,7 +28,7 @@ builder.Services.AddJwtSignatureHandler(api => new Dictionary(user => new Dictionary +builder.Services.AddJwtSignatureHandler(user => new Dictionary { { JwtRegisteredClaimNames.Sub, user.Id }, { JwtRegisteredClaimNames.UniqueName, user.Id }, @@ -37,6 +38,10 @@ builder.Services.AddJwtSignatureHandler(user => new Dictionary>("ConsumerAPIs") ?? throw new InvalidOperationException($"No Consumer list found in {key} in configuration."); - services.AddSingleton(Options.Create(ConsumerApis)); + var consumerApis = configuration.GetValue>("ConsumerAPIs") ?? throw new InvalidOperationException($"No Consumer list found in {key} in configuration."); + services.AddSingleton(Options.Create(consumerApis)); services.AddSingleton(); return services; } diff --git a/src/DigitalData.Auth.API/appsettings.json b/src/DigitalData.Auth.API/appsettings.json index 06fd87a..f14524b 100644 --- a/src/DigitalData.Auth.API/appsettings.json +++ b/src/DigitalData.Auth.API/appsettings.json @@ -5,6 +5,9 @@ "Microsoft.AspNetCore": "Warning" } }, + "ConnectionStrings": { + "Default": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;" + }, "AllowedHosts": "*", "CryptParams": { "KeySizeInBits": 4096,