From 970ce43ca7837a0bc0ef20355513ef6aaaf241ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Wed, 7 Apr 2021 11:38:25 +0200 Subject: [PATCH] Allow authorization requests that don't specify response_type=code when PKCE is enforced --- ...OpenIddictServerHandlers.Authentication.cs | 6 +- ...ctServerIntegrationTests.Authentication.cs | 57 +++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs index d5986036..fda3e1d3 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs @@ -1588,9 +1588,9 @@ namespace OpenIddict.Server Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId)); - // If a code_challenge was provided, the request is always considered valid, - // whether the proof key for code exchange requirement is enforced or not. - if (!string.IsNullOrEmpty(context.Request.CodeChallenge)) + // If a code_challenge was provided or if no authorization code is requested, the request is always + // considered valid, whether the proof key for code exchange requirement is enforced or not. + if (!string.IsNullOrEmpty(context.Request.CodeChallenge) || !context.Request.HasResponseType(ResponseTypes.Code)) { return; } diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs index f34329a3..7c670be5 100644 --- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs +++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs @@ -1734,6 +1734,63 @@ namespace OpenIddict.Server.IntegrationTests Requirements.Features.ProofKeyForCodeExchange, It.IsAny()), Times.Never()); } + [Fact] + public async Task ValidateAuthorizationRequest_RequestIsValidatedWhenCodeIsNotRequestedWithPkceFeatureEnforced() + { + // Arrange + var application = new OpenIddictApplication(); + + var manager = CreateApplicationManager(mock => + { + mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny())) + .ReturnsAsync(application); + + mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny())) + .ReturnsAsync(true); + + mock.Setup(manager => manager.HasRequirementAsync(application, + Requirements.Features.ProofKeyForCodeExchange, It.IsAny())) + .ReturnsAsync(true); + }); + + await using var server = await CreateServerAsync(options => + { + options.SetRevocationEndpointUris(Array.Empty()); + options.DisableAuthorizationStorage(); + options.DisableTokenStorage(); + options.DisableSlidingRefreshTokenExpiration(); + + options.Services.AddSingleton(manager); + + options.AddEventHandler(builder => + builder.UseInlineHandler(context => + { + context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer")) + .SetClaim(Claims.Subject, "Bob le Magnifique"); + + return default; + })); + }); + + await using var client = await server.CreateClientAsync(); + + // Act + var response = await client.PostAsync("/connect/authorize", new OpenIddictRequest + { + ClientId = "Fabrikam", + RedirectUri = "http://www.fabrikam.com/path", + ResponseType = ResponseTypes.Token + }); + + // Assert + Assert.Null(response.Code); + Assert.NotNull(response.AccessToken); + + Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(manager => manager.HasRequirementAsync(application, + Requirements.Features.ProofKeyForCodeExchange, It.IsAny()), Times.Never()); + } + [Theory] [InlineData("custom_error", null, null)] [InlineData("custom_error", "custom_description", null)]