diff --git a/src/HealthChecks.OpenIdConnectServer/DependencyInjection/IdSvrHealthCheckBuilderExtensions.cs b/src/HealthChecks.OpenIdConnectServer/DependencyInjection/IdSvrHealthCheckBuilderExtensions.cs
index cad4e41af8..b7e47b8750 100644
--- a/src/HealthChecks.OpenIdConnectServer/DependencyInjection/IdSvrHealthCheckBuilderExtensions.cs
+++ b/src/HealthChecks.OpenIdConnectServer/DependencyInjection/IdSvrHealthCheckBuilderExtensions.cs
@@ -79,4 +79,77 @@ public static IHealthChecksBuilder AddIdentityServer(
tags,
timeout));
}
+
+ ///
+ /// Add a health check for Identity Server.
+ ///
+ /// The .
+ /// The uri of the Identity Server to check.
+ /// Identity Server discover configuration segment.
+ /// The signature algorithms that the identity server must support.
+ /// The health check name. Optional. If null the type name 'idsvr' will be used for the name.
+ ///
+ /// The that should be reported when the health check fails. Optional. If null then
+ /// the default status of will be reported.
+ ///
+ /// A list of tags that can be used to filter sets of health checks. Optional.
+ /// An optional representing the timeout of the check.
+ /// The specified .
+ public static IHealthChecksBuilder AddIdentityServer(
+ this IHealthChecksBuilder builder,
+ Uri idSvrUri,
+ string[] requiredSigningAlgorithms,
+ string discoverConfigurationSegment = IDSVR_DISCOVER_CONFIGURATION_SEGMENT,
+ string? name = default,
+ HealthStatus? failureStatus = default,
+ IEnumerable? tags = default,
+ TimeSpan? timeout = default)
+ {
+ var registrationName = name ?? NAME;
+
+ builder.Services.AddHttpClient(registrationName, client => client.BaseAddress = idSvrUri);
+
+ return builder.Add(new HealthCheckRegistration(
+ registrationName,
+ sp => new IdSvrHealthCheck(() => sp.GetRequiredService().CreateClient(registrationName), discoverConfigurationSegment, requiredSigningAlgorithms),
+ failureStatus,
+ tags,
+ timeout));
+ }
+
+ ///
+ /// Add a health check for Identity Server.
+ ///
+ /// The .
+ /// Factory for providing the uri of the Identity Server to check.
+ /// Identity Server discover configuration segment.
+ /// The signature algorithms that the identity server must support.
+ /// The health check name. Optional. If null the type name 'idsvr' will be used for the name.
+ ///
+ /// The that should be reported when the health check fails. Optional. If null then
+ /// the default status of will be reported.
+ /// A list of tags that can be used to filter sets of health checks. Optional.
+ /// An optional representing the timeout of the check.
+ /// The specified .
+ public static IHealthChecksBuilder AddIdentityServer(
+ this IHealthChecksBuilder builder,
+ Func uriProvider,
+ string[] requiredSigningAlgorithms,
+ string discoverConfigurationSegment = IDSVR_DISCOVER_CONFIGURATION_SEGMENT,
+ string? name = null,
+ HealthStatus? failureStatus = null,
+ IEnumerable? tags = null,
+ TimeSpan? timeout = null)
+ {
+ var registrationName = name ?? NAME;
+
+ builder.Services.AddHttpClient(registrationName, (sp, client) => client.BaseAddress = uriProvider(sp));
+
+ return builder.Add(new HealthCheckRegistration(
+ registrationName,
+ sp => new IdSvrHealthCheck(() => sp.GetRequiredService().CreateClient(registrationName), discoverConfigurationSegment, requiredSigningAlgorithms),
+ failureStatus,
+ tags,
+ timeout));
+ }
}
diff --git a/src/HealthChecks.OpenIdConnectServer/DiscoveryEndpointResponse.cs b/src/HealthChecks.OpenIdConnectServer/DiscoveryEndpointResponse.cs
index b34427e943..063c2667cc 100644
--- a/src/HealthChecks.OpenIdConnectServer/DiscoveryEndpointResponse.cs
+++ b/src/HealthChecks.OpenIdConnectServer/DiscoveryEndpointResponse.cs
@@ -25,7 +25,7 @@ internal class DiscoveryEndpointResponse
///
/// Validates Discovery response according to the OpenID specification
///
- public void ValidateResponse()
+ public void ValidateResponse(string[]? requiredAlgorithms = null)
{
ValidateValue(Issuer, OidcConstants.ISSUER);
ValidateValue(AuthorizationEndpoint, OidcConstants.AUTHORIZATION_ENDPOINT);
@@ -37,7 +37,7 @@ public void ValidateResponse()
// but some identity providers (f.e. Identity Server and Azure AD) return 'id_token token'
ValidateOneOfRequiredValues(ResponseTypesSupported, OidcConstants.RESPONSE_TYPES_SUPPORTED, OidcConstants.REQUIRED_COMBINED_RESPONSE_TYPES);
ValidateOneOfRequiredValues(SubjectTypesSupported, OidcConstants.SUBJECT_TYPES_SUPPORTED, OidcConstants.REQUIRED_SUBJECT_TYPES);
- ValidateRequiredValues(SigningAlgorithmsSupported, OidcConstants.ALGORITHMS_SUPPORTED, OidcConstants.REQUIRED_ALGORITHMS);
+ ValidateOneOfRequiredValues(SigningAlgorithmsSupported, OidcConstants.ALGORITHMS_SUPPORTED, requiredAlgorithms ?? OidcConstants.REQUIRED_ALGORITHMS);
}
private static void ValidateValue(string value, string metadata)
diff --git a/src/HealthChecks.OpenIdConnectServer/IdSvrHealthCheck.cs b/src/HealthChecks.OpenIdConnectServer/IdSvrHealthCheck.cs
index 62bafadf77..9141522ee1 100644
--- a/src/HealthChecks.OpenIdConnectServer/IdSvrHealthCheck.cs
+++ b/src/HealthChecks.OpenIdConnectServer/IdSvrHealthCheck.cs
@@ -8,16 +8,24 @@ public class IdSvrHealthCheck : IHealthCheck
{
private readonly Func _httpClientFactory;
private readonly string _discoverConfigurationSegment;
+ private readonly string[]? _requiredAlgorithms;
public IdSvrHealthCheck(Func httpClientFactory)
- : this(httpClientFactory, IdSvrHealthCheckBuilderExtensions.IDSVR_DISCOVER_CONFIGURATION_SEGMENT)
+ : this(httpClientFactory, IdSvrHealthCheckBuilderExtensions.IDSVR_DISCOVER_CONFIGURATION_SEGMENT, OidcConstants.REQUIRED_ALGORITHMS)
{
}
-
public IdSvrHealthCheck(Func httpClientFactory, string discoverConfigurationSegment)
{
_httpClientFactory = Guard.ThrowIfNull(httpClientFactory);
_discoverConfigurationSegment = discoverConfigurationSegment;
+ _requiredAlgorithms = null;
+ }
+
+ public IdSvrHealthCheck(Func httpClientFactory, string discoverConfigurationSegment, string[] requiredAlgorithms)
+ {
+ _httpClientFactory = Guard.ThrowIfNull(httpClientFactory);
+ _discoverConfigurationSegment = discoverConfigurationSegment;
+ _requiredAlgorithms = requiredAlgorithms;
}
///
@@ -39,7 +47,7 @@ public async Task CheckHealthAsync(HealthCheckContext context
.ConfigureAwait(false)
?? throw new ArgumentException("Could not deserialize to discovery endpoint response!");
- discoveryResponse.ValidateResponse();
+ discoveryResponse.ValidateResponse(_requiredAlgorithms);
return HealthCheckResult.Healthy();
}
diff --git a/src/HealthChecks.OpenIdConnectServer/OidcConstants.cs b/src/HealthChecks.OpenIdConnectServer/OidcConstants.cs
index 8df2c8fdab..cc5dd12f9c 100644
--- a/src/HealthChecks.OpenIdConnectServer/OidcConstants.cs
+++ b/src/HealthChecks.OpenIdConnectServer/OidcConstants.cs
@@ -20,5 +20,5 @@ internal class OidcConstants
internal static string[] REQUIRED_SUBJECT_TYPES => new[] { "pairwise", "public" };
- internal static string[] REQUIRED_ALGORITHMS => new[] { "RS256" };
+ internal static string[] REQUIRED_ALGORITHMS => new[] { "RS256", "RS512" };
}
diff --git a/test/HealthChecks.OpenIdConnectServer.Tests/DependencyInjection/RegistrationTests.cs b/test/HealthChecks.OpenIdConnectServer.Tests/DependencyInjection/RegistrationTests.cs
index a6ab2c74bc..1dc3687f2a 100644
--- a/test/HealthChecks.OpenIdConnectServer.Tests/DependencyInjection/RegistrationTests.cs
+++ b/test/HealthChecks.OpenIdConnectServer.Tests/DependencyInjection/RegistrationTests.cs
@@ -66,4 +66,36 @@ public void add_named_health_check_when_properly_configured_with_uri_provider()
registration.Name.ShouldBe("my-idsvr-group");
check.ShouldBeOfType();
}
+ [Fact]
+ public void add_health_check_when_properly_configured_with_requiredSigningAlgorithms()
+ {
+ var services = new ServiceCollection();
+ services.AddHealthChecks()
+ .AddIdentityServer(new Uri("http://myidsvr"), requiredSigningAlgorithms: ["RS256"]);
+
+ using var serviceProvider = services.BuildServiceProvider();
+ var options = serviceProvider.GetRequiredService>();
+
+ var registration = options.Value.Registrations.First();
+ var check = registration.Factory(serviceProvider);
+
+ registration.Name.ShouldBe("idsvr");
+ check.ShouldBeOfType();
+ }
+ [Fact]
+ public void add_health_check_when_properly_configured_with_uri_provider_and_requiredSigningAlgorithms()
+ {
+ var services = new ServiceCollection();
+ services.AddHealthChecks()
+ .AddIdentityServer(sp => new Uri("http://myidsvr"), requiredSigningAlgorithms: ["RS256"]);
+
+ using var serviceProvider = services.BuildServiceProvider();
+ var options = serviceProvider.GetRequiredService>();
+
+ var registration = options.Value.Registrations.First();
+ var check = registration.Factory(serviceProvider);
+
+ registration.Name.ShouldBe("idsvr");
+ check.ShouldBeOfType();
+ }
}
diff --git a/test/HealthChecks.OpenIdConnectServer.Tests/Functional/DiscoveryEndpointResponseTests.cs b/test/HealthChecks.OpenIdConnectServer.Tests/Functional/DiscoveryEndpointResponseTests.cs
index 7752b8b395..1ecd2cd765 100644
--- a/test/HealthChecks.OpenIdConnectServer.Tests/Functional/DiscoveryEndpointResponseTests.cs
+++ b/test/HealthChecks.OpenIdConnectServer.Tests/Functional/DiscoveryEndpointResponseTests.cs
@@ -129,7 +129,69 @@ public void be_invalid_when_required_id_token_signing_alg_values_supported_is_mi
validate
.ShouldThrow()
- .Message.ShouldBe("Invalid discovery response - 'id_token_signing_alg_values_supported' must contain the following values: RS256!");
+ .Message.ShouldBe($"Invalid discovery response - 'id_token_signing_alg_values_supported' must be one of the following values: {string.Join(",", OidcConstants.REQUIRED_ALGORITHMS)}!");
+ }
+
+ [Fact]
+ public void be_invalid_when_custom_required_id_token_signing_alg_values_supported_is_wrong()
+ {
+ string[] requiredSigningAlgorithms = { "ES512" };
+
+ var response = new DiscoveryEndpointResponse
+ {
+ Issuer = RandomString,
+ AuthorizationEndpoint = RandomString,
+ JwksUri = RandomString,
+ ResponseTypesSupported = REQUIRED_RESPONSE_TYPES,
+ SubjectTypesSupported = OidcConstants.REQUIRED_SUBJECT_TYPES,
+ SigningAlgorithmsSupported = new[] { "RS256" },
+ };
+
+ Action validate = () => response.ValidateResponse(requiredSigningAlgorithms);
+
+ validate
+ .ShouldThrow()
+ .Message.ShouldBe($"Invalid discovery response - 'id_token_signing_alg_values_supported' must be one of the following values: {string.Join(",", requiredSigningAlgorithms)}!");
+ }
+
+ [Theory]
+ [InlineData("RS256")]
+ [InlineData("RS512")]
+ public void be_valid_when_one_required_id_token_signing_alg_value_is_provided(string supportedSigningAlgorithm)
+ {
+ var response = new DiscoveryEndpointResponse
+ {
+ Issuer = RandomString,
+ AuthorizationEndpoint = RandomString,
+ JwksUri = RandomString,
+ ResponseTypesSupported = REQUIRED_RESPONSE_TYPES,
+ SubjectTypesSupported = OidcConstants.REQUIRED_SUBJECT_TYPES,
+ SigningAlgorithmsSupported = new[] { supportedSigningAlgorithm },
+ };
+
+ Action validate = () => response.ValidateResponse();
+
+ validate.ShouldNotThrow();
+ }
+
+ [Fact]
+ public void be_valid_when_custom_required_id_token_signing_alg_value_is_provided()
+ {
+ string[] requiredSigningAlgorithms = { "ES512" };
+
+ var response = new DiscoveryEndpointResponse
+ {
+ Issuer = RandomString,
+ AuthorizationEndpoint = RandomString,
+ JwksUri = RandomString,
+ ResponseTypesSupported = REQUIRED_RESPONSE_TYPES,
+ SubjectTypesSupported = OidcConstants.REQUIRED_SUBJECT_TYPES,
+ SigningAlgorithmsSupported = new[] { "ES512" },
+ };
+
+ Action validate = () => response.ValidateResponse(requiredSigningAlgorithms);
+
+ validate.ShouldNotThrow();
}
[Fact]