Browse Source

Port the token endpoint integration tests

pull/875/head
Kévin Chalet 7 years ago
parent
commit
eb35cbefb7
  1. 2
      eng/Versions.props
  2. 9
      src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs
  3. 9
      src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs
  4. 18
      src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs
  5. 23
      src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs
  6. 9
      src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs
  7. 45
      src/OpenIddict.Server/OpenIddictServerHandlers.cs
  8. 28
      test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs
  9. 57
      test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddictServerAspNetCoreIntegrationTests.Exchange.cs
  10. 108
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs
  11. 3019
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs
  12. 16
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs
  13. 56
      test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.Exchange.cs

2
eng/Versions.props

@ -40,7 +40,7 @@
<ImmutableCollectionsVersion>1.5.0</ImmutableCollectionsVersion> <ImmutableCollectionsVersion>1.5.0</ImmutableCollectionsVersion>
<LinqAsyncVersion>4.0.0</LinqAsyncVersion> <LinqAsyncVersion>4.0.0</LinqAsyncVersion>
<MongoDbVersion>2.9.0</MongoDbVersion> <MongoDbVersion>2.9.0</MongoDbVersion>
<MoqVersion>4.7.63</MoqVersion> <MoqVersion>4.13.1</MoqVersion>
<NHibernateVersion>5.2.2</NHibernateVersion> <NHibernateVersion>5.2.2</NHibernateVersion>
<OwinVersion>4.0.0</OwinVersion> <OwinVersion>4.0.0</OwinVersion>
<SystemTextJsonVersion>4.6.0</SystemTextJsonVersion> <SystemTextJsonVersion>4.6.0</SystemTextJsonVersion>

9
src/OpenIddict.Abstractions/Primitives/OpenIddictExtensions.cs

@ -1886,6 +1886,15 @@ namespace OpenIddict.Abstractions
public static ClaimsPrincipal SetInternalTokenId([NotNull] this ClaimsPrincipal principal, string identifier) public static ClaimsPrincipal SetInternalTokenId([NotNull] this ClaimsPrincipal principal, string identifier)
=> principal.SetClaim(Claims.Private.TokenId, identifier); => principal.SetClaim(Claims.Private.TokenId, identifier);
/// <summary>
/// Sets the token usage associated with the claims principal.
/// </summary>
/// <param name="principal">The claims principal.</param>
/// <param name="usage">The token usage to store.</param>
/// <returns>The claims principal.</returns>
public static ClaimsPrincipal SetTokenUsage([NotNull] this ClaimsPrincipal principal, string usage)
=> principal.SetClaim(Claims.Private.TokenUsage, usage);
private static IEnumerable<string> GetValues(string source, char[] separators) private static IEnumerable<string> GetValues(string source, char[] separators)
{ {
Debug.Assert(!string.IsNullOrEmpty(source), "The source string shouldn't be null or empty."); Debug.Assert(!string.IsNullOrEmpty(source), "The source string shouldn't be null or empty.");

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

@ -297,6 +297,15 @@ namespace OpenIddict.Server
context.SkipRequest(); context.SkipRequest();
return; return;
} }
else if (@event.IsRejected)
{
context.Reject(
error: @event.Error ?? Errors.InvalidGrant,
description: @event.ErrorDescription,
uri: @event.ErrorUri);
return;
}
} }
throw new InvalidOperationException(new StringBuilder() throw new InvalidOperationException(new StringBuilder()

18
src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs

@ -294,6 +294,15 @@ namespace OpenIddict.Server
return; return;
} }
else if (@event.IsRejected)
{
context.Reject(
error: @event.Error ?? Errors.InvalidGrant,
description: @event.ErrorDescription,
uri: @event.ErrorUri);
return;
}
throw new InvalidOperationException(new StringBuilder() throw new InvalidOperationException(new StringBuilder()
.Append("The device request was not handled. To handle device requests, ") .Append("The device request was not handled. To handle device requests, ")
.Append("create a class implementing 'IOpenIddictServerHandler<HandleDeviceRequestContext>' ") .Append("create a class implementing 'IOpenIddictServerHandler<HandleDeviceRequestContext>' ")
@ -1059,6 +1068,15 @@ namespace OpenIddict.Server
context.SkipRequest(); context.SkipRequest();
return; return;
} }
else if (@event.IsRejected)
{
context.Reject(
error: @event.Error ?? Errors.InvalidGrant,
description: @event.ErrorDescription,
uri: @event.ErrorUri);
return;
}
} }
throw new InvalidOperationException(new StringBuilder() throw new InvalidOperationException(new StringBuilder()

23
src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs

@ -268,7 +268,7 @@ namespace OpenIddict.Server
else if (notification.IsRejected) else if (notification.IsRejected)
{ {
context.Reject( context.Reject(
error: notification.Error ?? Errors.InvalidRequest, error: notification.Error ?? Errors.InvalidGrant,
description: notification.ErrorDescription, description: notification.ErrorDescription,
uri: notification.ErrorUri); uri: notification.ErrorUri);
return; return;
@ -295,6 +295,15 @@ namespace OpenIddict.Server
context.SkipRequest(); context.SkipRequest();
return; return;
} }
else if (@event.IsRejected)
{
context.Reject(
error: @event.Error ?? Errors.InvalidGrant,
description: @event.ErrorDescription,
uri: @event.ErrorUri);
return;
}
} }
throw new InvalidOperationException(new StringBuilder() throw new InvalidOperationException(new StringBuilder()
@ -1641,7 +1650,8 @@ namespace OpenIddict.Server
else if (string.Equals(method, CodeChallengeMethods.Sha256, StringComparison.Ordinal)) else if (string.Equals(method, CodeChallengeMethods.Sha256, StringComparison.Ordinal))
{ {
using var algorithm = SHA256.Create(); using var algorithm = SHA256.Create();
data = algorithm.ComputeHash(Encoding.ASCII.GetBytes(context.Request.CodeVerifier)); data = Encoding.ASCII.GetBytes(Base64UrlEncoder.Encode(
algorithm.ComputeHash(Encoding.ASCII.GetBytes(context.Request.CodeVerifier))));
} }
else else
@ -1657,7 +1667,7 @@ namespace OpenIddict.Server
// Compare the verifier and the code challenge: if the two don't match, return an error. // Compare the verifier and the code challenge: if the two don't match, return an error.
// Note: to prevent timing attacks, a time-constant comparer is always used. // Note: to prevent timing attacks, a time-constant comparer is always used.
if (!FixedTimeEquals(data, Base64UrlEncoder.DecodeBytes(challenge))) if (!FixedTimeEquals(data, Encoding.UTF8.GetBytes(challenge)))
{ {
context.Logger.LogError("The token request was rejected because the 'code_verifier' was invalid."); context.Logger.LogError("The token request was rejected because the 'code_verifier' was invalid.");
@ -1721,7 +1731,12 @@ namespace OpenIddict.Server
throw new ArgumentNullException(nameof(context)); throw new ArgumentNullException(nameof(context));
} }
if (!context.Request.IsAuthorizationCodeGrantType() || string.IsNullOrEmpty(context.Request.Scope)) if (string.IsNullOrEmpty(context.Request.Scope))
{
return default;
}
if (!context.Request.IsAuthorizationCodeGrantType() && !context.Request.IsRefreshTokenGrantType())
{ {
return default; return default;
} }

9
src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs

@ -270,6 +270,15 @@ namespace OpenIddict.Server
return; return;
} }
else if (@event.IsRejected)
{
context.Reject(
error: @event.Error ?? Errors.InvalidRequest,
description: @event.ErrorDescription,
uri: @event.ErrorUri);
return;
}
throw new InvalidOperationException(new StringBuilder() throw new InvalidOperationException(new StringBuilder()
.Append("The logout request was not handled. To handle logout requests, ") .Append("The logout request was not handled. To handle logout requests, ")
.Append("create a class implementing 'IOpenIddictServerHandler<HandleLogoutRequestContext>' ") .Append("create a class implementing 'IOpenIddictServerHandler<HandleLogoutRequestContext>' ")

45
src/OpenIddict.Server/OpenIddictServerHandlers.cs

@ -349,13 +349,13 @@ namespace OpenIddict.Server
description: context.EndpointType switch description: context.EndpointType switch
{ {
OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType() OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
=> "The specified authorization code is not valid.", => "The specified authorization code is invalid.",
OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType() OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
=> "The specified device code is not valid.", => "The specified device code is invalid.",
OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType() OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
=> "The specified refresh token is not valid.", => "The specified refresh token is invalid.",
_ => "The specified token is not valid." _ => "The specified token is invalid."
}); });
return; return;
@ -592,17 +592,17 @@ namespace OpenIddict.Server
}, },
description: context.EndpointType switch description: context.EndpointType switch
{ {
OpenIddictServerEndpointType.Authorization => "The specified identity token hint is not valid.", OpenIddictServerEndpointType.Authorization => "The specified identity token hint is invalid.",
OpenIddictServerEndpointType.Logout => "The specified identity token hint is not valid.", OpenIddictServerEndpointType.Logout => "The specified identity token hint is invalid.",
OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType() OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
=> "The specified authorization code is not valid.", => "The specified authorization code is invalid.",
OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType() OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
=> "The specified device code is not valid.", => "The specified device code is invalid.",
OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType() OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
=> "The specified refresh token is not valid.", => "The specified refresh token is invalid.",
_ => "The specified token is not valid." _ => "The specified token is invalid."
}); });
@ -679,13 +679,13 @@ namespace OpenIddict.Server
description: context.EndpointType switch description: context.EndpointType switch
{ {
OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType() OpenIddictServerEndpointType.Token when context.Request.IsAuthorizationCodeGrantType()
=> "The specified authorization code is not valid.", => "The specified authorization code is invalid.",
OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType() OpenIddictServerEndpointType.Token when context.Request.IsDeviceCodeGrantType()
=> "The specified device code is not valid.", => "The specified device code is invalid.",
OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType() OpenIddictServerEndpointType.Token when context.Request.IsRefreshTokenGrantType()
=> "The specified refresh token is not valid.", => "The specified refresh token is invalid.",
_ => "The specified token is not valid." _ => "The specified token is invalid."
}); });
return; return;
@ -701,7 +701,11 @@ namespace OpenIddict.Server
// See https://tools.ietf.org/html/rfc6749#section-10.5 for more information. // See https://tools.ietf.org/html/rfc6749#section-10.5 for more information.
if (await _tokenManager.HasStatusAsync(token, Statuses.Redeemed)) if (await _tokenManager.HasStatusAsync(token, Statuses.Redeemed))
{ {
await TryRevokeAuthorizationChainAsync(token); // First, mark the redeemed token submitted by the client as revoked.
await _tokenManager.TryRevokeAsync(token);
// Then, try to revoke the authorization and the associated token entries.
await TryRevokeAuthorizationChainAsync(context.Principal.GetInternalAuthorizationId());
context.Logger.LogError("The token '{Identifier}' has already been redeemed.", identifier); context.Logger.LogError("The token '{Identifier}' has already been redeemed.", identifier);
@ -786,12 +790,8 @@ namespace OpenIddict.Server
.SetInternalTokenId(await _tokenManager.GetIdAsync(token)) .SetInternalTokenId(await _tokenManager.GetIdAsync(token))
.SetClaim(Claims.Private.TokenUsage, await _tokenManager.GetTypeAsync(token)); .SetClaim(Claims.Private.TokenUsage, await _tokenManager.GetTypeAsync(token));
async ValueTask TryRevokeAuthorizationChainAsync(object token) async ValueTask TryRevokeAuthorizationChainAsync(string identifier)
{ {
// First, mark the redeemed token submitted by the client as revoked.
await _tokenManager.TryRevokeAsync(token);
var identifier = context.Principal.GetInternalAuthorizationId();
if (context.Options.DisableAuthorizationStorage || string.IsNullOrEmpty(identifier)) if (context.Options.DisableAuthorizationStorage || string.IsNullOrEmpty(identifier))
{ {
return; return;
@ -805,12 +805,11 @@ namespace OpenIddict.Server
await _authorizationManager.TryRevokeAsync(authorization); await _authorizationManager.TryRevokeAsync(authorization);
} }
await using var enumerator = _tokenManager.FindByAuthorizationIdAsync(identifier).GetAsyncEnumerator(); await foreach (var token in _tokenManager.FindByAuthorizationIdAsync(identifier))
while (await enumerator.MoveNextAsync())
{ {
// Don't change the status of the token used in the token request. // Don't change the status of the token used in the token request.
if (string.Equals(context.Principal.GetInternalTokenId(), if (string.Equals(context.Principal.GetInternalTokenId(),
await _tokenManager.GetIdAsync(enumerator.Current), StringComparison.Ordinal)) await _tokenManager.GetIdAsync(token), StringComparison.Ordinal))
{ {
continue; continue;
} }

28
test/OpenIddict.Abstractions.Tests/Primitives/OpenIddictExtensionsTests.cs

@ -2972,6 +2972,34 @@ namespace OpenIddict.Abstractions.Tests.Primitives
Assert.Equal(identifier, principal.GetClaim(Claims.Private.TokenId)); Assert.Equal(identifier, principal.GetClaim(Claims.Private.TokenId));
} }
[Fact]
public void SetTokenUsage_ThrowsAnExceptionForNullPrincipal()
{
// Arrange
var principal = (ClaimsPrincipal) null;
// Act and assert
var exception = Assert.Throws<ArgumentNullException>(() => principal.SetTokenUsage(null));
Assert.Equal("principal", exception.ParamName);
}
[Theory]
[InlineData(null)]
[InlineData("access_token")]
public void SetTokenUsage_AddsUsage(string usage)
{
// Arrange
var identity = new ClaimsIdentity();
var principal = new ClaimsPrincipal(identity);
// Act
principal.SetTokenUsage(usage);
// Assert
Assert.Equal(usage, principal.GetClaim(Claims.Private.TokenUsage));
}
private TimeSpan? ParseLifeTime(string lifetime) private TimeSpan? ParseLifeTime(string lifetime)
{ {
var lifeT = lifetime != null var lifeT = lifetime != null

57
test/OpenIddict.Server.AspNetCore.IntegrationTests/OpenIddictServerAspNetCoreIntegrationTests.Exchange.cs

@ -0,0 +1,57 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.Net.Http.Headers;
using OpenIddict.Abstractions;
using OpenIddict.Server.FunctionalTests;
using Xunit;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Server.OpenIddictServerEvents;
namespace OpenIddict.Server.AspNetCore.FunctionalTests
{
public partial class OpenIddictServerAspNetCoreIntegrationTests : OpenIddictServerIntegrationTests
{
[Fact]
public async Task ExtractTokenRequest_MultipleClientCredentialsCauseAnError()
{
// Arrange
var client = CreateClient(options =>
{
options.EnableDegradedMode();
options.AddEventHandler<ExtractTokenRequestContext>(builder =>
{
builder.UseInlineHandler(context =>
{
var request = context.Transaction.GetHttpRequest();
request.Headers[HeaderNames.Authorization] = "Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW";
return default;
});
builder.SetOrder(int.MinValue);
});
});
// Act
var response = await client.PostAsync("/connect/token", new OpenIddictRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
GrantType = GrantTypes.Password,
Username = "johndoe",
Password = "A3ddj3w"
});
// Assert
Assert.Equal(Errors.InvalidRequest, response.Error);
Assert.Equal("Multiple client credentials cannot be specified.", response.ErrorDescription);
}
}
}

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

@ -131,7 +131,7 @@ namespace OpenIddict.Server.FunctionalTests
{ {
context.Transaction.SetProperty("custom_response", new context.Transaction.SetProperty("custom_response", new
{ {
name = "Bob le Magnifique" name = "Bob le Bricoleur"
}); });
context.HandleRequest(); context.HandleRequest();
@ -144,7 +144,7 @@ namespace OpenIddict.Server.FunctionalTests
var response = await client.GetAsync("/connect/authorize"); var response = await client.GetAsync("/connect/authorize");
// Assert // Assert
Assert.Equal("Bob le Magnifique", (string) response["name"]); Assert.Equal("Bob le Bricoleur", (string) response["name"]);
} }
[Fact] [Fact]
@ -538,13 +538,13 @@ namespace OpenIddict.Server.FunctionalTests
var application = new OpenIddictApplication(); var application = new OpenIddictApplication();
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<OpenIddictApplication>(application)); .ReturnsAsync(application);
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<bool>(true)); .ReturnsAsync(true);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.Returns(new ValueTask<string>(ClientTypes.Public)); .ReturnsAsync(ClientTypes.Public);
})); }));
options.Services.AddSingleton(CreateScopeManager(mock => options.Services.AddSingleton(CreateScopeManager(mock =>
@ -581,13 +581,13 @@ namespace OpenIddict.Server.FunctionalTests
var application = new OpenIddictApplication(); var application = new OpenIddictApplication();
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<OpenIddictApplication>(application)); .ReturnsAsync(application);
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<bool>(true)); .ReturnsAsync(true);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.Returns(new ValueTask<string>(ClientTypes.Public)); .ReturnsAsync(ClientTypes.Public);
})); }));
options.Services.AddSingleton(CreateApplicationManager(mock => options.Services.AddSingleton(CreateApplicationManager(mock =>
@ -595,13 +595,13 @@ namespace OpenIddict.Server.FunctionalTests
var application = new OpenIddictApplication(); var application = new OpenIddictApplication();
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<OpenIddictApplication>(application)); .ReturnsAsync(application);
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<bool>(true)); .ReturnsAsync(true);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.Returns(new ValueTask<string>(ClientTypes.Public)); .ReturnsAsync(ClientTypes.Public);
})); }));
options.RegisterScopes("registered_scope"); options.RegisterScopes("registered_scope");
@ -637,13 +637,13 @@ namespace OpenIddict.Server.FunctionalTests
var application = new OpenIddictApplication(); var application = new OpenIddictApplication();
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<OpenIddictApplication>(application)); .ReturnsAsync(application);
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<bool>(true)); .ReturnsAsync(true);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.Returns(new ValueTask<string>(ClientTypes.Public)); .ReturnsAsync(ClientTypes.Public);
})); }));
options.Services.AddSingleton(CreateScopeManager(mock => options.Services.AddSingleton(CreateScopeManager(mock =>
@ -654,7 +654,7 @@ namespace OpenIddict.Server.FunctionalTests
.Returns(new[] { scope }.ToAsyncEnumerable()); .Returns(new[] { scope }.ToAsyncEnumerable());
mock.Setup(manager => manager.GetNameAsync(scope, It.IsAny<CancellationToken>())) mock.Setup(manager => manager.GetNameAsync(scope, It.IsAny<CancellationToken>()))
.Returns(new ValueTask<string>("scope_registered_in_database")); .ReturnsAsync("scope_registered_in_database");
})); }));
options.RegisterScopes("scope_registered_in_options"); options.RegisterScopes("scope_registered_in_options");
@ -859,7 +859,7 @@ namespace OpenIddict.Server.FunctionalTests
{ {
context.Transaction.SetProperty("custom_response", new context.Transaction.SetProperty("custom_response", new
{ {
name = "Bob le Magnifique" name = "Bob le Bricoleur"
}); });
context.HandleRequest(); context.HandleRequest();
@ -878,7 +878,7 @@ namespace OpenIddict.Server.FunctionalTests
}); });
// Assert // Assert
Assert.Equal("Bob le Magnifique", (string) response["name"]); Assert.Equal("Bob le Bricoleur", (string) response["name"]);
} }
[Fact] [Fact]
@ -972,7 +972,7 @@ namespace OpenIddict.Server.FunctionalTests
var manager = CreateApplicationManager(mock => var manager = CreateApplicationManager(mock =>
{ {
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<OpenIddictApplication>(result: null)); .ReturnsAsync(value: null);
}); });
var client = CreateClient(options => var client = CreateClient(options =>
@ -1008,10 +1008,10 @@ namespace OpenIddict.Server.FunctionalTests
var manager = CreateApplicationManager(mock => var manager = CreateApplicationManager(mock =>
{ {
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<OpenIddictApplication>(application)); .ReturnsAsync(application);
mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>())) mock.Setup(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.Returns(new ValueTask<string>(ClientTypes.Confidential)); .ReturnsAsync(ClientTypes.Confidential);
}); });
var client = CreateClient(options => var client = CreateClient(options =>
@ -1034,7 +1034,7 @@ namespace OpenIddict.Server.FunctionalTests
Assert.Equal("The specified 'response_type' parameter is not valid for this client application.", response.ErrorDescription); Assert.Equal("The specified 'response_type' parameter is not valid for this client application.", response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.GetClientTypeAsync(application, It.IsAny<CancellationToken>()), Times.Once());
} }
[Fact] [Fact]
@ -1046,14 +1046,14 @@ namespace OpenIddict.Server.FunctionalTests
var manager = CreateApplicationManager(mock => var manager = CreateApplicationManager(mock =>
{ {
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<OpenIddictApplication>(application)); .ReturnsAsync(application);
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<bool>(true)); .ReturnsAsync(true);
mock.Setup(manager => manager.HasPermissionAsync(application, mock.Setup(manager => manager.HasPermissionAsync(application,
Permissions.Endpoints.Authorization, It.IsAny<CancellationToken>())) Permissions.Endpoints.Authorization, It.IsAny<CancellationToken>()))
.Returns(new ValueTask<bool>(false)); .ReturnsAsync(false);
}); });
var client = CreateClient(options => var client = CreateClient(options =>
@ -1077,7 +1077,7 @@ namespace OpenIddict.Server.FunctionalTests
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application,
Permissions.Endpoints.Authorization, It.IsAny<CancellationToken>()), Times.AtLeastOnce()); Permissions.Endpoints.Authorization, It.IsAny<CancellationToken>()), Times.Once());
} }
[Theory] [Theory]
@ -1118,15 +1118,15 @@ namespace OpenIddict.Server.FunctionalTests
var manager = CreateApplicationManager(mock => var manager = CreateApplicationManager(mock =>
{ {
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<OpenIddictApplication>(application)); .ReturnsAsync(application);
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<bool>(true)); .ReturnsAsync(true);
foreach (var permission in permissions) foreach (var permission in permissions)
{ {
mock.Setup(manager => manager.HasPermissionAsync(application, permission, It.IsAny<CancellationToken>())) mock.Setup(manager => manager.HasPermissionAsync(application, permission, It.IsAny<CancellationToken>()))
.Returns(new ValueTask<bool>(false)); .ReturnsAsync(false);
} }
}); });
@ -1152,7 +1152,7 @@ namespace OpenIddict.Server.FunctionalTests
Assert.Equal(description, response.ErrorDescription); Assert.Equal(description, response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, permissions[0], It.IsAny<CancellationToken>()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, permissions[0], It.IsAny<CancellationToken>()), Times.Once());
} }
[Fact] [Fact]
@ -1164,18 +1164,18 @@ namespace OpenIddict.Server.FunctionalTests
var manager = CreateApplicationManager(mock => var manager = CreateApplicationManager(mock =>
{ {
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<OpenIddictApplication>(application)); .ReturnsAsync(application);
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<bool>(true)); .ReturnsAsync(true);
mock.Setup(manager => manager.HasPermissionAsync(application, mock.Setup(manager => manager.HasPermissionAsync(application,
Permissions.GrantTypes.AuthorizationCode, It.IsAny<CancellationToken>())) Permissions.GrantTypes.AuthorizationCode, It.IsAny<CancellationToken>()))
.Returns(new ValueTask<bool>(true)); .ReturnsAsync(true);
mock.Setup(manager => manager.HasPermissionAsync(application, mock.Setup(manager => manager.HasPermissionAsync(application,
Permissions.GrantTypes.RefreshToken, It.IsAny<CancellationToken>())) Permissions.GrantTypes.RefreshToken, It.IsAny<CancellationToken>()))
.Returns(new ValueTask<bool>(false)); .ReturnsAsync(false);
}); });
var client = CreateClient(options => var client = CreateClient(options =>
@ -1199,7 +1199,7 @@ namespace OpenIddict.Server.FunctionalTests
Assert.Equal("The client application is not allowed to use the 'offline_access' scope.", response.ErrorDescription); Assert.Equal("The client application is not allowed to use the 'offline_access' scope.", response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application,
Permissions.GrantTypes.RefreshToken, It.IsAny<CancellationToken>()), Times.AtLeastOnce()); Permissions.GrantTypes.RefreshToken, It.IsAny<CancellationToken>()), Times.Once());
} }
[Fact] [Fact]
@ -1211,10 +1211,10 @@ namespace OpenIddict.Server.FunctionalTests
var manager = CreateApplicationManager(mock => var manager = CreateApplicationManager(mock =>
{ {
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<OpenIddictApplication>(application)); .ReturnsAsync(application);
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<bool>(false)); .ReturnsAsync(false);
}); });
var client = CreateClient(options => var client = CreateClient(options =>
@ -1235,7 +1235,7 @@ namespace OpenIddict.Server.FunctionalTests
Assert.Equal("The specified 'redirect_uri' parameter is not valid for this client application.", response.ErrorDescription); Assert.Equal("The specified 'redirect_uri' parameter is not valid for this client application.", response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()), Times.AtLeastOnce()); Mock.Get(manager).Verify(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()), Times.Once());
} }
[Fact] [Fact]
@ -1247,20 +1247,18 @@ namespace OpenIddict.Server.FunctionalTests
var manager = CreateApplicationManager(mock => var manager = CreateApplicationManager(mock =>
{ {
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<OpenIddictApplication>(application)); .ReturnsAsync(application);
mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>())) mock.Setup(manager => manager.ValidateRedirectUriAsync(application, "http://www.fabrikam.com/path", It.IsAny<CancellationToken>()))
.Returns(new ValueTask<bool>(true)); .ReturnsAsync(true);
mock.Setup(manager => manager.HasPermissionAsync(application, mock.Setup(manager => manager.HasPermissionAsync(application,
Permissions.Prefixes.Scope + Permissions.Prefixes.Scope + Scopes.Profile, It.IsAny<CancellationToken>()))
Scopes.Profile, It.IsAny<CancellationToken>())) .ReturnsAsync(true);
.Returns(new ValueTask<bool>(true));
mock.Setup(manager => manager.HasPermissionAsync(application, mock.Setup(manager => manager.HasPermissionAsync(application,
Permissions.Prefixes.Scope + Permissions.Prefixes.Scope + Scopes.Email, It.IsAny<CancellationToken>()))
Scopes.Email, It.IsAny<CancellationToken>())) .ReturnsAsync(false);
.Returns(new ValueTask<bool>(false));
}); });
var client = CreateClient(options => var client = CreateClient(options =>
@ -1284,17 +1282,13 @@ namespace OpenIddict.Server.FunctionalTests
Assert.Equal("This client application is not allowed to use the specified scope.", response.ErrorDescription); Assert.Equal("This client application is not allowed to use the specified scope.", response.ErrorDescription);
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application,
Permissions.Prefixes.Scope + Permissions.Prefixes.Scope + Scopes.OpenId, It.IsAny<CancellationToken>()), Times.Never());
Scopes.OpenId, It.IsAny<CancellationToken>()), Times.Never());
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application,
Permissions.Prefixes.Scope + Permissions.Prefixes.Scope + Scopes.OfflineAccess, It.IsAny<CancellationToken>()), Times.Never());
Scopes.OfflineAccess, It.IsAny<CancellationToken>()), Times.Never());
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application,
Permissions.Prefixes.Scope + Permissions.Prefixes.Scope + Scopes.Profile, It.IsAny<CancellationToken>()), Times.Once());
Scopes.Profile, It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application, Mock.Get(manager).Verify(manager => manager.HasPermissionAsync(application,
Permissions.Prefixes.Scope + Permissions.Prefixes.Scope + Scopes.Email, It.IsAny<CancellationToken>()), Times.Once());
Scopes.Email, It.IsAny<CancellationToken>()), Times.AtLeastOnce());
} }
[Theory] [Theory]
@ -1349,7 +1343,7 @@ namespace OpenIddict.Server.FunctionalTests
{ {
context.Transaction.SetProperty("custom_response", new context.Transaction.SetProperty("custom_response", new
{ {
name = "Bob le Magnifique" name = "Bob le Bricoleur"
}); });
context.HandleRequest(); context.HandleRequest();
@ -1368,7 +1362,7 @@ namespace OpenIddict.Server.FunctionalTests
}); });
// Assert // Assert
Assert.Equal("Bob le Magnifique", (string) response["name"]); Assert.Equal("Bob le Bricoleur", (string) response["name"]);
} }
[Fact] [Fact]
@ -1452,7 +1446,7 @@ namespace OpenIddict.Server.FunctionalTests
{ {
context.Transaction.SetProperty("custom_response", new context.Transaction.SetProperty("custom_response", new
{ {
name = "Bob le Magnifique" name = "Bob le Bricoleur"
}); });
context.HandleRequest(); context.HandleRequest();
@ -1471,7 +1465,7 @@ namespace OpenIddict.Server.FunctionalTests
}); });
// Assert // Assert
Assert.Equal("Bob le Magnifique", (string) response["name"]); Assert.Equal("Bob le Bricoleur", (string) response["name"]);
} }
[Fact] [Fact]

3019
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs

File diff suppressed because it is too large

16
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.cs

@ -101,6 +101,22 @@ namespace OpenIddict.Server.FunctionalTests
builder.SetOrder(int.MaxValue); builder.SetOrder(int.MaxValue);
}); });
options.AddEventHandler<HandleTokenRequestContext>(builder =>
{
builder.UseInlineHandler(context =>
{
var identity = new ClaimsIdentity("Bearer");
identity.AddClaim(Claims.Subject, "Bob le Magnifique");
context.Principal = new ClaimsPrincipal(identity);
context.HandleAuthentication();
return default;
});
builder.SetOrder(int.MaxValue);
});
}); });
} }

56
test/OpenIddict.Server.Owin.IntegrationTests/OpenIddictServerOwinIntegrationTests.Exchange.cs

@ -0,0 +1,56 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System.Threading.Tasks;
using OpenIddict.Abstractions;
using OpenIddict.Server.FunctionalTests;
using Owin;
using Xunit;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Server.OpenIddictServerEvents;
namespace OpenIddict.Server.Owin.FunctionalTests
{
public partial class OpenIddictServerOwinIntegrationTests : OpenIddictServerIntegrationTests
{
[Fact]
public async Task ExtractTokenRequest_MultipleClientCredentialsCauseAnError()
{
// Arrange
var client = CreateClient(options =>
{
options.EnableDegradedMode();
options.AddEventHandler<ExtractTokenRequestContext>(builder =>
{
builder.UseInlineHandler(context =>
{
var request = context.Transaction.GetOwinRequest();
request.Headers["Authorization"] = "Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW";
return default;
});
builder.SetOrder(int.MinValue);
});
});
// Act
var response = await client.PostAsync("/connect/token", new OpenIddictRequest
{
ClientId = "Fabrikam",
ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
GrantType = GrantTypes.Password,
Username = "johndoe",
Password = "A3ddj3w"
});
// Assert
Assert.Equal(Errors.InvalidRequest, response.Error);
Assert.Equal("Multiple client credentials cannot be specified.", response.ErrorDescription);
}
}
}
Loading…
Cancel
Save