From efae188d5c415dc190f9547781a5e01129cf1774 Mon Sep 17 00:00:00 2001 From: Developer 02 Date: Thu, 23 Jan 2025 10:31:27 +0100 Subject: [PATCH] =?UTF-8?q?refactor(ConsumerService):=20Entfernt=20ReadLoc?= =?UTF-8?q?alAsync=20Methode.=20=20-=20LocalConsumer=20Eigenschaft=20in=20?= =?UTF-8?q?AuthApiParams=20hinzugef=C3=BCgt.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Config/AuthApiParams.cs | 32 +-------------- .../Config/ConfigExtensions.cs | 27 ------------- .../Config/DependentExtensions.cs | 39 ------------------- .../Controllers/AuthController.cs | 39 +++++++++++-------- .../Entities/CookieOptionsProvider.cs | 2 +- src/DigitalData.Auth.API/Program.cs | 10 ++--- .../Services/ConfiguredConsumerService.cs | 7 +--- .../Services/Contracts/IConsumerService.cs | 2 - src/DigitalData.Auth.API/appsettings.json | 25 ++++++------ .../consumer-repository.json | 8 +--- 10 files changed, 45 insertions(+), 146 deletions(-) delete mode 100644 src/DigitalData.Auth.API/Config/ConfigExtensions.cs delete mode 100644 src/DigitalData.Auth.API/Config/DependentExtensions.cs diff --git a/src/DigitalData.Auth.API/Config/AuthApiParams.cs b/src/DigitalData.Auth.API/Config/AuthApiParams.cs index a5e1076..b8c6c39 100644 --- a/src/DigitalData.Auth.API/Config/AuthApiParams.cs +++ b/src/DigitalData.Auth.API/Config/AuthApiParams.cs @@ -4,46 +4,18 @@ namespace DigitalData.Auth.API.Config { public class AuthApiParams { - private IEnumerable _consumers = new List(); - - public IEnumerable Consumers - { - get => _consumers; - init - { - _consumers = value; - for (int i = 0; i < _consumers.Count(); i++) - _consumers.ElementAt(i).Parent = this; - } - } - - public Consumer DefaultConsumer => Consumers.First(); - public CookieOptionsProvider DefaultCookieOptions { get; init; } = new() { HttpOnly = true, SameSite = SameSiteMode.Strict }; - public string CookieName { get; init; } = "AuthToken"; + public string DefaultCookieName { get; init; } = "AuthToken"; public required string Issuer { get; init; } public bool RequireHttpsMetadata { get; init; } = true; - public class Consumer - { - public required string Route { get; init; } - - public required string Audience { get; init; } - - private CookieOptionsProvider? _cookieOptions; - -#pragma warning disable CS8603 // Possible null reference return. - public CookieOptionsProvider CookieOptions { get => _cookieOptions ?? Parent?.DefaultCookieOptions; init => _cookieOptions = value; } -#pragma warning restore CS8603 // Possible null reference return. - - internal AuthApiParams? Parent { private get; set; } - } + public required Consumer LocalConsumer { get; init; } } } \ No newline at end of file diff --git a/src/DigitalData.Auth.API/Config/ConfigExtensions.cs b/src/DigitalData.Auth.API/Config/ConfigExtensions.cs deleted file mode 100644 index 6902410..0000000 --- a/src/DigitalData.Auth.API/Config/ConfigExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using static DigitalData.Auth.API.Config.AuthApiParams; - -namespace DigitalData.Auth.API.Config -{ - public static class ConfigExtensions -{ - public static Consumer? GetByAudience(this IEnumerable audiances, string name) => audiances.FirstOrDefault(a => a.Audience == name); - - public static Consumer? GetByRoute(this IEnumerable audiances, string route) => audiances.FirstOrDefault(a => a.Route == route); - - public static bool TryGetByAudience(this IEnumerable audiances, string audience, out Consumer audiance) - { -#pragma warning disable CS8601 // Possible null reference assignment. - audiance = audiances.GetByAudience(audience); -#pragma warning restore CS8601 // Possible null reference assignment. - return audiance is not null; - } - - public static bool TryGetByRoute(this IEnumerable audiances, string route, out Consumer audiance) - { -#pragma warning disable CS8601 // Possible null reference assignment. - audiance = audiances.SingleOrDefault(a => a.Route == route); -#pragma warning restore CS8601 // Possible null reference assignment. - return audiance is not null; - } - } -} \ No newline at end of file diff --git a/src/DigitalData.Auth.API/Config/DependentExtensions.cs b/src/DigitalData.Auth.API/Config/DependentExtensions.cs deleted file mode 100644 index 0bc6d3d..0000000 --- a/src/DigitalData.Auth.API/Config/DependentExtensions.cs +++ /dev/null @@ -1,39 +0,0 @@ -using DigitalData.Core.Abstractions.Security; -using Microsoft.Extensions.Options; - -namespace DigitalData.Auth.API.Config -{ - public static class DependentExtensions - { - private static AuthApiParams? _authApiParams; - - private static AuthApiParams AuthApiParams - { - get => _authApiParams - ?? throw new InvalidOperationException( - $"DependentExtensions have not been added to the application or are not configured correctly. {typeof(AuthApiParams)} cannot be provided." - ); - set => _authApiParams = value; - } - - public static IApplicationBuilder AddDependentExtensions(this IApplicationBuilder application) - { - var authApiParamOptions = application.ApplicationServices.GetRequiredService>(); - _authApiParams = authApiParamOptions.Value; - return application; - } - - public static bool TryGetByRoute(this IEnumerable descriptors, string consumerRoute, out IAsymmetricTokenDescriptor descriptor) - { -#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. - descriptor = null; -#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. - if (!AuthApiParams.Consumers.TryGetByRoute(consumerRoute, out var consumer) - || !descriptors.TryGet(AuthApiParams.Issuer, consumer.Audience, out var _descriptor)) - return false; - - descriptor = _descriptor; - return true; - } - } -} \ No newline at end of file diff --git a/src/DigitalData.Auth.API/Controllers/AuthController.cs b/src/DigitalData.Auth.API/Controllers/AuthController.cs index 1c3b8c0..ce3a6f3 100644 --- a/src/DigitalData.Auth.API/Controllers/AuthController.cs +++ b/src/DigitalData.Auth.API/Controllers/AuthController.cs @@ -45,7 +45,7 @@ namespace DigitalData.Auth.API.Controllers _consumerSignatureHandler = apiSignatureHandler; } - private async Task CreateTokenAsync(LogInDto login, string consumerRoute, bool cookie = true) + private async Task CreateTokenAsync(LogInDto login, string consumerName, bool cookie = true) { bool isValid = await _dirSearchService.ValidateCredentialsAsync(login.Username, login.Password); @@ -53,8 +53,12 @@ namespace DigitalData.Auth.API.Controllers return Unauthorized(); //find the user - var uRes = await _userService.ReadByUsernameAsync(login.Username); - if (uRes.IsFailed || !_apiParams.Consumers.TryGetByRoute(consumerRoute, out var consumer)) + var uRes = await _userService.ReadByUsernameAsync(login.Username); + if (uRes.IsFailed) + return Unauthorized(); + + var consumer = await _consumerService.ReadByNameAsync(consumerName); + if (consumer is null) return Unauthorized(); if (!_cryptoFactory.TokenDescriptors.TryGet(_apiParams.Issuer, consumer.Audience, out var descriptor)) @@ -65,7 +69,8 @@ namespace DigitalData.Auth.API.Controllers //set cookie if (cookie) { - Response.Cookies.Append(_apiParams.CookieName, token, consumer.CookieOptions.Create(lifetime: descriptor.Lifetime)); + var cookieOptions = consumer.CookieOptions ?? _apiParams.DefaultCookieOptions; + Response.Cookies.Append(_apiParams.DefaultCookieName, token, cookieOptions.Create(lifetime: descriptor.Lifetime)); return Ok(); } else @@ -74,20 +79,20 @@ namespace DigitalData.Auth.API.Controllers private async Task CreateTokenAsync(ConsumerLogin login, bool cookie = true) { - var api = await _consumerService.ReadByNameAsync(login.Name); - - if (api is null || api.Password != login.Password) + var consumer = await _consumerService.ReadByNameAsync(login.Name); + if (consumer is null || consumer.Password != login.Password) return Unauthorized(); - if (!_cryptoFactory.TokenDescriptors.TryGet(_apiParams.Issuer, _apiParams.DefaultConsumer.Audience, out var descriptor)) + if (!_cryptoFactory.TokenDescriptors.TryGet(_apiParams.Issuer, _apiParams.LocalConsumer.Audience, out var descriptor)) return StatusCode(StatusCodes.Status500InternalServerError); - var token = _consumerSignatureHandler.WriteToken(api, descriptor); + var token = _consumerSignatureHandler.WriteToken(consumer, descriptor); //set cookie if (cookie) { - Response.Cookies.Append(_apiParams.CookieName, token, _apiParams.DefaultConsumer.CookieOptions.Create(lifetime: descriptor.Lifetime)); + var cookieOptions = _apiParams.LocalConsumer.CookieOptions ?? _apiParams.DefaultCookieOptions; + Response.Cookies.Append(_apiParams.DefaultCookieName, token, cookieOptions.Create(lifetime: descriptor.Lifetime)); return Ok(); } else @@ -95,13 +100,13 @@ namespace DigitalData.Auth.API.Controllers } //TODO: Add role depends on group name - [HttpPost("{consumerRoute}/login")] + [HttpPost("{consumerName}/login")] [AllowAnonymous] - public async Task Login([FromForm] LogInDto login, [FromRoute] string consumerRoute) + public async Task Login([FromForm] LogInDto login, [FromRoute] string consumerName) { try { - return await CreateTokenAsync(login, consumerRoute, true); + return await CreateTokenAsync(login, consumerName, true); } catch (Exception ex) { @@ -130,7 +135,7 @@ namespace DigitalData.Auth.API.Controllers { try { - Response.Cookies.Delete(_apiParams.CookieName); + Response.Cookies.Delete(_apiParams.DefaultCookieName); return Ok(); } catch (Exception ex) @@ -140,12 +145,12 @@ namespace DigitalData.Auth.API.Controllers } } - [HttpPost("{consumerRoute}")] - public async Task CreateTokenViaBody([FromBody] LogInDto login, [FromRoute] string consumerRoute, [FromQuery] bool cookie = false) + [HttpPost("{consumerName}")] + public async Task CreateTokenViaBody([FromBody] LogInDto login, [FromRoute] string consumerName, [FromQuery] bool cookie = false) { try { - return await CreateTokenAsync(login, consumerRoute, cookie); + return await CreateTokenAsync(login, consumerName, cookie); } catch (Exception ex) { diff --git a/src/DigitalData.Auth.API/Entities/CookieOptionsProvider.cs b/src/DigitalData.Auth.API/Entities/CookieOptionsProvider.cs index 4d7d3ef..d53811d 100644 --- a/src/DigitalData.Auth.API/Entities/CookieOptionsProvider.cs +++ b/src/DigitalData.Auth.API/Entities/CookieOptionsProvider.cs @@ -68,6 +68,6 @@ public bool IsEssential { get => _optionsBase.IsEssential; set => _optionsBase.IsEssential = value; } #endregion - public CookieOptions Create() => new(_optionsBase) { Expires = DateTime.UtcNow.AddTicks(Lifetime.Ticks) }; + public CookieOptions Create(TimeSpan? lifetime = null) => new(_optionsBase) { Expires = DateTime.UtcNow.AddTicks(lifetime?.Ticks ?? Lifetime.Ticks) }; } } \ No newline at end of file diff --git a/src/DigitalData.Auth.API/Program.cs b/src/DigitalData.Auth.API/Program.cs index 5781083..b6d4b17 100644 --- a/src/DigitalData.Auth.API/Program.cs +++ b/src/DigitalData.Auth.API/Program.cs @@ -85,13 +85,13 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) { options.RequireHttpsMetadata = apiParams!.RequireHttpsMetadata; options.ClaimsIssuer = apiParams!.Issuer; - options.Audience = apiParams!.DefaultConsumer.Audience; + options.Audience = apiParams.LocalConsumer.Audience; options.TokenValidationParameters = new() { ValidateIssuer = true, ValidIssuer = apiParams!.Issuer, ValidateAudience = true, - ValidAudience = apiParams!.DefaultConsumer.Audience, + ValidAudience = apiParams.LocalConsumer.Audience, ValidateLifetime = true, IssuerSigningKey = issuerSigningKeyInitiator?.Value }; @@ -102,7 +102,7 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) { // if there is no token read related cookie if (context.Token is null // if there is no token - && context.Request.Cookies.TryGetValue(apiParams!.CookieName, out var token) // get token from cookies + && context.Request.Cookies.TryGetValue(apiParams!.DefaultCookieName, out var token) // get token from cookies && token is not null) context.Token = token; return Task.CompletedTask; @@ -112,12 +112,10 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) var app = builder.Build(); -app.AddDependentExtensions(); - issuerSigningKeyInitiator = new Lazy(() => { var factory = app.Services.GetRequiredService(); - var desc = factory.TokenDescriptors.Get(apiParams.Issuer, apiParams.DefaultConsumer.Audience); + var desc = factory.TokenDescriptors.Get(apiParams.Issuer, apiParams.LocalConsumer.Audience); return desc.Validator.SecurityKey; }); diff --git a/src/DigitalData.Auth.API/Services/ConfiguredConsumerService.cs b/src/DigitalData.Auth.API/Services/ConfiguredConsumerService.cs index 6652445..1cd9b72 100644 --- a/src/DigitalData.Auth.API/Services/ConfiguredConsumerService.cs +++ b/src/DigitalData.Auth.API/Services/ConfiguredConsumerService.cs @@ -9,20 +9,15 @@ namespace DigitalData.Auth.API.Services { private readonly IEnumerable _consumers; - private readonly AuthApiParams _authApiParams; - - public ConfiguredConsumerService(IOptions> consumeroptions, IOptions authApiParamOptions) + public ConfiguredConsumerService(IOptions> consumeroptions) { _consumers = consumeroptions.Value; - _authApiParams = authApiParamOptions.Value; } public Task ReadByIdAsync(int id) => Task.Run(() => _consumers.FirstOrDefault(api => api.Id == id)); public Task ReadByNameAsync(string name) => Task.Run(() => _consumers.FirstOrDefault(api => api.Name == name)); - public Task ReadLocalAsync() => Task.Run(() => _consumers.FirstOrDefault() ?? throw new InvalidOperationException("Unable to read the local consumer because no consumers are available.")); - public async Task VerifyAsync(string name, string password) => (await ReadByNameAsync(name))?.Password == password; } } \ No newline at end of file diff --git a/src/DigitalData.Auth.API/Services/Contracts/IConsumerService.cs b/src/DigitalData.Auth.API/Services/Contracts/IConsumerService.cs index a56e1e5..6d32d69 100644 --- a/src/DigitalData.Auth.API/Services/Contracts/IConsumerService.cs +++ b/src/DigitalData.Auth.API/Services/Contracts/IConsumerService.cs @@ -8,8 +8,6 @@ namespace DigitalData.Auth.API.Services.Contracts public Task ReadByNameAsync(string name); - public Task ReadLocalAsync(); - public Task VerifyAsync(string name, string password); } } \ No newline at end of file diff --git a/src/DigitalData.Auth.API/appsettings.json b/src/DigitalData.Auth.API/appsettings.json index 842a011..bdcbb4c 100644 --- a/src/DigitalData.Auth.API/appsettings.json +++ b/src/DigitalData.Auth.API/appsettings.json @@ -14,17 +14,13 @@ "ServerName": "DD-VMP01-DC01", "Root": "DC=dd-gan,DC=local,DC=digitaldata,DC=works" }, - "Consumers": [ - { - "Route": "api", - "Audience": "api.digitaldata.works" - }, - { - "Route": "work-flow", - "Audience": "work-flow.digitaldata.works" - } - ], "Issuer": "auth.digitaldata.works", + "LocalConsumer": { + "Id": -1, + "Name": "auth-flow", + "Audience": "auth.digitaldata.works", + "Password": "n7l^)s,v;jbr0c+x%urk=fak4[s==z?<" + }, "CryptParams": { "KeySizeInBits": 4096, "Padding": "OaepSHA512", @@ -38,7 +34,7 @@ { "Id": "4062504f-f081-43d1-b4ed-78256a0879e1", "Issuer": "auth.digitaldata.works", - "Audience": "api.digitaldata.works", + "Audience": "auth.digitaldata.works", "IsEncrypted": true, "Lifetime": "5:00:00" }, @@ -48,6 +44,13 @@ "Audience": "work-flow.digitaldata.works", "IsEncrypted": true, "Lifetime": "02:00:00" + }, + { + "Id": "9e3b0e68-c3e4-489e-b68c-47df26d6b612", + "Issuer": "auth.digitaldata.works", + "Audience": "user-manager.digitaldata.works", + "IsEncrypted": true, + "Lifetime": "02:00:00" } ] } diff --git a/src/DigitalData.Auth.API/consumer-repository.json b/src/DigitalData.Auth.API/consumer-repository.json index c17da48..6ab0df0 100644 --- a/src/DigitalData.Auth.API/consumer-repository.json +++ b/src/DigitalData.Auth.API/consumer-repository.json @@ -2,18 +2,12 @@ "Consumers": [ { "Id": 0, - "Name": "auth-flow", - "Audience": "auth.digitaldata.works", - "Password": "aQ9z!2@TgY7b#fHcD3pLmV1$wX" - }, - { - "Id": 1, "Name": "work-flow", "Audience": "work-flow.digitaldata.works", "Password": "t3B|aiJ'i-snLzNRj3B{9=&:lM5P@'iL" }, { - "Id": 2, + "Id": 1, "Name": "user-manager", "Audience": "user-manager.digitaldata.works", "Password": "a098Hvu1-y29ep{KPQO]#>8TK+fk{O`_d"