Browse Source

Allow authorization requests that don't specify response_type=code when PKCE is enforced

pull/1240/head
Kévin Chalet 5 years ago
parent
commit
970ce43ca7
  1. 6
      src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs
  2. 57
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs

6
src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs

@ -1588,9 +1588,9 @@ namespace OpenIddict.Server
Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId)); Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
// If a code_challenge was provided, the request is always considered valid, // If a code_challenge was provided or if no authorization code is requested, the request is always
// whether the proof key for code exchange requirement is enforced or not. // considered valid, whether the proof key for code exchange requirement is enforced or not.
if (!string.IsNullOrEmpty(context.Request.CodeChallenge)) if (!string.IsNullOrEmpty(context.Request.CodeChallenge) || !context.Request.HasResponseType(ResponseTypes.Code))
{ {
return; return;
} }

57
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs

@ -1734,6 +1734,63 @@ namespace OpenIddict.Server.IntegrationTests
Requirements.Features.ProofKeyForCodeExchange, It.IsAny<CancellationToken>()), Times.Never()); Requirements.Features.ProofKeyForCodeExchange, It.IsAny<CancellationToken>()), 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<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
mock.Setup(manager => manager.HasRequirementAsync(application,
Requirements.Features.ProofKeyForCodeExchange, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
});
await using var server = await CreateServerAsync(options =>
{
options.SetRevocationEndpointUris(Array.Empty<Uri>());
options.DisableAuthorizationStorage();
options.DisableTokenStorage();
options.DisableSlidingRefreshTokenExpiration();
options.Services.AddSingleton(manager);
options.AddEventHandler<HandleAuthorizationRequestContext>(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<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.HasRequirementAsync(application,
Requirements.Features.ProofKeyForCodeExchange, It.IsAny<CancellationToken>()), Times.Never());
}
[Theory] [Theory]
[InlineData("custom_error", null, null)] [InlineData("custom_error", null, null)]
[InlineData("custom_error", "custom_description", null)] [InlineData("custom_error", "custom_description", null)]

Loading…
Cancel
Save