Compare commits

...

11 Commits

Author SHA1 Message Date
Developer 02
2aab942563 refactor(PlaceholderAuthController): AuthController umbenennen 2025-03-25 15:34:34 +01:00
Developer 02
054c91609e feat(AuthController): Aktiviere GetUserWithClaims Endpunkt 2025-03-25 14:15:31 +01:00
Developer 02
5c097eda80 Aktivierter Logout-Endpunkt 2025-03-25 14:00:19 +01:00
Developer 02
d228a3cd50 Aktiviert Endpunkt prüfen 2025-03-25 13:56:17 +01:00
Developer 02
0786013bd0 fix(AuthTokenKey): Update default audiance 2025-03-25 13:50:30 +01:00
Developer 02
beabc3f4e0 fix(appsettings): AuthCLientParms.Url 2025-03-25 13:32:24 +01:00
Developer 02
44560e7057 feat(Programm): Konfiguriert für Auth.API 2025-03-25 13:19:29 +01:00
Developer 02
2098a7d48d feat(LazyServiceProvider): Erstellt als Callback des Service Providers 2025-03-25 13:09:39 +01:00
Developer 02
140f172369 Ansprüche über ModelExtensipons konfigurieren 2025-03-25 13:07:06 +01:00
Developer 02
b3e7c1aa99 chore(API): Aktualisierung DigitalData.Auth.Client 1.3.5 2025-03-25 12:33:29 +01:00
Developer 02
0d4436b061 chore: aktualisiert, um sowohl .net 7 als auch .net 8 zu unterstützen 2025-03-25 11:52:03 +01:00
12 changed files with 243 additions and 55 deletions

View File

@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "9.0.3",
"commands": [
"dotnet-ef"
]
}
}
}

View File

@@ -0,0 +1,72 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using DigitalData.UserManager.Application.DTOs.Auth;
using DigitalData.UserManager.Application.Contracts;
using DigitalData.Core.DTO;
using Microsoft.Extensions.Localization;
using DigitalData.UserManager.Application;
using System.Security.Claims;
namespace DigitalData.UserManager.API.Controllers;
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly ILogger<UserController> _logger;
private readonly IUserService _userService;
private readonly IStringLocalizer<Resource> _localizer;
public AuthController(ILogger<UserController> logger, IUserService userService, IStringLocalizer<Resource> localizer)
{
_logger = logger;
_userService = userService;
_localizer = localizer;
}
[Authorize]
[HttpGet("check")]
public IActionResult CheckAuthentication() => Ok();
[AllowAnonymous]
[HttpPost("login")]
public Task<IActionResult> Login([FromBody] LogInDto login) => throw new NotImplementedException();
[Authorize]
[HttpGet("user")]
public async Task<IActionResult> GetUserWithClaims()
{
try
{
// Extract the username from the Name claim.
string? username = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
if (string.IsNullOrEmpty(username))
return Unauthorized();
return await _userService.ReadByUsernameAsync(username)
.ThenAsync(Ok, IActionResult (m, n) =>
{
_logger.LogNotice(n);
return NotFound(Result.Fail().Message(_localizer[Key.UserNotFound]));
});
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[Authorize]
[HttpPost("logout")]
public IActionResult Logout()
{
Response.Cookies.Delete("AuthToken", new()
{
Path = "/"
});
return Ok();
}
}

View File

@@ -1,27 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using DigitalData.UserManager.Application.DTOs.Auth;
namespace DigitalData.UserManager.API.Controllers;
[Route("api/Auth")]
[ApiController]
[Tags("Auth")]
public class PlaceholderAuthController : ControllerBase
{
[AllowAnonymous]
[HttpGet("check")]
public IActionResult CheckAuthentication() => throw new NotImplementedException();
[AllowAnonymous]
[HttpPost("login")]
public Task<IActionResult> Login([FromBody] LogInDto login) => throw new NotImplementedException();
[Authorize]
[HttpGet("user")]
public Task<IActionResult> GetUserWithClaims() => throw new NotImplementedException();
[Authorize]
[HttpPost("logout")]
public Task<IActionResult> Logout() => throw new NotImplementedException();
}

View File

@@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Version>4.0.0.0</Version>
<AssemblyVersion>4.0.0.0</AssemblyVersion>
<FileVersion>4.0.0.0</FileVersion>
<Version>6.0.0</Version>
<AssemblyVersion>6.0.0</AssemblyVersion>
<FileVersion>6.0.0</FileVersion>
</PropertyGroup>
<ItemGroup>
@@ -20,7 +20,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="DigitalData.Auth.Client" Version="1.3.3" />
<PackageReference Include="DigitalData.Auth.Client" Version="1.3.5" />
<PackageReference Include="DigitalData.Core.API" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.14" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.20" />

View File

@@ -0,0 +1,18 @@
namespace DigitalData.UserManager.API;
public class LazyServiceProvider : IServiceProvider
{
private Lazy<IServiceProvider>? _serviceProvider;
public Func<IServiceProvider> Factory
{
set => _serviceProvider = new(value);
}
public object? GetService(Type serviceType)
{
if (_serviceProvider is null)
throw new InvalidOperationException("GetService cannot be called before _serviceProvider is set.");
return _serviceProvider.Value.GetService(serviceType);
}
}

View File

@@ -0,0 +1,12 @@
namespace DigitalData.UserManager.API.Models;
public class AuthTokenKeys
{
public string Cookie { get; init; } = "AuthToken";
public string QueryString { get; init; } = "AuthToken";
public string Issuer { get; init; } = "auth.digitaldata.works";
public string Audience { get; init; } = "user-manager.digitaldata.works";
}

View File

@@ -0,0 +1,18 @@
using DigitalData.UserManager.Application.DTOs.User;
using System.Security.Claims;
namespace DigitalData.UserManager.API.Models;
public static class ModelExtensions
{
public static List<Claim> ToClaimList(this UserReadDto user) => new()
{
new (ClaimTypes.NameIdentifier, user.Id.ToString()),
new (ClaimTypes.Name, user.Username),
new (ClaimTypes.Surname, user.Name ?? ""),
new (ClaimTypes.GivenName, user.Prename ?? ""),
new (ClaimTypes.Email, user.Email ?? "")
};
public static Dictionary<string, object> ToClaimDictionary(this UserReadDto user) => user.ToClaimList().ToDictionary(claim => claim.Type, claim => (object)claim.Value);
}

View File

@@ -10,12 +10,21 @@ using DigitalData.UserManager.API.Controllers;
using DigitalData.UserManager.Application.Services;
using Microsoft.Data.SqlClient;
using Newtonsoft.Json;
using Microsoft.IdentityModel.Tokens;
using DigitalData.UserManager.Application.DTOs.User;
using DigitalData.UserManager.API.Models;
using DigitalData.Auth.Client;
using DigitalData.UserManager.API;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Options;
using DigitalData.Core.Abstractions.Security.Extensions;
using Microsoft.OpenApi.Models;
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger.Debug("init main");
try {
var builder = WebApplication.CreateBuilder(args);
var builder = WebApplication.CreateBuilder();
var config = builder.Configuration;
@@ -84,11 +93,90 @@ try {
builder.ConfigureBySection<DirectorySearchOptions>();
builder.Services.AddDirectorySearchService(config.GetSection("DirectorySearchOptions"));
builder.Services.AddJWTService<UserReadDto>(user => new SecurityTokenDescriptor()
{
Claims = user.ToClaimList().ToDictionary(claim => claim.Type, claim => claim.Value as object)
});
var lazyProvider = new LazyServiceProvider();
builder.Services.AddAuthHubClient(config.GetSection("AuthClientParams"));
var authTokenKeys = config.GetSection(nameof(AuthTokenKeys)).Get<AuthTokenKeys>() ?? new();
builder.Services
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(opt =>
{
opt.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKeyResolver = (token, securityToken, identifier, parameters) =>
{
var clientParams = lazyProvider.GetRequiredService<IOptions<ClientParams>>()?.Value;
var publicKey = clientParams!.PublicKeys.Get(authTokenKeys.Issuer, authTokenKeys.Audience);
return new List<SecurityKey>() { publicKey.SecurityKey };
},
ValidateIssuer = true,
ValidIssuer = authTokenKeys.Issuer,
ValidateAudience = true,
ValidAudience = authTokenKeys.Audience,
};
opt.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// if there is no token read related cookie or query string
if (context.Token is null) // if there is no token
{
if (context.Request.Cookies.TryGetValue(authTokenKeys.Cookie, out var cookieToken) && cookieToken is not null)
context.Token = cookieToken;
else if (context.Request.Query.TryGetValue(authTokenKeys.QueryString, out var queryStrToken))
context.Token = queryStrToken;
}
return Task.CompletedTask;
}
};
});
builder.Services.AddSwaggerGen(setupAct =>
{
setupAct.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "Bearer"
});
setupAct.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
});
builder.Services.AddCookieBasedLocalizer();
var app = builder.Build();
lazyProvider.Factory = () => app.Services;
cnn_str = new(() =>
{
var encryptor = app.Services.GetRequiredService<Encryptor>();

View File

@@ -11,9 +11,12 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<ExcludeApp_Data>false</ExcludeApp_Data>
<ProjectGuid>07ccd651-647c-49f7-9715-30cebc13710d</ProjectGuid>
<DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\DD-UserManager\Web\${Version}\$(Version).zip</DesktopBuildPackageLocation>
<DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\DD-UserManager\Web\$(Version)\$(Version).zip</DesktopBuildPackageLocation>
<PackageAsSingleFile>true</PackageAsSingleFile>
<DeployIisAppPath>UserManager.API</DeployIisAppPath>
<_TargetId>IISWebDeployPackage</_TargetId>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
</PropertyGroup>
</Project>

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<WebPublishMethod>Package</WebPublishMethod>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<ExcludeApp_Data>false</ExcludeApp_Data>
<ProjectGuid>07ccd651-647c-49f7-9715-30cebc13710d</ProjectGuid>
<DesktopBuildPackageLocation>E:\TekH\Visual Studio\src\DigitalData.UserManager.API.zip</DesktopBuildPackageLocation>
<PackageAsSingleFile>true</PackageAsSingleFile>
<DeployIisAppPath>UserManager</DeployIisAppPath>
<_TargetId>IISWebDeployPackage</_TargetId>
</PropertyGroup>
</Project>

View File

@@ -22,7 +22,7 @@
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchBrowser": false,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7103;http://localhost:5137",
"environmentVariables": {

View File

@@ -75,5 +75,16 @@
"DateTimeZoneHandling": "Local",
// Delete below in production
"UseEncryptor": true,
"UseSwagger": true
"UseSwagger": true,
"AuthClientParams": {
"Url": "https://localhost:7192/auth-hub",
"PublicKeys": [
{
"Issuer": "auth.digitaldata.works",
"Audience": "user-manager.digitaldata.works",
"Content": "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3QCd7dH/xOUITFZbitMa/xnh8a0LyL6ZBvSRAwkI9ceplTRSHJXoM1oB+xtjWE1kOuHVLe941Tm03szS4+/rHIm0Ejva/KKlv7sPFAHE/pWuoPS303vOHgI4HAFcuwywA8CghUWzaaK5LU/Hl8srWwxBHv5hKIUjJFJygeAIENvFOZ1gFbB3MPEC99PiPOwAmfl4tMQUmSsFyspl/RWVi7bTv26ZE+m3KPcWppmvmYjXlSitxRaySxnfFvpca/qWfd/uUUg2KWKtpAwWVkqr0qD9v3TyKSgHoGDsrFpwSx8qufUJSinmZ1u/0iKl6TXeHubYS4C4SUSVjOWXymI2ZQIDAQAB-----END PUBLIC KEY-----"
}
],
"RetryDelay": "00:00:05"
}
}