@@ -2,6 +2,8 @@ namespace HttpsRichardy.Federation.WebApi.Workers;
22
33public 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