From 233c76b08e724a430e79acdcb02a25418693d154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Thu, 23 Mar 2023 21:15:25 +0100 Subject: [PATCH] Update OpenIddictValidationServerIntegrationConfiguration to initialize OpenIddictValidationOptions.Issuer and add new SetClientUri/SetIssuer overloads = --- .../OpenIddictResources.resx | 6 ++++++ .../OpenIddictClientSystemNetHttpBuilder.cs | 2 +- .../OpenIddictClientBuilder.cs | 21 +++++++++++++++++++ .../OpenIddictClientConfiguration.cs | 8 ++++++- .../OpenIddictClientHandlers.cs | 2 +- .../OpenIddictServerBuilder.cs | 21 +++++++++++++++++++ .../OpenIddictServerHandlers.Exchange.cs | 2 +- ...alidationServerIntegrationConfiguration.cs | 3 ++- ...penIddictValidationSystemNetHttpBuilder.cs | 2 +- .../OpenIddictValidationConfiguration.cs | 10 ++++++++- .../OpenIddictServerBuilderTests.cs | 20 ++++++++++++++++-- 11 files changed, 88 insertions(+), 9 deletions(-) diff --git a/src/OpenIddict.Abstractions/OpenIddictResources.resx b/src/OpenIddict.Abstractions/OpenIddictResources.resx index 1e91effb..033c7dcb 100644 --- a/src/OpenIddict.Abstractions/OpenIddictResources.resx +++ b/src/OpenIddict.Abstractions/OpenIddictResources.resx @@ -1480,6 +1480,12 @@ To apply post-logout redirection responses, create a class implementing 'IOpenId The web authentication result cannot be resolved or contains invalid data. + + The issuer attached to the static configuration must be the same as the one configured in the validation options. + + + The issuer attached to the static configuration must be the same as the one configured in the client registration. + The security token is missing. diff --git a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpBuilder.cs b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpBuilder.cs index 889e3750..4b5e5929 100644 --- a/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpBuilder.cs +++ b/src/OpenIddict.Client.SystemNetHttp/OpenIddictClientSystemNetHttpBuilder.cs @@ -75,7 +75,7 @@ public sealed class OpenIddictClientSystemNetHttpBuilder { if (string.IsNullOrEmpty(address)) { - throw new ArgumentException(SR.GetResourceString(SR.ID0366), nameof(address)); + throw new ArgumentException(SR.FormatID0366(nameof(address)), nameof(address)); } return SetContactAddress(new MailAddress(address)); diff --git a/src/OpenIddict.Client/OpenIddictClientBuilder.cs b/src/OpenIddict.Client/OpenIddictClientBuilder.cs index df2a9fb8..fefeb1ad 100644 --- a/src/OpenIddict.Client/OpenIddictClientBuilder.cs +++ b/src/OpenIddict.Client/OpenIddictClientBuilder.cs @@ -1141,6 +1141,27 @@ public sealed class OpenIddictClientBuilder return Configure(options => options.ClientUri = uri); } + /// + /// Sets the client URI, which is used as the value of the "issuer" claim. + /// + /// The client URI. + /// The instance. + [EditorBrowsable(EditorBrowsableState.Advanced)] + public OpenIddictClientBuilder SetClientUri([StringSyntax(StringSyntaxAttribute.Uri)] string uri) + { + if (string.IsNullOrEmpty(uri)) + { + throw new ArgumentException(SR.FormatID0366(nameof(uri)), nameof(uri)); + } + + if (!Uri.TryCreate(uri, UriKind.Absolute, out Uri? value) || !value.IsWellFormedOriginalString()) + { + throw new ArgumentException(SR.GetResourceString(SR.ID0144), nameof(uri)); + } + + return SetClientUri(value); + } + /// [EditorBrowsable(EditorBrowsableState.Never)] public override bool Equals(object? obj) => base.Equals(obj); diff --git a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs index 92720ead..5f7c98b9 100644 --- a/src/OpenIddict.Client/OpenIddictClientConfiguration.cs +++ b/src/OpenIddict.Client/OpenIddictClientConfiguration.cs @@ -57,7 +57,13 @@ public sealed class OpenIddictClientConfiguration : IPostConfigureOptions(registration.Configuration); } diff --git a/src/OpenIddict.Client/OpenIddictClientHandlers.cs b/src/OpenIddict.Client/OpenIddictClientHandlers.cs index 0be38508..e542f60a 100644 --- a/src/OpenIddict.Client/OpenIddictClientHandlers.cs +++ b/src/OpenIddict.Client/OpenIddictClientHandlers.cs @@ -4483,7 +4483,7 @@ public static partial class OpenIddictClientHandlers // If the request is an OpenID Connect request, the nonce will also be hashed and // attached to the authorization request so that the identity provider can bind // the issued identity tokens to the generated value, which helps detect token - // replay (and authorization code injection attacks when PKCE is not available). + // replays (and authorization code injection attacks when PKCE is not available). context.Nonce = Base64UrlEncoder.Encode(OpenIddictHelpers.CreateRandomArray(size: 256)); return default; diff --git a/src/OpenIddict.Server/OpenIddictServerBuilder.cs b/src/OpenIddict.Server/OpenIddictServerBuilder.cs index da4a97f5..66fb2dc7 100644 --- a/src/OpenIddict.Server/OpenIddictServerBuilder.cs +++ b/src/OpenIddict.Server/OpenIddictServerBuilder.cs @@ -1731,6 +1731,27 @@ public sealed class OpenIddictServerBuilder return Configure(options => options.Issuer = uri); } + /// + /// Sets the issuer URI, which is used as the value of the "issuer" claim and + /// is returned from the discovery endpoint to identify the authorization server. + /// + /// The issuer uri. + /// The instance. + public OpenIddictServerBuilder SetIssuer([StringSyntax(StringSyntaxAttribute.Uri)] string uri) + { + if (string.IsNullOrEmpty(uri)) + { + throw new ArgumentException(SR.FormatID0366(nameof(uri)), nameof(uri)); + } + + if (!Uri.TryCreate(uri, UriKind.Absolute, out Uri? value) || !value.IsWellFormedOriginalString()) + { + throw new ArgumentException(SR.GetResourceString(SR.ID0144), nameof(uri)); + } + + return SetIssuer(value); + } + /// /// Configures OpenIddict to use reference tokens, so that the access token payloads /// are stored in the database (only an identifier is returned to the client application). diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs index 77480861..0e4dda6a 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs @@ -1559,7 +1559,7 @@ public static partial class OpenIddictServerHandlers if (string.IsNullOrEmpty(challenge)) { // Validate that the token request does not include a code_verifier parameter - // when code_challenge private claim was attached to the authorization code. + // when no code_challenge private claim was attached to the authorization code. if (!string.IsNullOrEmpty(context.Request.CodeVerifier)) { context.Logger.LogInformation(SR.GetResourceString(SR.ID6093), Parameters.CodeVerifier); diff --git a/src/OpenIddict.Validation.ServerIntegration/OpenIddictValidationServerIntegrationConfiguration.cs b/src/OpenIddict.Validation.ServerIntegration/OpenIddictValidationServerIntegrationConfiguration.cs index 7f7c4139..775256d9 100644 --- a/src/OpenIddict.Validation.ServerIntegration/OpenIddictValidationServerIntegrationConfiguration.cs +++ b/src/OpenIddict.Validation.ServerIntegration/OpenIddictValidationServerIntegrationConfiguration.cs @@ -36,9 +36,10 @@ public sealed class OpenIddictValidationServerIntegrationConfiguration : IConfig // Note: the issuer may be null. In this case, it will be usually provided by // a validation handler registered by the host (e.g ASP.NET Core or OWIN/Katana). + options.Issuer = _options.CurrentValue.Issuer; options.Configuration = new OpenIddictConfiguration { - Issuer = _options.CurrentValue.Issuer + Issuer = options.Issuer }; // Import the signing keys from the server configuration. diff --git a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpBuilder.cs b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpBuilder.cs index 914960f5..209c2e88 100644 --- a/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpBuilder.cs +++ b/src/OpenIddict.Validation.SystemNetHttp/OpenIddictValidationSystemNetHttpBuilder.cs @@ -75,7 +75,7 @@ public sealed class OpenIddictValidationSystemNetHttpBuilder { if (string.IsNullOrEmpty(address)) { - throw new ArgumentException(SR.GetResourceString(SR.ID0366), nameof(address)); + throw new ArgumentException(SR.FormatID0366(nameof(address)), nameof(address)); } return SetContactAddress(new MailAddress(address)); diff --git a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs index 66575995..4252f309 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationConfiguration.cs @@ -95,7 +95,15 @@ public sealed class OpenIddictValidationConfiguration : IPostConfigureOptions(options.Configuration); } diff --git a/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs b/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs index 9f8a28ff..35ebd601 100644 --- a/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs +++ b/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs @@ -1808,11 +1808,27 @@ public class OpenIddictServerBuilderTests var builder = CreateBuilder(services); // Act and assert - var exception = Assert.Throws(() => builder.SetIssuer(null!)); + var exception = Assert.Throws(() => builder.SetIssuer((Uri?) null!)); Assert.Equal("uri", exception.ParamName); } + [Theory] + [InlineData(null)] + [InlineData("")] + public void SetIssuer_ThrowsAnExceptionForNullOrEmptyIssuer(string? uri) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act and assert + var exception = Assert.Throws(() => builder.SetIssuer(uri!)); + + Assert.Equal("uri", exception.ParamName); + Assert.StartsWith(SR.FormatID0366("uri"), exception.Message); + } + [Fact] public void SetIssuer_IssuerIsReplaced() { @@ -1821,7 +1837,7 @@ public class OpenIddictServerBuilderTests var builder = CreateBuilder(services); // Act - builder.SetIssuer(new Uri("http://www.fabrikam.com/")); + builder.SetIssuer("http://www.fabrikam.com/"); var options = GetOptions(services);