Skip to content

Commit 33857ca

Browse files
committed
feat: ensure the security feature is initialized before the app is used
1 parent 9fd014d commit 33857ca

6 files changed

Lines changed: 34 additions & 5 deletions

File tree

VirtualFinland.UserAPI/src/VirtualFinland.UsersAPI/Security/ApplicationSecurity.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ public class ApplicationSecurity : IApplicationSecurity
99
{
1010
private readonly ITermsOfServiceRepository _termsOfServiceRepository;
1111
private readonly SecuritySetup _setup;
12+
private readonly int _initializationTimeoutInMilliseconds = 10000;
13+
1214
public ApplicationSecurity(ITermsOfServiceRepository termsOfServiceRepository, SecuritySetup securitySetup)
1315
{
1416
_termsOfServiceRepository = termsOfServiceRepository;
@@ -27,7 +29,8 @@ public async Task<RequestAuthenticationCandinate> ParseJwtToken(string token)
2729
if (!tokenHandler.CanReadToken(token)) throw new NotAuthorizedException("The given token is not valid");
2830
var parsedToken = tokenHandler.ReadJwtToken(token);
2931

30-
// Resolve the security feature by token issuer (must be enabled) // @TODO: ensure the security feature is loaded before this
32+
// Resolve the security feature by token issuer (must be enabled)
33+
await EnsureSecurityInitializationCompleted();
3134
var tokenIssuer = parsedToken.Issuer;
3235
var securityFeature = _setup.Features.Find(o => o.Issuer == tokenIssuer) ?? throw new NotAuthorizedException("The given token issuer is not valid");
3336

@@ -49,4 +52,17 @@ public async Task VerifyPersonTermsOfServiceAgreement(Guid personId)
4952
// Fetch person terms of service agreement
5053
_ = await _termsOfServiceRepository.GetTermsOfServiceAgreementOfTheLatestTermsByPersonId(personId) ?? throw new NotAuthorizedException("User has not accepted the latest terms of service.");
5154
}
55+
56+
/// <summary>
57+
/// All the enabled security features must be initialized before the application can be used, verify that the initializations are completed
58+
/// </summary>
59+
private async Task EnsureSecurityInitializationCompleted()
60+
{
61+
var initializationTimeout = DateTime.Now.AddMilliseconds(_initializationTimeoutInMilliseconds);
62+
while (_setup.Features.Any(o => !o.IsInitialized))
63+
{
64+
if (DateTime.Now > initializationTimeout) throw new NotAuthorizedException("Security initialization timeout");
65+
await Task.Delay(100);
66+
}
67+
}
5268
}

VirtualFinland.UserAPI/src/VirtualFinland.UsersAPI/Security/Features/ISecurityFeature.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ public interface ISecurityFeature
1313
Task ValidateSecurityTokenAudience(string audience);
1414

1515
public string Issuer { get; }
16+
public bool IsInitialized { get; set; }
1617
}

VirtualFinland.UserAPI/src/VirtualFinland.UsersAPI/Security/Features/SecurityFeature.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,19 @@
1212

1313
namespace VirtualFinland.UserAPI.Security.Features;
1414

15-
public class SecurityFeature : ISecurityFeature
15+
public abstract class SecurityFeature : ISecurityFeature
1616
{
1717
///
1818
/// The issuer of the JWT token
1919
///
2020
public string Issuer => _issuer ?? throw new ArgumentNullException("Issuer is not set");
2121
protected string? _issuer;
2222

23+
/// <summary>
24+
/// Is the security feature initialized
25+
/// </summary>
26+
public bool IsInitialized { get; set; } = false;
27+
2328
/// <summary>
2429
/// Security feature options
2530
/// </summary>
@@ -171,13 +176,18 @@ protected virtual void ConfigureOpenIdConnect(AuthenticationBuilder authenticati
171176
/// </summary>
172177
protected virtual async void LoadOpenIdConfigUrl()
173178
{
174-
if (_openIDConfigurationURL == null) return;
179+
if (_openIDConfigurationURL == null)
180+
{
181+
IsInitialized = true;
182+
return;
183+
}
175184

176185
if (Options.IsOidcMetadataCachingEnabled && await CacheRepository.Exists(Constants.Cache.OpenIdConfigPrefix))
177186
{
178187
var cachedResult = await CacheRepository.Get<OpenIdConfiguration>(Constants.Cache.OpenIdConfigPrefix);
179188
_issuer = cachedResult.Issuer;
180189
_jwksOptionsUrl = cachedResult.JwksUri;
190+
IsInitialized = true;
181191
return;
182192
}
183193

@@ -205,6 +215,8 @@ protected virtual async void LoadOpenIdConfigUrl()
205215
Issuer = _issuer,
206216
JwksUri = _jwksOptionsUrl
207217
}, cacheDuration);
218+
219+
IsInitialized = true;
208220
}
209221

210222
break;

VirtualFinland.UserAPI/src/VirtualFinland.UsersAPI/Security/Features/TestbedSecurityFeature.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using VirtualFinland.UserAPI.Data.Repositories;
21
using VirtualFinland.UserAPI.Security.Models;
32

43
namespace VirtualFinland.UserAPI.Security.Features;

VirtualFinland.UsersAPI.UnitTests/Helpers/APITestBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ private UsersDbContext GetMemoryContext()
6666
new SecuritySetup()
6767
{
6868
Features = new List<ISecurityFeature>() {
69-
new SecurityFeature(
69+
new TestSecurityFeature(
7070
new SecurityFeatureOptions {
7171
Issuer = requestAuthenticationCandinate.Issuer,
7272
OpenIdConfigurationUrl = "test-openid-config-url",

VirtualFinland.UsersAPI.UnitTests/Helpers/TestSecurityFeature.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public class TestSecurityFeature : SecurityFeature
88
{
99
public TestSecurityFeature(SecurityFeatureOptions options, SecurityClientProviders securityClientProviders) : base(options, securityClientProviders)
1010
{
11+
IsInitialized = true;
1112
}
1213

1314
/// <summary>

0 commit comments

Comments
 (0)