From 9bae6740dba86eb532a59180fe707d650bb84d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Fri, 1 Dec 2017 15:38:35 +0100 Subject: [PATCH] Cache the TApplication/TToken entities in the request properties to avoid multiple stores calls --- src/OpenIddict.Core/OpenIddictConstants.cs | 3 + .../OpenIddictProvider.Authentication.cs | 4 + src/OpenIddict/OpenIddictProvider.Exchange.cs | 25 ++-- src/OpenIddict/OpenIddictProvider.Helpers.cs | 107 +++++++++--------- .../OpenIddictProvider.Introspection.cs | 18 ++- .../OpenIddictProvider.Revocation.cs | 18 ++- src/OpenIddict/OpenIddictProvider.cs | 7 +- .../OpenIddictProviderTests.Exchange.cs | 12 +- .../OpenIddictProviderTests.Introspection.cs | 7 +- .../OpenIddictProviderTests.Revocation.cs | 2 +- .../OpenIddictProviderTests.Serialization.cs | 24 ++-- .../OpenIddictProviderTests.cs | 14 +-- 12 files changed, 124 insertions(+), 117 deletions(-) diff --git a/src/OpenIddict.Core/OpenIddictConstants.cs b/src/OpenIddict.Core/OpenIddictConstants.cs index 098c2e85..56d38393 100644 --- a/src/OpenIddict.Core/OpenIddictConstants.cs +++ b/src/OpenIddict.Core/OpenIddictConstants.cs @@ -39,8 +39,11 @@ namespace OpenIddict.Core public static class Properties { + public const string Application = ".application"; public const string AuthenticationTicket = ".authentication_ticket"; public const string AuthorizationId = ".authorization_id"; + public const string ReferenceToken = ".reference_token"; + public const string Token = ".token"; } public static class PropertyTypes diff --git a/src/OpenIddict/OpenIddictProvider.Authentication.cs b/src/OpenIddict/OpenIddictProvider.Authentication.cs index e3ab1b60..10910073 100644 --- a/src/OpenIddict/OpenIddictProvider.Authentication.cs +++ b/src/OpenIddict/OpenIddictProvider.Authentication.cs @@ -267,6 +267,10 @@ namespace OpenIddict return; } + // Store the application entity as a request property to make it accessible + // from the other provider methods without having to call the store twice. + context.Request.SetProperty($"{OpenIddictConstants.Properties.Application}:{context.ClientId}", application); + // Ensure that the specified redirect_uri is valid and is associated with the client application. if (!await Applications.ValidateRedirectUriAsync(application, context.RedirectUri, context.HttpContext.RequestAborted)) { diff --git a/src/OpenIddict/OpenIddictProvider.Exchange.cs b/src/OpenIddict/OpenIddictProvider.Exchange.cs index dfb62d23..300d2b62 100644 --- a/src/OpenIddict/OpenIddictProvider.Exchange.cs +++ b/src/OpenIddict/OpenIddictProvider.Exchange.cs @@ -131,6 +131,10 @@ namespace OpenIddict return; } + // Store the application entity as a request property to make it accessible + // from the other provider methods without having to call the store twice. + context.Request.SetProperty($"{OpenIddictConstants.Properties.Application}:{context.ClientId}", application); + if (await Applications.IsPublicAsync(application, context.HttpContext.RequestAborted)) { // Note: public applications are not allowed to use the client credentials grant. @@ -222,24 +226,11 @@ namespace OpenIddict // Extract the token identifier from the authentication ticket. var identifier = context.Ticket.GetTokenId(); - Debug.Assert(!string.IsNullOrEmpty(identifier), - "The authentication ticket should contain a ticket identifier."); - - // Retrieve the authorization code/refresh token from the database and ensure it is still valid. - var token = await Tokens.FindByIdAsync(identifier, context.HttpContext.RequestAborted); - if (token == null) - { - Logger.LogError("The token request was rejected because the authorization code " + - "or refresh token '{Identifier}' was not found in the database.", identifier); - - context.Reject( - error: OpenIdConnectConstants.Errors.InvalidGrant, - description: context.Request.IsAuthorizationCodeGrantType() ? - "The specified authorization code is no longer valid." : - "The specified refresh token is no longer valid."); + Debug.Assert(!string.IsNullOrEmpty(identifier), "The authentication ticket should contain a token identifier."); - return; - } + // Retrieve the authorization code/refresh token from the request properties. + var token = context.Request.GetProperty($"{OpenIddictConstants.Properties.Token}:{identifier}"); + Debug.Assert(token != null, "The token shouldn't be null."); // If the authorization code/refresh token is already marked as redeemed, this may indicate that // it was compromised. In this case, revoke the authorization and all the associated tokens. diff --git a/src/OpenIddict/OpenIddictProvider.Helpers.cs b/src/OpenIddict/OpenIddictProvider.Helpers.cs index 369edd68..b9d07a98 100644 --- a/src/OpenIddict/OpenIddictProvider.Helpers.cs +++ b/src/OpenIddict/OpenIddictProvider.Helpers.cs @@ -5,11 +5,9 @@ */ using System; -using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; -using System.Security.Cryptography; using System.Threading.Tasks; using AspNet.Security.OpenIdConnect.Extensions; using AspNet.Security.OpenIdConnect.Primitives; @@ -52,11 +50,8 @@ namespace OpenIddict // If the client application is known, bind it to the authorization. if (!string.IsNullOrEmpty(request.ClientId)) { - var application = await Applications.FindByClientIdAsync(request.ClientId, context.RequestAborted); - if (application == null) - { - throw new InvalidOperationException("The client application cannot be retrieved from the database."); - } + var application = request.GetProperty($"{OpenIddictConstants.Properties.Application}:{request.ClientId}"); + Debug.Assert(application != null, "The client application shouldn't be null."); descriptor.ApplicationId = await Applications.GetIdAsync(application, context.RequestAborted); } @@ -174,11 +169,8 @@ namespace OpenIddict // If the client application is known, associate it with the token. if (!string.IsNullOrEmpty(request.ClientId)) { - var application = await Applications.FindByClientIdAsync(request.ClientId, context.RequestAborted); - if (application == null) - { - throw new InvalidOperationException("The client application cannot be retrieved from the database."); - } + var application = request.GetProperty($"{OpenIddictConstants.Properties.Application}:{request.ClientId}"); + Debug.Assert(application != null, "The client application shouldn't be null."); descriptor.ApplicationId = await Applications.GetIdAsync(application, context.RequestAborted); } @@ -238,18 +230,24 @@ namespace OpenIddict if (options.UseReferenceTokens) { - // Retrieve the token entry from the database. - // If it cannot be found, assume the token is not valid. - try + // For introspection or revocation requests, this method may be called more than once. + // For reference tokens, this may result in multiple database calls being made. + // To optimize that, the token is added to the request properties to indicate that + // a database lookup was already made with the same identifier. If the marker exists, + // the property value (that may be null) is used instead of making a database call. + if (request.HasProperty($"{OpenIddictConstants.Properties.ReferenceToken}:{value}")) { - token = await Tokens.FindByReferenceIdAsync(value, context.RequestAborted); + token = request.GetProperty($"{OpenIddictConstants.Properties.ReferenceToken}:{value}"); } - // Swallow format-related exceptions to ensure badly formed - // or tampered tokens don't cause an exception at this stage. - catch + else { - return null; + // Retrieve the token entry from the database. If it + // cannot be found, assume the token is not valid. + token = await Tokens.FindByReferenceIdAsync(value, context.RequestAborted); + + // Store the token as a request property so it can be retrieved if this method is called another time. + request.AddProperty($"{OpenIddictConstants.Properties.ReferenceToken}:{value}", token); } if (token == null) @@ -289,6 +287,8 @@ namespace OpenIddict return null; } + + request.SetProperty($"{OpenIddictConstants.Properties.Token}:{identifier}", token); } else if (type == OpenIdConnectConstants.TokenUsages.AuthorizationCode || @@ -302,21 +302,38 @@ namespace OpenIddict return null; } - // Retrieve the authorization code/refresh token entry from the database. - // If it cannot be found, assume the authorization code/refresh token is not valid. - token = await Tokens.FindByIdAsync(ticket.GetTokenId(), context.RequestAborted); - if (token == null) + identifier = ticket.GetTokenId(); + if (string.IsNullOrEmpty(identifier)) { - Logger.LogInformation("The token '{Identifier}' cannot be found in the database.", ticket.GetTokenId()); + Logger.LogWarning("The identifier associated with the received token cannot be retrieved. " + + "This may indicate that the token entry is corrupted."); return null; } - identifier = await Tokens.GetIdAsync(token, context.RequestAborted); - if (string.IsNullOrEmpty(identifier)) + // For introspection or revocation requests, this method may be called more than once. + // For codes/refresh tokens, this may result in multiple database calls being made. + // To optimize that, the token is added to the request properties to indicate that + // a database lookup was already made with the same identifier. If the marker exists, + // the property value (that may be null) is used instead of making a database call. + if (request.HasProperty($"{OpenIddictConstants.Properties.Token}:{identifier}")) { - Logger.LogWarning("The identifier associated with the received token cannot be retrieved. " + - "This may indicate that the token entry is corrupted."); + token = request.GetProperty($"{OpenIddictConstants.Properties.Token}:{identifier}"); + } + + // Otherwise, retrieve the authorization code/refresh token entry from the database. + // If it cannot be found, assume the authorization code/refresh token is not valid. + else + { + token = await Tokens.FindByIdAsync(identifier, context.RequestAborted); + + // Store the token as a request property so it can be retrieved if this method is called another time. + request.AddProperty($"{OpenIddictConstants.Properties.Token}:{identifier}", token); + } + + if (token == null) + { + Logger.LogInformation("The token '{Identifier}' cannot be found in the database.", ticket.GetTokenId()); return null; } @@ -413,22 +430,10 @@ namespace OpenIddict return true; } - private async Task TryRedeemTokenAsync([NotNull] AuthenticationTicket ticket, [NotNull] HttpContext context) + private async Task TryRedeemTokenAsync([NotNull] TToken token, [NotNull] HttpContext context) { - // Note: if the token identifier or the token itself - // cannot be found, return true as the token doesn't need - // to be revoked if it doesn't exist or is already invalid. - var identifier = ticket.GetTokenId(); - if (string.IsNullOrEmpty(identifier)) - { - return true; - } - - var token = await Tokens.FindByIdAsync(identifier, context.RequestAborted); - if (token == null) - { - return true; - } + var identifier = await Tokens.GetIdAsync(token, context.RequestAborted); + Debug.Assert(!string.IsNullOrEmpty(identifier), "The token identifier shouldn't be null or empty."); try { @@ -449,19 +454,11 @@ namespace OpenIddict } private async Task TryExtendTokenAsync( - [NotNull] AuthenticationTicket ticket, [NotNull] HttpContext context, [NotNull] OpenIddictOptions options) + [NotNull] TToken token, [NotNull] AuthenticationTicket ticket, + [NotNull] HttpContext context, [NotNull] OpenIddictOptions options) { var identifier = ticket.GetTokenId(); - if (string.IsNullOrEmpty(identifier)) - { - return false; - } - - var token = await Tokens.FindByIdAsync(identifier, context.RequestAborted); - if (token == null) - { - return false; - } + Debug.Assert(!string.IsNullOrEmpty(identifier), "The token identifier shouldn't be null or empty."); try { diff --git a/src/OpenIddict/OpenIddictProvider.Introspection.cs b/src/OpenIddict/OpenIddictProvider.Introspection.cs index bff6c072..292d677d 100644 --- a/src/OpenIddict/OpenIddictProvider.Introspection.cs +++ b/src/OpenIddict/OpenIddictProvider.Introspection.cs @@ -12,6 +12,7 @@ using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Server; using JetBrains.Annotations; using Microsoft.Extensions.Logging; +using OpenIddict.Core; namespace OpenIddict { @@ -63,6 +64,10 @@ namespace OpenIddict return; } + // Store the application entity as a request property to make it accessible + // from the other provider methods without having to call the store twice. + context.Request.SetProperty($"{OpenIddictConstants.Properties.Application}:{context.ClientId}", application); + // Reject introspection requests sent by public applications. if (await Applications.IsPublicAsync(application, context.HttpContext.RequestAborted)) { @@ -99,8 +104,8 @@ namespace OpenIddict Debug.Assert(context.Ticket != null, "The authentication ticket shouldn't be null."); Debug.Assert(!string.IsNullOrEmpty(context.Request.ClientId), "The client_id parameter shouldn't be null."); - var identifier = context.Ticket.GetProperty(OpenIdConnectConstants.Properties.TokenId); - Debug.Assert(!string.IsNullOrEmpty(identifier), "The token identifier shouldn't be null or empty."); + var identifier = context.Ticket.GetTokenId(); + Debug.Assert(!string.IsNullOrEmpty(identifier), "The authentication ticket should contain a token identifier."); // Note: the OpenID Connect server middleware allows authorized presenters (e.g relying parties) to introspect access tokens // but OpenIddict uses a stricter policy that only allows resource servers to use the introspection endpoint, unless the ticket @@ -124,10 +129,11 @@ namespace OpenIddict // When the received ticket is revocable, ensure it is still valid. if (options.UseReferenceTokens || context.Ticket.IsAuthorizationCode() || context.Ticket.IsRefreshToken()) { - // Retrieve the token from the database using the unique identifier stored in the authentication ticket: - // if the corresponding entry cannot be found, return Active = false to indicate that is is no longer valid. - var token = await Tokens.FindByIdAsync(identifier, context.HttpContext.RequestAborted); - if (token == null || !await Tokens.IsValidAsync(token, context.HttpContext.RequestAborted)) + // Retrieve the token from the request properties. If it's marked as invalid, return active = false. + var token = context.Request.GetProperty($"{OpenIddictConstants.Properties.Token}:{identifier}"); + Debug.Assert(token != null, "The token shouldn't be null."); + + if (!await Tokens.IsValidAsync(token, context.HttpContext.RequestAborted)) { Logger.LogInformation("The token '{Identifier}' was declared as inactive because it was revoked.", identifier); diff --git a/src/OpenIddict/OpenIddictProvider.Revocation.cs b/src/OpenIddict/OpenIddictProvider.Revocation.cs index c3d1c960..945153ca 100644 --- a/src/OpenIddict/OpenIddictProvider.Revocation.cs +++ b/src/OpenIddict/OpenIddictProvider.Revocation.cs @@ -12,6 +12,7 @@ using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Server; using JetBrains.Annotations; using Microsoft.Extensions.Logging; +using OpenIddict.Core; namespace OpenIddict { @@ -89,6 +90,10 @@ namespace OpenIddict return; } + // Store the application entity as a request property to make it accessible + // from the other provider methods without having to call the store twice. + context.Request.SetProperty($"{OpenIddictConstants.Properties.Application}:{context.ClientId}", application); + // Reject revocation requests containing a client_secret if the application is a public client. if (await Applications.IsPublicAsync(application, context.HttpContext.RequestAborted)) { @@ -175,13 +180,14 @@ namespace OpenIddict } // Extract the token identifier from the authentication ticket. - var identifier = context.Ticket.GetProperty(OpenIdConnectConstants.Properties.TokenId); - Debug.Assert(!string.IsNullOrEmpty(identifier), "The token should contain a ticket identifier."); + var identifier = context.Ticket.GetTokenId(); + Debug.Assert(!string.IsNullOrEmpty(identifier), "The authentication ticket should contain a token identifier."); + + // Retrieve the token from the request properties. If it's already marked as revoked, directly return a 200 response. + var token = context.Request.GetProperty($"{OpenIddictConstants.Properties.Token}:{identifier}"); + Debug.Assert(token != null, "The token shouldn't be null."); - // Retrieve the token from the database. If the token cannot be found, - // assume it is invalid and consider the revocation as successful. - var token = await Tokens.FindByIdAsync(identifier, context.HttpContext.RequestAborted); - if (token == null || await Tokens.IsRevokedAsync(token, context.HttpContext.RequestAborted)) + if (await Tokens.IsRevokedAsync(token, context.HttpContext.RequestAborted)) { Logger.LogInformation("The token '{Identifier}' was not revoked because " + "it was already marked as invalid.", identifier); diff --git a/src/OpenIddict/OpenIddictProvider.cs b/src/OpenIddict/OpenIddictProvider.cs index 9356c8bc..c9332356 100644 --- a/src/OpenIddict/OpenIddictProvider.cs +++ b/src/OpenIddict/OpenIddictProvider.cs @@ -147,12 +147,15 @@ namespace OpenIddict return; } + var token = context.Request.GetProperty($"{OpenIddictConstants.Properties.Token}:{context.Ticket.GetTokenId()}"); + Debug.Assert(token != null, "The token shouldn't be null."); + // If rolling tokens are enabled or if the request is a grant_type=authorization_code request, // mark the authorization code or the refresh token as redeemed to prevent future reuses. // See https://tools.ietf.org/html/rfc6749#section-6 for more information. if (options.UseRollingTokens || context.Request.IsAuthorizationCodeGrantType()) { - if (!await TryRedeemTokenAsync(context.Ticket, context.HttpContext)) + if (!await TryRedeemTokenAsync(token, context.HttpContext)) { context.Reject( error: OpenIdConnectConstants.Errors.InvalidGrant, @@ -183,7 +186,7 @@ namespace OpenIddict // with a new expiration date if sliding expiration was not disabled. else if (options.UseSlidingExpiration && context.Request.IsRefreshTokenGrantType()) { - if (!await TryExtendTokenAsync(context.Ticket, context.HttpContext, options)) + if (!await TryExtendTokenAsync(token, context.Ticket, context.HttpContext, options)) { context.Reject( error: OpenIdConnectConstants.Errors.InvalidGrant, diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs index 0a2d391e..96d1baea 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs +++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs @@ -687,7 +687,7 @@ namespace OpenIddict.Tests Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error); Assert.Equal("The specified authorization code has already been redeemed.", response.ErrorDescription); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.IsRedeemedAsync(token, It.IsAny()), Times.Once()); } @@ -753,7 +753,7 @@ namespace OpenIddict.Tests Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error); Assert.Equal("The specified refresh token has already been redeemed.", response.ErrorDescription); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.IsRedeemedAsync(token, It.IsAny()), Times.Once()); } @@ -1005,7 +1005,7 @@ namespace OpenIddict.Tests Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error); Assert.Equal("The specified authorization code has already been redeemed.", response.ErrorDescription); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.IsRedeemedAsync(tokens[0], It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[0], It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[1], It.IsAny()), Times.Once()); @@ -1092,7 +1092,7 @@ namespace OpenIddict.Tests Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error); Assert.Equal("The specified refresh token has already been redeemed.", response.ErrorDescription); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.IsRedeemedAsync(tokens[0], It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[0], It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[1], It.IsAny()), Times.Once()); @@ -1167,7 +1167,7 @@ namespace OpenIddict.Tests Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error); Assert.Equal("The specified authorization code is no longer valid.", response.ErrorDescription); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.IsRedeemedAsync(token, It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.IsValidAsync(token, It.IsAny()), Times.Once()); } @@ -1237,7 +1237,7 @@ namespace OpenIddict.Tests Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error); Assert.Equal("The specified refresh token is no longer valid.", response.ErrorDescription); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.IsValidAsync(token, It.IsAny()), Times.Once()); } diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs index a329aa86..cb9c5f3d 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs +++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs @@ -395,7 +395,7 @@ namespace OpenIddict.Tests Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); - Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI", It.IsAny()), Times.Exactly(3)); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI", It.IsAny()), Times.Once()); } [Fact] @@ -476,7 +476,6 @@ namespace OpenIddict.Tests Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI", It.IsAny()), Times.Once()); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.IsValidAsync(token, It.IsAny()), Times.Once()); } @@ -613,7 +612,7 @@ namespace OpenIddict.Tests Assert.Single(response.GetParameters()); Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.IsValidAsync(token, It.IsAny()), Times.Once()); } @@ -753,7 +752,7 @@ namespace OpenIddict.Tests Assert.Single(response.GetParameters()); Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.IsValidAsync(token, It.IsAny()), Times.Once()); } } diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Revocation.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Revocation.cs index 1133b382..ec0a7cfb 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.Revocation.cs +++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Revocation.cs @@ -478,7 +478,7 @@ namespace OpenIddict.Tests // Assert Assert.Empty(response.GetParameters()); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.RevokeAsync(token, It.IsAny()), Times.Once()); } } diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs index 46e711e1..0740aa22 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs +++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs @@ -194,7 +194,7 @@ namespace OpenIddict.Tests Assert.Single(response.GetParameters()); Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); - Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); } @@ -252,7 +252,7 @@ namespace OpenIddict.Tests Assert.Single(response.GetParameters()); Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); - Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); } @@ -317,7 +317,7 @@ namespace OpenIddict.Tests Assert.Single(response.GetParameters()); Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); - Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once()); } @@ -590,7 +590,7 @@ namespace OpenIddict.Tests Assert.Single(response.GetParameters()); Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); - Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); } @@ -648,7 +648,7 @@ namespace OpenIddict.Tests Assert.Single(response.GetParameters()); Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); - Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); } @@ -713,7 +713,7 @@ namespace OpenIddict.Tests Assert.Single(response.GetParameters()); Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); - Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once()); } @@ -993,8 +993,7 @@ namespace OpenIddict.Tests Assert.Equal(1483228800, (long) response[OpenIdConnectConstants.Claims.IssuedAt]); Assert.Equal(1484006400, (long) response[OpenIdConnectConstants.Claims.ExpiresAt]); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce()); - Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.Once()); format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once()); } @@ -1170,7 +1169,7 @@ namespace OpenIddict.Tests Assert.Single(response.GetParameters()); Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); - Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); } @@ -1228,7 +1227,7 @@ namespace OpenIddict.Tests Assert.Single(response.GetParameters()); Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); - Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); } @@ -1293,7 +1292,7 @@ namespace OpenIddict.Tests Assert.Single(response.GetParameters()); Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]); - Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once()); } @@ -1573,8 +1572,7 @@ namespace OpenIddict.Tests Assert.Equal(1483228800, (long) response[OpenIdConnectConstants.Claims.IssuedAt]); Assert.Equal(1484006400, (long) response[OpenIdConnectConstants.Claims.ExpiresAt]); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce()); - Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.Once()); format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once()); } diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.cs index 58a36378..e8a104d2 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.cs +++ b/test/OpenIddict.Tests/OpenIddictProviderTests.cs @@ -446,7 +446,7 @@ namespace OpenIddict.Tests }); // Assert - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny()), Times.Once()); } @@ -521,7 +521,7 @@ namespace OpenIddict.Tests Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error); Assert.Equal("The specified authorization code is no longer valid.", response.ErrorDescription); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny()), Times.Once()); } @@ -587,7 +587,7 @@ namespace OpenIddict.Tests // Assert Assert.NotNull(response.RefreshToken); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny()), Times.Once()); } @@ -657,7 +657,7 @@ namespace OpenIddict.Tests Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error); Assert.Equal("The specified refresh token is no longer valid.", response.ErrorDescription); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny()), Times.Once()); } @@ -715,7 +715,7 @@ namespace OpenIddict.Tests // Assert Assert.Null(response.RefreshToken); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny()), Times.Never()); } @@ -791,7 +791,7 @@ namespace OpenIddict.Tests // Assert Assert.NotNull(response.RefreshToken); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[1], It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[2], It.IsAny()), Times.Once()); } @@ -860,7 +860,7 @@ namespace OpenIddict.Tests // Assert Assert.Null(response.RefreshToken); - Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[1], It.IsAny()), Times.Never()); Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[2], It.IsAny()), Times.Never()); }