From c9d4215a34684d13d6cae4f302684af0a79617ad Mon Sep 17 00:00:00 2001 From: Oleks Dovzhenko Date: Wed, 28 Oct 2026 16:31:19 +0100 Subject: [PATCH 1/3] feat: Add an option to return authorize errors to the client --- .../DependencyInjection/Options/IdentityServerOptions.cs | 5 +++++ .../src/Endpoints/Results/AuthorizeResult.cs | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Open.IdentityServer/src/Configuration/DependencyInjection/Options/IdentityServerOptions.cs b/src/Open.IdentityServer/src/Configuration/DependencyInjection/Options/IdentityServerOptions.cs index 603281e43..7e8e276d7 100644 --- a/src/Open.IdentityServer/src/Configuration/DependencyInjection/Options/IdentityServerOptions.cs +++ b/src/Open.IdentityServer/src/Configuration/DependencyInjection/Options/IdentityServerOptions.cs @@ -50,6 +50,11 @@ public class IdentityServerOptions /// public bool StrictJarValidation { get; set; } = false; + /// + /// Specifies whether the authorize endpoint returns all errors to the client once the redirect URI and response mode have been validated, as RFC 6749 section 4.1.2.1 describes. + /// + public bool StrictAuthorizeErrorResponses { get; set; } = false; + /// /// Gets or sets the supported JWT request object signing algorithms. /// Defaults to an empty collection. diff --git a/src/Open.IdentityServer/src/Endpoints/Results/AuthorizeResult.cs b/src/Open.IdentityServer/src/Endpoints/Results/AuthorizeResult.cs index 81352b900..27c431abd 100644 --- a/src/Open.IdentityServer/src/Endpoints/Results/AuthorizeResult.cs +++ b/src/Open.IdentityServer/src/Endpoints/Results/AuthorizeResult.cs @@ -85,7 +85,9 @@ private async Task ProcessErrorAsync(HttpContext context) Response.Error == OidcConstants.AuthorizeErrors.ConsentRequired || Response.Error == OidcConstants.AuthorizeErrors.InteractionRequired; - if (isSafeError) + var returnToClientInStrictMode = _options.StrictAuthorizeErrorResponses && Response.RedirectUri != null && + Response.Request?.ResponseMode != null; + if (isSafeError || returnToClientInStrictMode) { // this scenario we can return back to the client await ProcessResponseAsync(context); From 155ffd488f5a3c98204baca293e23ea37070de00 Mon Sep 17 00:00:00 2001 From: Oleks Dovzhenko Date: Wed, 28 Oct 2026 16:31:31 +0100 Subject: [PATCH 2/3] test: Cover StrictAuthorizeErrorResponses Co-Authored-By: Claude Opus 5.5 --- .../Endpoints/Results/AuthorizeResultTests.cs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/Open.IdentityServer/test/Open.IdentityServer.UnitTests/Endpoints/Results/AuthorizeResultTests.cs b/src/Open.IdentityServer/test/Open.IdentityServer.UnitTests/Endpoints/Results/AuthorizeResultTests.cs index 5d3b62744..97f7ef9e5 100644 --- a/src/Open.IdentityServer/test/Open.IdentityServer.UnitTests/Endpoints/Results/AuthorizeResultTests.cs +++ b/src/Open.IdentityServer/test/Open.IdentityServer.UnitTests/Endpoints/Results/AuthorizeResultTests.cs @@ -97,6 +97,69 @@ public async Task SafeErrors_ShouldReturnToClient(string error) location.Should().Contain("#_"); } + [Theory] + [InlineData(OidcConstants.AuthorizeErrors.InvalidRequest)] + [InlineData(OidcConstants.AuthorizeErrors.TemporarilyUnavailable)] + [InlineData(OidcConstants.AuthorizeErrors.UnmetAuthenticationRequirements)] + public async Task OtherErrors_WhenStrictAuthorizeErrorResponsesDisabled_ShouldRedirectToErrorPage(string error) + { + _response.Error = error; + _response.Request = new ValidatedAuthorizeRequest + { + ResponseMode = OidcConstants.ResponseModes.Query, + RedirectUri = "http://client/callback" + }; + + await _subject.ExecuteAsync(_context); + + _mockErrorMessageStore.Messages.Count.Should().Be(1); + _context.Response.Headers["Location"].First().Should().StartWith("https://server/error"); + } + + [Theory] + [InlineData(OidcConstants.AuthorizeErrors.InvalidRequest)] + [InlineData(OidcConstants.AuthorizeErrors.TemporarilyUnavailable)] + [InlineData(OidcConstants.AuthorizeErrors.UnmetAuthenticationRequirements)] + [InlineData("some_error")] + public async Task OtherErrors_WhenStrictAuthorizeErrorResponsesEnabled_ShouldReturnToClient(string error) + { + _options.StrictAuthorizeErrorResponses = true; + _response.Error = error; + _response.Request = new ValidatedAuthorizeRequest + { + ResponseMode = OidcConstants.ResponseModes.Query, + RedirectUri = "http://client/callback" + }; + + await _subject.ExecuteAsync(_context); + + _mockErrorMessageStore.Messages.Should().BeEmpty(); + var location = _context.Response.Headers["Location"].First(); + location.Should().StartWith("http://client/callback"); + QueryHelpers.ParseQuery(new Uri(location).Query)["error"].First().Should().Be(error); + } + + [Theory] + [InlineData(OidcConstants.ResponseModes.Query, null)] + [InlineData(null, "http://client/callback")] + public async Task OtherErrors_WhenStrictAuthorizeErrorResponsesEnabled_AndResponseNotRoutable_ShouldRedirectToErrorPage( + string responseMode, string redirectUri) + { + // the validator sets these only once the redirect uri and response mode are known to be valid + _options.StrictAuthorizeErrorResponses = true; + _response.Error = OidcConstants.AuthorizeErrors.InvalidRequest; + _response.Request = new ValidatedAuthorizeRequest + { + ResponseMode = responseMode, + RedirectUri = redirectUri + }; + + await _subject.ExecuteAsync(_context); + + _mockErrorMessageStore.Messages.Count.Should().Be(1); + _context.Response.Headers["Location"].First().Should().StartWith("https://server/error"); + } + [Theory] [InlineData(OidcConstants.AuthorizeErrors.AccountSelectionRequired)] [InlineData(OidcConstants.AuthorizeErrors.LoginRequired)] From 08c82e26dcf1a5c69840b3cf7c1beb38e9948eff Mon Sep 17 00:00:00 2001 From: Oleks Dovzhenko Date: Wed, 28 Oct 2026 16:31:31 +0100 Subject: [PATCH 3/3] docs: Document StrictAuthorizeErrorResponses Co-Authored-By: Claude Opus 5.5 --- docs/reference/options.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/reference/options.rst b/docs/reference/options.rst index 9dfb8e2ec..c8cacc741 100644 --- a/docs/reference/options.rst +++ b/docs/reference/options.rst @@ -21,6 +21,10 @@ IdentityServer Options * ``StrictJarValidation`` When enabled, applies strict JWT-secured authorization request validation, including typ and content-type checks, according to IETF spec. Defaults to ``false``. +* ``StrictAuthorizeErrorResponses`` + When enabled, the authorize endpoint returns every error to the client's redirect URI once the redirect URI and response mode have been validated, as RFC 6749 section 4.1.2.1 describes. Errors found before that still go to the error page: an unknown client or redirect URI, an invalid request object or ``request_uri``, and a missing or unknown ``response_type``. + Defaults to ``false``, which returns only ``access_denied``, ``account_selection_required``, ``login_required``, ``consent_required`` and ``interaction_required`` to the client. + * ``EnableAuthorizeResponseIssuerParam`` When enabled, the authorize response will include the issuer ``iss`` parameter, and advertises support in the discovery document. Defaults to ``false``.