feat(AuthController): Erstellt, um Token für Benutzer von UserManager bereitzustellen.

This commit is contained in:
Developer 02 2025-01-15 12:53:51 +01:00
parent 0a3e1566eb
commit a66570bebb
5 changed files with 138 additions and 9 deletions

View File

@ -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<UserReadDto> _userSignatureHandler;
private readonly AuthApiParams _apiParams;
private readonly ICryptoFactory _cryptoFactory;
private readonly ILogger<AuthController> _logger;
private readonly IUserService _userService;
private readonly IDirectorySearchService _dirSearchService;
public AuthController(IJwtSignatureHandler<UserReadDto> userSignatureHandler, IOptions<AuthApiParams> cookieParamsOptions, ICryptoFactory cryptoFactory, ILogger<AuthController> logger, IUserService userService, IDirectorySearchService dirSearchService)
{
_apiParams = cookieParamsOptions.Value;
_userSignatureHandler = userSignatureHandler;
_cryptoFactory = cryptoFactory;
_logger = logger;
_userService = userService;
_dirSearchService = dirSearchService;
}
private async Task<IActionResult> 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<IActionResult> 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<IActionResult> 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();
}
}

View File

@ -12,11 +12,9 @@
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.12" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.12" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.3.0" /> <PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.3.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="UserManager.Domain" Version="2.0.0" /> <PackageReference Include="UserManager.Application" Version="3.1.0" />
</ItemGroup> <PackageReference Include="UserManager.Domain" Version="3.0.0" />
<PackageReference Include="UserManager.Infrastructure" Version="3.0.0" />
<ItemGroup>
<Folder Include="Controllers\" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -3,7 +3,8 @@ using DigitalData.Auth.API.Dto;
using DigitalData.Auth.API.Services; using DigitalData.Auth.API.Services;
using DigitalData.Core.Abstractions.Security; using DigitalData.Core.Abstractions.Security;
using DigitalData.Core.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.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
@ -27,7 +28,7 @@ builder.Services.AddJwtSignatureHandler<ConsumerApi>(api => new Dictionary<strin
{ JwtRegisteredClaimNames.Sub, api.Name }, { JwtRegisteredClaimNames.Sub, api.Name },
{ JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds() } { JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds() }
}); });
builder.Services.AddJwtSignatureHandler<User>(user => new Dictionary<string, object> builder.Services.AddJwtSignatureHandler<UserReadDto>(user => new Dictionary<string, object>
{ {
{ JwtRegisteredClaimNames.Sub, user.Id }, { JwtRegisteredClaimNames.Sub, user.Id },
{ JwtRegisteredClaimNames.UniqueName, user.Id }, { JwtRegisteredClaimNames.UniqueName, user.Id },
@ -37,6 +38,10 @@ builder.Services.AddJwtSignatureHandler<User>(user => new Dictionary<string, obj
{ JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds() } { JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds() }
}); });
var cnn_str = builder.Configuration.GetConnectionString("Default") ?? throw new InvalidOperationException("Default connection string is not found.");
builder.Services.AddUserManager(cnn_str);
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();

View File

@ -8,8 +8,8 @@ namespace DigitalData.Auth.API.Services
{ {
public static IServiceCollection AddConsumerApiServiceFromConfiguration(this IServiceCollection services, IConfiguration configuration, string key = "ConsumerAPIs") public static IServiceCollection AddConsumerApiServiceFromConfiguration(this IServiceCollection services, IConfiguration configuration, string key = "ConsumerAPIs")
{ {
var ConsumerApis = configuration.GetValue<IEnumerable<ConsumerApi>>("ConsumerAPIs") ?? throw new InvalidOperationException($"No Consumer list found in {key} in configuration."); var consumerApis = configuration.GetValue<IEnumerable<ConsumerApi>>("ConsumerAPIs") ?? throw new InvalidOperationException($"No Consumer list found in {key} in configuration.");
services.AddSingleton(Options.Create(ConsumerApis)); services.AddSingleton(Options.Create(consumerApis));
services.AddSingleton<IConsumerApiService, ConfiguredConsumerApiService>(); services.AddSingleton<IConsumerApiService, ConfiguredConsumerApiService>();
return services; return services;
} }

View File

@ -5,6 +5,9 @@
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"ConnectionStrings": {
"Default": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"
},
"AllowedHosts": "*", "AllowedHosts": "*",
"CryptParams": { "CryptParams": {
"KeySizeInBits": 4096, "KeySizeInBits": 4096,