5-improve-encrypted-storage (#6)

Added the use of DEK's for encryption of secrets. Both the KEK's and DEK's are stored in a way that you can have multiple key of which one is active. But the others are still available for decrypting. This allows for implementing key rotation.

Co-authored-by: eelke <eelke@eelkeklein.nl>
Co-authored-by: Eelke76 <31384324+Eelke76@users.noreply.github.com>
Reviewed-on: #6
This commit is contained in:
eelke 2026-02-27 17:57:42 +00:00
parent 138f335af0
commit 07393f57fc
87 changed files with 1903 additions and 533 deletions

View file

@ -0,0 +1,21 @@
using IdentityShroud.Core.Contracts;
using IdentityShroud.Core.Model;
namespace IdentityShroud.Api;
public class ClientIdValidationFilter(IClientService clientService) : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{
Guid realmId = context.Arguments.OfType<Guid>().First();
int id = context.Arguments.OfType<int>().First();
Client? client = await clientService.FindById(realmId, id, context.HttpContext.RequestAborted);
if (client is null)
{
return Results.NotFound();
}
context.HttpContext.Items["ClientEntity"] = client;
return await next(context);
}
}

View file

@ -0,0 +1,20 @@
using IdentityShroud.Core.Contracts;
using IdentityShroud.Core.Model;
namespace IdentityShroud.Api;
public class RealmIdValidationFilter(IRealmService realmService) : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{
Guid id = context.Arguments.OfType<Guid>().First();
Realm? realm = await realmService.FindById(id, context.HttpContext.RequestAborted);
if (realm is null)
{
return Results.NotFound();
}
context.HttpContext.Items["RealmEntity"] = realm;
return await next(context);
}
}

View file

@ -1,5 +1,5 @@
using IdentityShroud.Core.Contracts;
using IdentityShroud.Core.Model;
using IdentityShroud.Core.Services;
namespace IdentityShroud.Api;
@ -9,12 +9,13 @@ namespace IdentityShroud.Api;
/// consistently.
/// </summary>
/// <param name="realmService"></param>
public class SlugValidationFilter(IRealmService realmService) : IEndpointFilter
public class RealmSlugValidationFilter(IRealmService realmService) : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{
string slug = context.Arguments.OfType<string>().First();
Realm? realm = await realmService.FindBySlug(slug);
string realmSlug = context.Arguments.OfType<string>().FirstOrDefault()
?? throw new InvalidOperationException("Expected argument missing, ensure you include path parameters in your handlers signature even when you don't use them");
Realm? realm = await realmService.FindBySlug(realmSlug, context.HttpContext.RequestAborted);
if (realm is null)
{
return Results.NotFound();