From b3c0d886a7cf8b3cbce92b12ea562dae12526762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Sun, 17 Sep 2017 04:58:40 +0200 Subject: [PATCH] Introduce OpenIddictOptions.Claims/OpenIddictBuilder.RegisterClaims() --- src/OpenIddict/OpenIddictExtensions.cs | 28 ++++++++++++++++ src/OpenIddict/OpenIddictOptions.cs | 5 +++ .../OpenIddictProvider.Discovery.cs | 5 +++ .../OpenIddictExtensionsTests.cs | 19 ++++++++++- .../OpenIddictProviderTests.Discovery.cs | 33 +++++++++++++++++++ 5 files changed, 89 insertions(+), 1 deletion(-) diff --git a/src/OpenIddict/OpenIddictExtensions.cs b/src/OpenIddict/OpenIddictExtensions.cs index 24810e50..659b7c87 100644 --- a/src/OpenIddict/OpenIddictExtensions.cs +++ b/src/OpenIddict/OpenIddictExtensions.cs @@ -847,6 +847,34 @@ namespace Microsoft.AspNetCore.Builder return builder.Configure(options => options.Issuer = address); } + /// + /// Registers the specified claims as supported claims so + /// they can be returned as part of the discovery document. + /// + /// The services builder used by OpenIddict to register new services. + /// The supported claims. + /// The . + public static OpenIddictBuilder RegisterClaims( + [NotNull] this OpenIddictBuilder builder, [NotNull] params string[] claims) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (claims == null) + { + throw new ArgumentNullException(nameof(claims)); + } + + if (claims.Any(claim => string.IsNullOrEmpty(claim))) + { + throw new ArgumentException("Claims cannot be null or empty.", nameof(claims)); + } + + return builder.Configure(options => options.Claims.UnionWith(claims)); + } + /// /// Registers the specified scopes as supported scopes so /// they can be returned as part of the discovery document. diff --git a/src/OpenIddict/OpenIddictOptions.cs b/src/OpenIddict/OpenIddictOptions.cs index 6e35223f..cf4c62e9 100644 --- a/src/OpenIddict/OpenIddictOptions.cs +++ b/src/OpenIddict/OpenIddictOptions.cs @@ -35,6 +35,11 @@ namespace OpenIddict /// public IDistributedCache Cache { get; set; } + /// + /// Gets the OAuth2/OpenID Connect claims supported by this application. + /// + public ISet Claims { get; } = new HashSet(StringComparer.Ordinal); + /// /// Gets or sets a boolean indicating whether token revocation should be disabled. /// When disabled, authorization code and refresh tokens are not stored diff --git a/src/OpenIddict/OpenIddictProvider.Discovery.cs b/src/OpenIddict/OpenIddictProvider.Discovery.cs index 87d54001..467c3503 100644 --- a/src/OpenIddict/OpenIddictProvider.Discovery.cs +++ b/src/OpenIddict/OpenIddictProvider.Discovery.cs @@ -39,6 +39,11 @@ namespace OpenIddict context.Scopes.Clear(); context.Scopes.UnionWith(options.Scopes); + // Note: claims_supported is a recommended parameter but is not strictly required. + // If no claim was registered, the claims_supported property will be automatically + // excluded from the response by the OpenID Connect server middleware. + context.Metadata[OpenIdConnectConstants.Metadata.ClaimsSupported] = new JArray(options.Claims); + // Note: the optional "claims" parameter is not supported by OpenIddict, // so a "false" flag is returned to encourage clients not to use it. context.Metadata[OpenIdConnectConstants.Metadata.ClaimsParameterSupported] = false; diff --git a/test/OpenIddict.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.Tests/OpenIddictExtensionsTests.cs index ee4da70e..d04ad101 100644 --- a/test/OpenIddict.Tests/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.Tests/OpenIddictExtensionsTests.cs @@ -583,7 +583,24 @@ namespace OpenIddict.Tests } [Fact] - public void RegisterScopes_ScopeIsAdded() + public void RegisterClaims_ClaimsAreAdded() + { + // Arrange + var services = CreateServices(); + var builder = new OpenIddictBuilder(services); + + // Act + builder.RegisterClaims("custom_claim_1", "custom_claim_2"); + + var options = GetOptions(services); + + // Assert + Assert.Contains("custom_claim_1", options.Claims); + Assert.Contains("custom_claim_2", options.Claims); + } + + [Fact] + public void RegisterScopes_ScopesAreAdded() { // Arrange var services = CreateServices(); diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Discovery.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Discovery.cs index 23f16b13..87d4e311 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.Discovery.cs +++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Discovery.cs @@ -138,6 +138,39 @@ namespace OpenIddict.Tests ((JArray) response[OpenIdConnectConstants.Metadata.ScopesSupported]).Values()); } + [Fact] + public async Task HandleConfigurationRequest_NoSupportedClaimsPropertyIsReturnedWhenNoClaimIsConfigured() + { + // Arrange + var server = CreateAuthorizationServer(); + + var client = new OpenIdConnectClient(server.CreateClient()); + + // Act + var response = await client.GetAsync(ConfigurationEndpoint); + + // Assert + Assert.False(response.HasParameter(OpenIdConnectConstants.Metadata.ClaimsSupported)); + } + + [Fact] + public async Task HandleConfigurationRequest_ConfiguredClaimsAreReturned() + { + // Arrange + var server = CreateAuthorizationServer(builder => + { + builder.Configure(options => options.Claims.Add("custom_claim")); + }); + + var client = new OpenIdConnectClient(server.CreateClient()); + + // Act + var response = await client.GetAsync(ConfigurationEndpoint); + + // Assert + Assert.Contains("custom_claim", ((JArray) response[OpenIdConnectConstants.Metadata.ClaimsSupported]).Values()); + } + [Fact] public async Task HandleConfigurationRequest_ClaimsParameterSupportedIsReturned() {