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);