Compare commits

...

5 Commits

Author SHA1 Message Date
Developer 02
74444d301d feat(ClientPublicKey): implementiert IUniqueSecurityContext 2025-03-07 16:11:36 +01:00
Developer 02
2378b93579 chore(Client): Hochgestuft auf 1.1.3 2025-03-07 15:22:23 +01:00
Developer 02
85a047467e refactor: AsymmetricPublicKey durch ClientPublicKey ersetzt und RSA-Schlüsselverwaltung verbessert
- `AsymmetricPublicKey` in `ClientPublicKey` umbenannt
- `ClientPublicKey` von `RSAKeyBase` abgeleitet für RSA-Funktionalität
- Dynamischen PEM-Import in `UpdateContent` hinzugefügt
2025-03-07 15:21:31 +01:00
Developer 02
106d31b068 feat(DIExtensions): Optionale IConfiguration-Eingabe hinzugefügt, um AddAuthHubClient mit Hilfe von Appsettings konfigurieren zu können 2025-03-07 13:25:40 +01:00
Developer 02
48f5c69c91 fix(ClientParams): Umbenennung von OnMessageReceived in OnPublicKeyReceived 2025-03-07 12:52:21 +01:00
9 changed files with 65 additions and 36 deletions

View File

@ -1,14 +0,0 @@
using DigitalData.Core.Abstractions.Security;
namespace DigitalData.Auth.Client;
public class AsymmetricPublicKey : IUniqueSecurityContext, IAsymmetricPublicKey
{
public required string Issuer { get; init; }
public required string Audience { get; init; }
public string? Id { get; init; }
public string Content { get; internal set; } = string.Empty;
}

View File

@ -34,7 +34,7 @@ public class AuthClient : IAuthClient, IAsyncDisposable
public bool IsConnected { get; private set; } = false;
public IEnumerable<AsymmetricPublicKey> PublicKeys => _params.PublicKeys;
public IEnumerable<ClientPublicKey> PublicKeys => _params.PublicKeys;
public async Task StartAsync()
{
@ -57,7 +57,7 @@ public class AuthClient : IAuthClient, IAsyncDisposable
}
}
public Task ReceivePublicKeyAsync(string issuer, string audience, string message) => Task.Run(() => _params.TriggerOnMessageReceived(this, issuer, audience, message, _logger));
public Task ReceivePublicKeyAsync(string issuer, string audience, string message) => Task.Run(() => _params.TriggerOnPublicReceivedEvent(this, issuer, audience, message, _logger));
public Task SendPublicKeyAsync(string issuer, string audience, string message) => _connection.InvokeAsync(nameof(SendPublicKeyAsync), issuer, audience, message);

View File

@ -10,7 +10,7 @@ public static class ClientEvents
public static readonly ClientEvent UpdatePublicKeys = (client, issuer, audience, content, logger) =>
{
if(client.PublicKeys.TryGet(issuer, audience, out var publicKey))
publicKey.Content = content;
publicKey.UpdateContent(content);
else
logger?.LogWarning(
"Failed to update public key: No matching key found. Issuer: {Issuer}, Audience: {Audience}. Ensure the key exists before attempting an update.", issuer, audience);

View File

@ -1,5 +1,4 @@
using DigitalData.Auth.Client;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Logging;
namespace DigitalData.Auth.Client;
@ -25,10 +24,10 @@ public class ClientParams
/// </summary>
public TimeSpan? RetryDelay { get; set; }
public event ClientEvent OnMessageReceived = ClientEvents.UpdatePublicKeys;
public event ClientEvent OnPublicKeyReceived = ClientEvents.UpdatePublicKeys;
internal void TriggerOnMessageReceived(AuthClient client, string issuer, string audience, string key, ILogger? logger = null)
=> OnMessageReceived(client, issuer, audience, key, logger);
internal void TriggerOnPublicReceivedEvent(AuthClient client, string issuer, string audience, string key, ILogger? logger = null)
=> OnPublicKeyReceived(client, issuer, audience, key, logger);
public ClientParams()
{
@ -42,5 +41,5 @@ public class ClientParams
});
}
public List<AsymmetricPublicKey> PublicKeys { get; init; } = new();
public List<ClientPublicKey> PublicKeys { get; set; } = new();
}

View File

@ -0,0 +1,39 @@
using DigitalData.Core.Abstractions.Security;
using DigitalData.Core.Security.RSAKey;
using Microsoft.IdentityModel.Tokens;
using System.Security.Cryptography;
namespace DigitalData.Auth.Client;
/// <summary>
/// Represents a public RSA key, allowing dynamic updates and PEM import functionality.
/// </summary>
public class ClientPublicKey : RSAKeyBase, IAsymmetricTokenValidator, IUniqueSecurityContext
{
public required string Issuer { get; init; }
public required string Audience { get; init; }
private string _content = string.Empty;
public override string Content
{
get
{
return _content;
}
init
{
UpdateContent(value);
}
}
public void UpdateContent(string content)
{
_content = content;
RSA.ImportFromPem(content);
SecurityKey = new RsaSecurityKey(RSA);
}
public SecurityKey SecurityKey { get; private set; } = new RsaSecurityKey(RSA.Create());
}

View File

@ -1,17 +1,21 @@
using DigitalData.Auth.Abstractions;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace DigitalData.Auth.Client;
public static class DIExtensions
{
public static IServiceCollection AddAuthHubClient(this IServiceCollection services, Action<ClientParams> options)
public static IServiceCollection AddAuthHubClient(this IServiceCollection services, IConfiguration? configuration = null, Action<ClientParams>? options = null)
{
var clientParams = configuration?.GetSection(nameof(ClientParams)).Get<ClientParams>() ?? new ClientParams();
options?.Invoke(clientParams);
services
.Configure(options)
.AddSingleton<IAuthClient, AuthClient>()
.AddSingleton(Options.Create(clientParams))
.AddSingleton<IAuthClient, AuthClient>()
.TryAddSingleton<HubConnectionBuilder>();
return services;

View File

@ -5,7 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackageId>DigitalData.Auth.Client</PackageId>
<Version>1.1.0</Version>
<Version>1.1.4.1</Version>
<Description>DigitalData.Auth.Client is a SignalR-based authentication client that enables applications to connect to a central authentication hub for real-time message exchange. It provides seamless connection management, automatic reconnection (RetryPolicy), and event-driven communication (ClientEvents). The package includes dependency injection support via DIExtensions, allowing easy integration into ASP.NET Core applications. With built-in retry policies and secure message handling, it ensures a reliable and scalable authentication client for real-time authentication workflows.</Description>
<Company>Digital Data GmbH</Company>
<Product>Digital Data GmbH</Product>
@ -14,8 +14,8 @@
<PackageIcon>auth_icon.png</PackageIcon>
<RepositoryUrl>http://git.dd:3000/AppStd/DigitalData.Auth</RepositoryUrl>
<PackageTags>Digital Data Auth Authorization Authentication</PackageTags>
<AssemblyVersion>1.1.0</AssemblyVersion>
<FileVersion>1.1.0</FileVersion>
<AssemblyVersion>1.1.4.1</AssemblyVersion>
<FileVersion>1.1.4.1</FileVersion>
</PropertyGroup>
<ItemGroup>
@ -27,6 +27,7 @@
<ItemGroup>
<PackageReference Include="DigitalData.Core.Abstractions" Version="3.3.0" />
<PackageReference Include="DigitalData.Core.Security" Version="1.0.0" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.1" />
</ItemGroup>

View File

@ -40,7 +40,7 @@ public class AuthHubTests
Build = options =>
{
var provider = new ServiceCollection()
.AddAuthHubClient(options)
.AddAuthHubClient(options: options)
.BuildServiceProvider();
_disposableAsync.Enqueue(provider);
@ -98,7 +98,7 @@ public class AuthHubTests
var provider_receiver = Build(opt =>
{
opt.Url = _hubUrl;
opt.OnMessageReceived += (client, issuer, audience, key, logger) =>
opt.OnPublicKeyReceived += (client, issuer, audience, key, logger) =>
{
rcv_issuer = issuer;
rcv_audience = audience;

View File

@ -85,7 +85,7 @@ public class AuthClientTests
Build = options =>
{
var provider = new ServiceCollection()
.AddAuthHubClient(options)
.AddAuthHubClient(options: options)
.BuildServiceProvider();
_disposableAsync.Enqueue(provider);
@ -143,7 +143,7 @@ public class AuthClientTests
var provider_receiver = Build(opt =>
{
opt.Url = _hubUrl;
opt.OnMessageReceived += (client, issuer, audience, key, logger) =>
opt.OnPublicKeyReceived += (client, issuer, audience, key, logger) =>
{
rcv_issuer = issuer;
rcv_audience = audience;
@ -180,7 +180,7 @@ public class AuthClientTests
var provider = Build(opt =>
{
opt.Url = _hubUrl;
opt.OnMessageReceived += (client, issuer, audience, key, logger) => publicKey = key;
opt.OnPublicKeyReceived += (client, issuer, audience, key, logger) => publicKey = key;
});
var client = provider.GetRequiredService<IAuthClient>();
await client.StartAsync();
@ -206,7 +206,7 @@ public class AuthClientTests
public async Task StartAsync_ShouldUpdateAllPublicKey()
{
// Arrange
var publicKey = new AsymmetricPublicKey() { Issuer = "Foo", Audience = "Bar" };
var publicKey = new ClientPublicKey() { Issuer = "Foo", Audience = "Bar" };
var provider = Build(opt =>
{
opt.Url = _hubUrl;
@ -229,7 +229,7 @@ public class AuthClientTests
public async Task Reconnected_ShouldUpdateAllPublicKey()
{
// Arrange
var publicKey = new AsymmetricPublicKey() { Issuer = "Foo", Audience = "Bar" };
var publicKey = new ClientPublicKey() { Issuer = "Foo", Audience = "Bar" };
var provider = Build(opt =>
{
opt.Url = _hubUrl;