Skip to content

Commit d71cdf7

Browse files
feature(#22): enhance key rotation logic to ensure valid signing keys and prune outdated secrets
1 parent 329b899 commit d71cdf7

1 file changed

Lines changed: 20 additions & 3 deletions

File tree

Applications/Backend/Source/HttpsRichardy.Federation.WebApi/Workers/KeyRotationBackgroundService.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ namespace HttpsRichardy.Federation.WebApi.Workers;
22

33
public sealed class KeyRotationBackgroundService(IServiceScopeFactory scopeFactory, ILogger<KeyRotationBackgroundService> logger) : BackgroundService
44
{
5+
private static readonly TimeSpan _rotationInterval = TimeSpan.FromHours(24);
6+
57
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
68
{
79
while (!stoppingToken.IsCancellationRequested)
@@ -10,6 +12,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
1012

1113
var rotationService = scope.ServiceProvider.GetRequiredService<ISecretRotationService>();
1214
var realmCollection = scope.ServiceProvider.GetRequiredService<IRealmCollection>();
15+
var secretCollection = scope.ServiceProvider.GetRequiredService<ISecretCollection>();
1316

1417
var realms = await realmCollection.GetRealmsAsync(RealmFilters.WithoutFilters, stoppingToken);
1518

@@ -19,10 +22,24 @@ await Parallel.ForEachAsync(realms, stoppingToken, async (realm, cancellation) =
1922
{
2023
logger.LogInformation("rotating keys for realm {realm}", realm.Name);
2124

22-
/* !important: ensures the realm has at least one valid signing key before rotation */
2325
await rotationService.EnsureSecretExistsAsync(realm, cancellation);
2426

25-
await rotationService.RotateSecretAsync(realm, cancellation);
27+
var now = DateTime.UtcNow;
28+
var filters = SecretFilters.WithSpecifications()
29+
.WithRealm(realm.Id)
30+
.WithCanSign(now)
31+
.Build();
32+
33+
var secrets = await secretCollection.GetSecretsAsync(filters, cancellation);
34+
var current = secrets
35+
.OrderByDescending(secret => secret.CreatedAt)
36+
.FirstOrDefault();
37+
38+
if (current is not null && now - current.CreatedAt >= _rotationInterval)
39+
{
40+
await rotationService.RotateSecretAsync(realm, cancellation);
41+
}
42+
2643
await rotationService.PruneSecretsAsync(realm, cancellation);
2744
}
2845
catch (Exception exception)
@@ -31,7 +48,7 @@ await Parallel.ForEachAsync(realms, stoppingToken, async (realm, cancellation) =
3148
}
3249
});
3350

34-
await Task.Delay(TimeSpan.FromHours(24), stoppingToken);
51+
await Task.Delay(_rotationInterval, stoppingToken);
3552
}
3653
}
3754
}

0 commit comments

Comments
 (0)