diff --git a/build/version.props b/build/version.props
index b0387258..bf0d2289 100644
--- a/build/version.props
+++ b/build/version.props
@@ -2,7 +2,7 @@
1.0.0
- rc1
+ rc2
$(VersionSuffix)-$(BuildNumber)
diff --git a/src/OpenIddict/OpenIddictProvider.Helpers.cs b/src/OpenIddict/OpenIddictProvider.Helpers.cs
index b2fb74de..6b0ee4f2 100644
--- a/src/OpenIddict/OpenIddictProvider.Helpers.cs
+++ b/src/OpenIddict/OpenIddictProvider.Helpers.cs
@@ -5,7 +5,6 @@
*/
using System;
-using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
@@ -37,11 +36,6 @@ namespace OpenIddict
var authorizations = context.RequestServices.GetRequiredService>();
var logger = context.RequestServices.GetRequiredService>>();
- if (options.DisableTokenRevocation)
- {
- return;
- }
-
var descriptor = new OpenIddictAuthorizationDescriptor
{
Status = OpenIddictConstants.Statuses.Valid,
@@ -228,74 +222,119 @@ namespace OpenIddict
}
private async Task ReceiveTokenAsync(
- [NotNull] string value, [NotNull] OpenIddictOptions options,
- [NotNull] HttpContext context, [NotNull] OpenIdConnectRequest request,
+ [NotNull] string type, [NotNull] string value,
+ [NotNull] OpenIddictOptions options, [NotNull] HttpContext context,
+ [NotNull] OpenIdConnectRequest request,
[NotNull] ISecureDataFormat format)
{
var logger = context.RequestServices.GetRequiredService>>();
var tokens = context.RequestServices.GetRequiredService>();
- if (!options.UseReferenceTokens)
- {
- return null;
- }
+ Debug.Assert(type == OpenIdConnectConstants.TokenUsages.AccessToken ||
+ type == OpenIdConnectConstants.TokenUsages.AuthorizationCode ||
+ type == OpenIdConnectConstants.TokenUsages.RefreshToken,
+ "Only authorization codes, access and refresh tokens should be validated using this method.");
- string hash;
- try
+ string identifier;
+ AuthenticationTicket ticket;
+ TToken token;
+
+ if (options.UseReferenceTokens)
{
- // Compute the digest of the received token and use it
- // to retrieve the reference token from the database.
- using (var algorithm = SHA256.Create())
+ string hash;
+ try
{
- hash = Convert.ToBase64String(algorithm.ComputeHash(Base64UrlEncoder.DecodeBytes(value)));
+ // Compute the digest of the received token and use it
+ // to retrieve the reference token from the database.
+ using (var algorithm = SHA256.Create())
+ {
+ hash = Convert.ToBase64String(algorithm.ComputeHash(Base64UrlEncoder.DecodeBytes(value)));
+ }
}
- }
- // Swallow format-related exceptions to ensure badly formed
- // or tampered tokens don't cause an exception at this stage.
- catch
- {
- return null;
- }
+ // Swallow format-related exceptions to ensure badly formed
+ // or tampered tokens don't cause an exception at this stage.
+ catch
+ {
+ return null;
+ }
- // Retrieve the token entry from the database. If it
- // cannot be found, assume the token is not valid.
- var token = await tokens.FindByHashAsync(hash, context.RequestAborted);
- if (token == null)
- {
- logger.LogInformation("The reference token corresponding to the '{Hash}' hashed " +
- "identifier cannot be found in the database.", hash);
+ // Retrieve the token entry from the database. If it
+ // cannot be found, assume the token is not valid.
+ token = await tokens.FindByHashAsync(hash, context.RequestAborted);
+ if (token == null)
+ {
+ logger.LogInformation("The reference token corresponding to the '{Hash}' hashed " +
+ "identifier cannot be found in the database.", hash);
- return null;
- }
+ return null;
+ }
- var identifier = await tokens.GetIdAsync(token, context.RequestAborted);
- if (string.IsNullOrEmpty(identifier))
- {
- 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))
+ {
+ logger.LogWarning("The identifier associated with the received token cannot be retrieved. " +
+ "This may indicate that the token entry is corrupted.");
+
+ return null;
+ }
+
+ // Extract the encrypted payload from the token. If it's null or empty,
+ // assume the token is not a reference token and consider it as invalid.
+ var ciphertext = await tokens.GetCiphertextAsync(token, context.RequestAborted);
+ if (string.IsNullOrEmpty(ciphertext))
+ {
+ logger.LogWarning("The ciphertext associated with the token '{Identifier}' cannot be retrieved. " +
+ "This may indicate that the token is not a reference token.", identifier);
+
+ return null;
+ }
+
+ ticket = format.Unprotect(ciphertext);
+ if (ticket == null)
+ {
+ logger.LogWarning("The ciphertext associated with the token '{Identifier}' cannot be decrypted. " +
+ "This may indicate that the token entry is corrupted or tampered.",
+ await tokens.GetIdAsync(token, context.RequestAborted));
+
+ return null;
+ }
}
- // Extract the encrypted payload from the token. If it's null or empty,
- // assume the token is not a reference token and consider it as invalid.
- var ciphertext = await tokens.GetCiphertextAsync(token, context.RequestAborted);
- if (string.IsNullOrEmpty(ciphertext))
+ else if (type == OpenIdConnectConstants.TokenUsages.AuthorizationCode ||
+ type == OpenIdConnectConstants.TokenUsages.RefreshToken)
{
- logger.LogWarning("The ciphertext associated with the token '{Identifier}' cannot be retrieved. " +
- "This may indicate that the token is not a reference token.", identifier);
+ ticket = format.Unprotect(value);
+ if (ticket == null)
+ {
+ logger.LogTrace("The received token was invalid or malformed: {Token}.", value);
- return null;
+ 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)
+ {
+ logger.LogInformation("The token '{Identifier}' cannot be found in the database.", ticket.GetTokenId());
+
+ return null;
+ }
+
+ identifier = await tokens.GetIdAsync(token, context.RequestAborted);
+ if (string.IsNullOrEmpty(identifier))
+ {
+ logger.LogWarning("The identifier associated with the received token cannot be retrieved. " +
+ "This may indicate that the token entry is corrupted.");
+
+ return null;
+ }
}
- var ticket = format.Unprotect(ciphertext);
- if (ticket == null)
+ else
{
- logger.LogWarning("The ciphertext associated with the token '{Identifier}' cannot be decrypted. " +
- "This may indicate that the token entry is corrupted or tampered.",
- await tokens.GetIdAsync(token, context.RequestAborted));
-
return null;
}
@@ -311,9 +350,9 @@ namespace OpenIddict
ticket.SetProperty(OpenIddictConstants.Properties.AuthorizationId,
await tokens.GetAuthorizationIdAsync(token, context.RequestAborted));
- logger.LogTrace("The reference token '{Identifier}' was successfully retrieved " +
- "from the database and decrypted: {Claims} ; {Properties}.",
- identifier, ticket.Principal.Claims, ticket.Properties.Items);
+ logger.LogTrace("The token '{Identifier}' was successfully decrypted and " +
+ "retrieved from the database: {Claims} ; {Properties}.",
+ ticket.GetTokenId(), ticket.Principal.Claims, ticket.Properties.Items);
return ticket;
}
@@ -370,12 +409,6 @@ namespace OpenIddict
foreach (var token in await tokens.FindByAuthorizationIdAsync(identifier, context.RequestAborted))
{
- // Don't overwrite the status of the token used in the token request.
- if (string.Equals(ticket.GetTokenId(), await tokens.GetIdAsync(token, context.RequestAborted)))
- {
- continue;
- }
-
try
{
await tokens.RevokeAsync(token, context.RequestAborted);
diff --git a/src/OpenIddict/OpenIddictProvider.Serialization.cs b/src/OpenIddict/OpenIddictProvider.Serialization.cs
index 0d7afa50..a45f3119 100644
--- a/src/OpenIddict/OpenIddictProvider.Serialization.cs
+++ b/src/OpenIddict/OpenIddictProvider.Serialization.cs
@@ -18,84 +18,70 @@ namespace OpenIddict
public override async Task DeserializeAccessToken([NotNull] DeserializeAccessTokenContext context)
{
var options = (OpenIddictOptions) context.Options;
- if (!options.UseReferenceTokens)
+ if (options.DisableTokenRevocation)
{
return;
}
context.Ticket = await ReceiveTokenAsync(
+ OpenIdConnectConstants.TokenUsages.AccessToken,
context.AccessToken, options, context.HttpContext,
context.Request, context.DataFormat);
// Prevent the OpenID Connect server middleware from using
- // its default logic to deserialize the reference token.
- if (context.Ticket != null)
+ // its default logic to deserialize reference access tokens.
+ if (options.UseReferenceTokens)
{
context.HandleResponse();
}
-
- else
- {
- context.SkipToNextMiddleware();
- }
}
public override async Task DeserializeAuthorizationCode([NotNull] DeserializeAuthorizationCodeContext context)
{
var options = (OpenIddictOptions) context.Options;
- if (!options.UseReferenceTokens)
+ if (options.DisableTokenRevocation)
{
return;
}
context.Ticket = await ReceiveTokenAsync(
+ OpenIdConnectConstants.TokenUsages.AuthorizationCode,
context.AuthorizationCode, options, context.HttpContext,
context.Request, context.DataFormat);
- // Prevent the OpenID Connect server middleware from using
- // its default logic to deserialize the reference token.
- if (context.Ticket != null)
- {
- context.HandleResponse();
- }
-
- else
- {
- context.SkipToNextMiddleware();
- }
+ // Prevent the OpenID Connect server middleware from using its default logic.
+ context.HandleResponse();
}
public override async Task DeserializeRefreshToken([NotNull] DeserializeRefreshTokenContext context)
{
var options = (OpenIddictOptions) context.Options;
- if (!options.UseReferenceTokens)
+ if (options.DisableTokenRevocation)
{
return;
}
context.Ticket = await ReceiveTokenAsync(
+ OpenIdConnectConstants.TokenUsages.RefreshToken,
context.RefreshToken, options, context.HttpContext,
context.Request, context.DataFormat);
- // Prevent the OpenID Connect server middleware from using
- // its default logic to deserialize the reference token.
- if (context.Ticket != null)
- {
- context.HandleResponse();
- }
-
- else
- {
- context.SkipToNextMiddleware();
- }
+ // Prevent the OpenID Connect server middleware from using its default logic.
+ context.HandleResponse();
}
public override async Task SerializeAccessToken([NotNull] SerializeAccessTokenContext context)
{
+ var options = (OpenIddictOptions) context.Options;
+ if (options.DisableTokenRevocation)
+ {
+ return;
+ }
+
var token = await CreateTokenAsync(
OpenIdConnectConstants.TokenUsages.AccessToken,
- context.Ticket, (OpenIddictOptions) context.Options,
- context.HttpContext, context.Request, context.DataFormat);
+ context.Ticket, options, context.HttpContext,
+ context.Request, context.DataFormat);
// If a reference token was returned by CreateTokenAsync(),
// force the OpenID Connect server middleware to use it.
@@ -111,12 +97,18 @@ namespace OpenIddict
public override async Task SerializeAuthorizationCode([NotNull] SerializeAuthorizationCodeContext context)
{
+ var options = (OpenIddictOptions) context.Options;
+ if (options.DisableTokenRevocation)
+ {
+ return;
+ }
+
Debug.Assert(context.Request.IsAuthorizationRequest(), "The request should be an authorization request.");
var token = await CreateTokenAsync(
OpenIdConnectConstants.TokenUsages.AuthorizationCode,
- context.Ticket, (OpenIddictOptions) context.Options,
- context.HttpContext, context.Request, context.DataFormat);
+ context.Ticket, options, context.HttpContext,
+ context.Request, context.DataFormat);
// If a reference token was returned by CreateTokenAsync(),
// force the OpenID Connect server middleware to use it.
@@ -132,12 +124,18 @@ namespace OpenIddict
public override async Task SerializeRefreshToken([NotNull] SerializeRefreshTokenContext context)
{
+ var options = (OpenIddictOptions) context.Options;
+ if (options.DisableTokenRevocation)
+ {
+ return;
+ }
+
Debug.Assert(context.Request.IsTokenRequest(), "The request should be a token request.");
var token = await CreateTokenAsync(
OpenIdConnectConstants.TokenUsages.RefreshToken,
- context.Ticket, (OpenIddictOptions) context.Options,
- context.HttpContext, context.Request, context.DataFormat);
+ context.Ticket, options, context.HttpContext,
+ context.Request, context.DataFormat);
// If a reference token was returned by CreateTokenAsync(),
// force the OpenID Connect server middleware to use it.
diff --git a/src/OpenIddict/OpenIddictProvider.cs b/src/OpenIddict/OpenIddictProvider.cs
index 0af6a545..37c72398 100644
--- a/src/OpenIddict/OpenIddictProvider.cs
+++ b/src/OpenIddict/OpenIddictProvider.cs
@@ -110,7 +110,9 @@ namespace OpenIddict
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
- description: "The specified authorization code is no longer valid.");
+ description: context.Request.IsAuthorizationCodeGrantType() ?
+ "The specified authorization code is no longer valid." :
+ "The specified refresh token is no longer valid.");
return;
}
diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs
index 9c483d25..1d4d0513 100644
--- a/test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs
+++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs
@@ -562,7 +562,7 @@ namespace OpenIddict.Tests
// Assert
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error);
- Assert.Equal("The specified authorization code is no longer valid.", response.ErrorDescription);
+ Assert.Equal("The specified authorization code is invalid.", response.ErrorDescription);
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.Once());
}
@@ -619,7 +619,7 @@ namespace OpenIddict.Tests
// Assert
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error);
- Assert.Equal("The specified refresh token is no longer valid.", response.ErrorDescription);
+ Assert.Equal("The specified refresh token is invalid.", response.ErrorDescription);
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.Once());
}
@@ -649,6 +649,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny()))
.ReturnsAsync(true);
});
@@ -686,7 +689,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.Once());
+ Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.IsRedeemedAsync(token, It.IsAny()), Times.Once());
}
@@ -714,6 +717,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
+
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny()))
.ReturnsAsync(true);
});
@@ -749,7 +755,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.Once());
+ Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.IsRedeemedAsync(token, It.IsAny()), Times.Once());
}
@@ -800,6 +806,12 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+
+ instance.Setup(mock => mock.GetAuthorizationIdAsync(token, It.IsAny()))
+ .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0");
+
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny()))
.ReturnsAsync(true);
@@ -877,6 +889,12 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+
+ instance.Setup(mock => mock.GetAuthorizationIdAsync(token, It.IsAny()))
+ .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0");
+
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny()))
.ReturnsAsync(true);
@@ -935,6 +953,12 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()))
.ReturnsAsync(tokens[0]);
+ instance.Setup(mock => mock.GetIdAsync(tokens[0], It.IsAny()))
+ .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+
+ instance.Setup(mock => mock.GetAuthorizationIdAsync(tokens[0], It.IsAny()))
+ .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0");
+
instance.Setup(mock => mock.IsRedeemedAsync(tokens[0], It.IsAny()))
.ReturnsAsync(true);
@@ -983,7 +1007,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.Once());
+ Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce());
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());
@@ -1018,6 +1042,12 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()))
.ReturnsAsync(tokens[0]);
+ instance.Setup(mock => mock.GetIdAsync(tokens[0], It.IsAny()))
+ .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+
+ instance.Setup(mock => mock.GetAuthorizationIdAsync(tokens[0], It.IsAny()))
+ .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0");
+
instance.Setup(mock => mock.IsRedeemedAsync(tokens[0], It.IsAny()))
.ReturnsAsync(true);
@@ -1064,7 +1094,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.Once());
+ Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce());
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());
@@ -1096,6 +1126,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny()))
.ReturnsAsync(false);
@@ -1136,7 +1169,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.Once());
+ Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.IsRedeemedAsync(token, It.IsAny()), Times.Once());
Mock.Get(manager).Verify(mock => mock.IsValidAsync(token, It.IsAny()), Times.Once());
}
@@ -1165,6 +1198,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
+
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny()))
.ReturnsAsync(false);
@@ -1203,7 +1239,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.Once());
+ Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.IsValidAsync(token, It.IsAny()), Times.Once());
}
@@ -1250,6 +1286,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
+
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny()))
.ReturnsAsync(true);
});
diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs
index 8392419f..14aa5a64 100644
--- a/test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs
+++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs
@@ -573,6 +573,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny()))
.ReturnsAsync(false);
});
@@ -612,7 +615,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.Once());
+ Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.IsValidAsync(token, It.IsAny()), Times.Once());
}
@@ -710,6 +713,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny()))
.ReturnsAsync(false);
});
@@ -749,7 +755,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.Once());
+ Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce());
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 389d2159..8965cf5a 100644
--- a/test/OpenIddict.Tests/OpenIddictProviderTests.Revocation.cs
+++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Revocation.cs
@@ -457,6 +457,9 @@ namespace OpenIddict.Tests
{
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()))
.ReturnsAsync(token);
+
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
});
var server = CreateAuthorizationServer(builder =>
@@ -477,7 +480,7 @@ namespace OpenIddict.Tests
// Assert
Assert.Empty(response.GetParameters());
- Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.Once());
+ Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce());
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 c4c42043..7f78f116 100644
--- a/test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs
+++ b/test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs
@@ -88,7 +88,7 @@ namespace OpenIddict.Tests
OpenIdConnectServerDefaults.AuthenticationScheme);
ticket.SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56");
- ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.RefreshToken);
+ ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.AccessToken);
var format = new Mock>();
@@ -124,7 +124,7 @@ namespace OpenIddict.Tests
builder.Services.AddSingleton(manager);
- builder.Configure(options => options.RefreshTokenFormat = format.Object);
+ builder.Configure(options => options.AccessTokenFormat = format.Object);
});
var client = new OpenIdConnectClient(server.CreateClient());
@@ -146,7 +146,7 @@ namespace OpenIddict.Tests
}
[Fact]
- public async Task DeserializeAccessToken_ReturnsNullForMissingTokenIdentifier()
+ public async Task DeserializeAccessToken_ReturnsNullForMissingReferenceTokenIdentifier()
{
// Arrange
var token = new OpenIddictToken();
@@ -201,7 +201,7 @@ namespace OpenIddict.Tests
}
[Fact]
- public async Task DeserializeAccessToken_ReturnsNullForMissingTokenCiphertext()
+ public async Task DeserializeAccessToken_ReturnsNullForMissingReferenceTokenCiphertext()
{
// Arrange
var token = new OpenIddictToken();
@@ -259,7 +259,7 @@ namespace OpenIddict.Tests
}
[Fact]
- public async Task DeserializeAccessToken_ReturnsNullForInvalidTokenCiphertext()
+ public async Task DeserializeAccessToken_ReturnsNullForInvalidReferenceTokenCiphertext()
{
// Arrange
var format = new Mock>();
@@ -325,7 +325,7 @@ namespace OpenIddict.Tests
}
[Fact]
- public async Task DeserializeAccessToken_ReturnsExpectedToken()
+ public async Task DeserializeAccessToken_ReturnsExpectedReferenceToken()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
@@ -337,7 +337,6 @@ namespace OpenIddict.Tests
OpenIdConnectServerDefaults.AuthenticationScheme);
ticket.SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56");
- ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.RefreshToken);
var format = new Mock>();
@@ -471,7 +470,7 @@ namespace OpenIddict.Tests
}
[Fact]
- public async Task DeserializeAuthorizationCode_AuthorizationCodeIsNotRetrievedFromDatabaseWhenReferenceTokensAreDisabled()
+ public async Task DeserializeAuthorizationCode_AuthorizationCodeIsNotRetrievedUsingHashWhenReferenceTokensAreDisabled()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
@@ -483,7 +482,6 @@ namespace OpenIddict.Tests
OpenIdConnectServerDefaults.AuthenticationScheme);
ticket.SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56");
- ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.RefreshToken);
var format = new Mock>();
@@ -497,6 +495,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny()))
.ReturnsAsync(true);
});
@@ -541,7 +542,7 @@ namespace OpenIddict.Tests
}
[Fact]
- public async Task DeserializeAuthorizationCode_ReturnsNullForMissingTokenIdentifier()
+ public async Task DeserializeAuthorizationCode_ReturnsNullForMissingReferenceTokenIdentifier()
{
// Arrange
var token = new OpenIddictToken();
@@ -596,7 +597,7 @@ namespace OpenIddict.Tests
}
[Fact]
- public async Task DeserializeAuthorizationCode_ReturnsNullForMissingTokenCiphertext()
+ public async Task DeserializeAuthorizationCode_ReturnsNullForMissingReferenceTokenCiphertext()
{
// Arrange
var token = new OpenIddictToken();
@@ -654,7 +655,7 @@ namespace OpenIddict.Tests
}
[Fact]
- public async Task DeserializeAuthorizationCode_ReturnsNullForInvalidTokenCiphertext()
+ public async Task DeserializeAuthorizationCode_ReturnsNullForInvalidReferenceTokenCiphertext()
{
// Arrange
var format = new Mock>();
@@ -720,7 +721,7 @@ namespace OpenIddict.Tests
}
[Fact]
- public async Task DeserializeAuthorizationCode_ReturnsExpectedToken()
+ public async Task DeserializeAuthorizationCode_ReturnsExpectedReferenceToken()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
@@ -731,9 +732,6 @@ namespace OpenIddict.Tests
new AuthenticationProperties(),
OpenIdConnectServerDefaults.AuthenticationScheme);
- ticket.SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56");
- ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.RefreshToken);
-
var format = new Mock>();
format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"))
@@ -816,6 +814,192 @@ namespace OpenIddict.Tests
format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once());
}
+ [Fact]
+ public async Task DeserializeAuthorizationCode_ReturnsNullForMissingTokenIdentifier()
+ {
+ // Arrange
+ var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
+ identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "Bob le Bricoleur");
+
+ var ticket = new AuthenticationTicket(
+ new ClaimsPrincipal(identity),
+ new AuthenticationProperties(),
+ OpenIdConnectServerDefaults.AuthenticationScheme);
+
+ var format = new Mock>();
+
+ format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"))
+ .Returns(ticket);
+
+ var server = CreateAuthorizationServer(builder =>
+ {
+ builder.Services.AddSingleton(CreateApplicationManager(instance =>
+ {
+ var application = new OpenIddictApplication();
+
+ instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny()))
+ .ReturnsAsync(application);
+
+ instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny()))
+ .ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
+
+ instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny()))
+ .ReturnsAsync(true);
+ }));
+
+ builder.Configure(options => options.AuthorizationCodeFormat = format.Object);
+ });
+
+ var client = new OpenIdConnectClient(server.CreateClient());
+
+ // Act
+ var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest
+ {
+ ClientId = "Fabrikam",
+ ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
+ Token = "2YotnFZFEjr1zCsicMWpAA",
+ TokenTypeHint = OpenIdConnectConstants.TokenTypeHints.AuthorizationCode
+ });
+
+ // Assert
+ Assert.Single(response.GetParameters());
+ Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]);
+ }
+
+ [Fact]
+ public async Task DeserializeAuthorizationCode_ReturnsNullForInvalidTokenCiphertext()
+ {
+ // Arrange
+ var format = new Mock>();
+
+ format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"))
+ .Returns(value: null);
+
+ var server = CreateAuthorizationServer(builder =>
+ {
+ builder.Services.AddSingleton(CreateApplicationManager(instance =>
+ {
+ var application = new OpenIddictApplication();
+
+ instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny()))
+ .ReturnsAsync(application);
+
+ instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny()))
+ .ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
+
+ instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny()))
+ .ReturnsAsync(true);
+ }));
+
+ builder.Configure(options => options.AuthorizationCodeFormat = format.Object);
+ });
+
+ var client = new OpenIdConnectClient(server.CreateClient());
+
+ // Act
+ var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest
+ {
+ ClientId = "Fabrikam",
+ ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
+ Token = "2YotnFZFEjr1zCsicMWpAA",
+ TokenTypeHint = OpenIdConnectConstants.TokenTypeHints.AuthorizationCode
+ });
+
+ // Assert
+ Assert.Single(response.GetParameters());
+ Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]);
+
+ format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once());
+ }
+
+ [Fact]
+ public async Task DeserializeAuthorizationCode_ReturnsExpectedToken()
+ {
+ // Arrange
+ var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
+ identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "Bob le Bricoleur");
+
+ var ticket = new AuthenticationTicket(
+ new ClaimsPrincipal(identity),
+ new AuthenticationProperties(),
+ OpenIdConnectServerDefaults.AuthenticationScheme);
+
+ ticket.SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56");
+
+ var format = new Mock>();
+
+ format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"))
+ .Returns(ticket);
+
+ var token = new OpenIddictToken();
+
+ var manager = CreateTokenManager(instance =>
+ {
+ instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()))
+ .ReturnsAsync(token);
+
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+
+ instance.Setup(mock => mock.IsValidAsync(token, It.IsAny()))
+ .ReturnsAsync(true);
+
+ instance.Setup(mock => mock.GetCreationDateAsync(token, It.IsAny()))
+ .ReturnsAsync(new DateTimeOffset(2017, 01, 01, 00, 00, 00, TimeSpan.Zero));
+
+ instance.Setup(mock => mock.GetExpirationDateAsync(token, It.IsAny()))
+ .ReturnsAsync(new DateTimeOffset(2017, 01, 10, 00, 00, 00, TimeSpan.Zero));
+ });
+
+ var server = CreateAuthorizationServer(builder =>
+ {
+ builder.Services.AddSingleton(CreateApplicationManager(instance =>
+ {
+ var application = new OpenIddictApplication();
+
+ instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny()))
+ .ReturnsAsync(application);
+
+ instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny()))
+ .ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
+
+ instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny()))
+ .ReturnsAsync(true);
+ }));
+
+ builder.Services.AddSingleton(manager);
+
+ builder.Configure(options =>
+ {
+ options.SystemClock = Mock.Of(mock => mock.UtcNow ==
+ new DateTimeOffset(2017, 01, 05, 00, 00, 00, TimeSpan.Zero));
+
+ options.AuthorizationCodeFormat = format.Object;
+ });
+ });
+
+ var client = new OpenIdConnectClient(server.CreateClient());
+
+ // Act
+ var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest
+ {
+ ClientId = "Fabrikam",
+ ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
+ Token = "2YotnFZFEjr1zCsicMWpAA",
+ TokenTypeHint = OpenIdConnectConstants.TokenTypeHints.AuthorizationCode
+ });
+
+ // Assert
+ Assert.True((bool) response[OpenIdConnectConstants.Claims.Active]);
+ Assert.Equal("3E228451-1555-46F7-A471-951EFBA23A56", response[OpenIdConnectConstants.Claims.JwtId]);
+ 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());
+ format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once());
+ }
+
[Fact]
public async Task DeserializeRefreshToken_ReturnsNullForMalformedReferenceToken()
{
@@ -866,7 +1050,7 @@ namespace OpenIddict.Tests
}
[Fact]
- public async Task DeserializeRefreshToken_RefreshTokenIsNotRetrievedFromDatabaseWhenReferenceTokensAreDisabled()
+ public async Task DeserializeRefreshToken_RefreshTokenIsNotRetrievedUsingHashWhenReferenceTokensAreDisabled()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
@@ -878,7 +1062,6 @@ namespace OpenIddict.Tests
OpenIdConnectServerDefaults.AuthenticationScheme);
ticket.SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56");
- ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.RefreshToken);
var format = new Mock>();
@@ -892,6 +1075,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny()))
.ReturnsAsync(true);
});
@@ -936,7 +1122,7 @@ namespace OpenIddict.Tests
}
[Fact]
- public async Task DeserializeRefreshToken_ReturnsNullForMissingTokenIdentifier()
+ public async Task DeserializeRefreshToken_ReturnsNullForMissingReferenceTokenIdentifier()
{
// Arrange
var token = new OpenIddictToken();
@@ -991,7 +1177,7 @@ namespace OpenIddict.Tests
}
[Fact]
- public async Task DeserializeRefreshToken_ReturnsNullForMissingTokenCiphertext()
+ public async Task DeserializeRefreshToken_ReturnsNullForMissingReferenceTokenCiphertext()
{
// Arrange
var token = new OpenIddictToken();
@@ -1049,7 +1235,7 @@ namespace OpenIddict.Tests
}
[Fact]
- public async Task DeserializeRefreshToken_ReturnsNullForInvalidTokenCiphertext()
+ public async Task DeserializeRefreshToken_ReturnsNullForInvalidReferenceTokenCiphertext()
{
// Arrange
var format = new Mock>();
@@ -1115,7 +1301,7 @@ namespace OpenIddict.Tests
}
[Fact]
- public async Task DeserializeRefreshToken_ReturnsExpectedToken()
+ public async Task DeserializeRefreshToken_ReturnsExpectedReferenceToken()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
@@ -1126,9 +1312,6 @@ namespace OpenIddict.Tests
new AuthenticationProperties(),
OpenIdConnectServerDefaults.AuthenticationScheme);
- ticket.SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56");
- ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.RefreshToken);
-
var format = new Mock>();
format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"))
@@ -1211,6 +1394,192 @@ namespace OpenIddict.Tests
format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once());
}
+ [Fact]
+ public async Task DeserializeRefreshToken_ReturnsNullForMissingTokenIdentifier()
+ {
+ // Arrange
+ var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
+ identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "Bob le Bricoleur");
+
+ var ticket = new AuthenticationTicket(
+ new ClaimsPrincipal(identity),
+ new AuthenticationProperties(),
+ OpenIdConnectServerDefaults.AuthenticationScheme);
+
+ var format = new Mock>();
+
+ format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"))
+ .Returns(ticket);
+
+ var server = CreateAuthorizationServer(builder =>
+ {
+ builder.Services.AddSingleton(CreateApplicationManager(instance =>
+ {
+ var application = new OpenIddictApplication();
+
+ instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny()))
+ .ReturnsAsync(application);
+
+ instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny()))
+ .ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
+
+ instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny()))
+ .ReturnsAsync(true);
+ }));
+
+ builder.Configure(options => options.RefreshTokenFormat = format.Object);
+ });
+
+ var client = new OpenIdConnectClient(server.CreateClient());
+
+ // Act
+ var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest
+ {
+ ClientId = "Fabrikam",
+ ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
+ Token = "2YotnFZFEjr1zCsicMWpAA",
+ TokenTypeHint = OpenIdConnectConstants.TokenTypeHints.RefreshToken
+ });
+
+ // Assert
+ Assert.Single(response.GetParameters());
+ Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]);
+ }
+
+ [Fact]
+ public async Task DeserializeRefreshToken_ReturnsNullForInvalidTokenCiphertext()
+ {
+ // Arrange
+ var format = new Mock>();
+
+ format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"))
+ .Returns(value: null);
+
+ var server = CreateAuthorizationServer(builder =>
+ {
+ builder.Services.AddSingleton(CreateApplicationManager(instance =>
+ {
+ var application = new OpenIddictApplication();
+
+ instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny()))
+ .ReturnsAsync(application);
+
+ instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny()))
+ .ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
+
+ instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny()))
+ .ReturnsAsync(true);
+ }));
+
+ builder.Configure(options => options.RefreshTokenFormat = format.Object);
+ });
+
+ var client = new OpenIdConnectClient(server.CreateClient());
+
+ // Act
+ var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest
+ {
+ ClientId = "Fabrikam",
+ ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
+ Token = "2YotnFZFEjr1zCsicMWpAA",
+ TokenTypeHint = OpenIdConnectConstants.TokenTypeHints.RefreshToken
+ });
+
+ // Assert
+ Assert.Single(response.GetParameters());
+ Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]);
+
+ format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once());
+ }
+
+ [Fact]
+ public async Task DeserializeRefreshToken_ReturnsExpectedToken()
+ {
+ // Arrange
+ var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
+ identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "Bob le Bricoleur");
+
+ var ticket = new AuthenticationTicket(
+ new ClaimsPrincipal(identity),
+ new AuthenticationProperties(),
+ OpenIdConnectServerDefaults.AuthenticationScheme);
+
+ ticket.SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56");
+
+ var format = new Mock>();
+
+ format.Setup(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"))
+ .Returns(ticket);
+
+ var token = new OpenIddictToken();
+
+ var manager = CreateTokenManager(instance =>
+ {
+ instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()))
+ .ReturnsAsync(token);
+
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+
+ instance.Setup(mock => mock.IsValidAsync(token, It.IsAny()))
+ .ReturnsAsync(true);
+
+ instance.Setup(mock => mock.GetCreationDateAsync(token, It.IsAny()))
+ .ReturnsAsync(new DateTimeOffset(2017, 01, 01, 00, 00, 00, TimeSpan.Zero));
+
+ instance.Setup(mock => mock.GetExpirationDateAsync(token, It.IsAny()))
+ .ReturnsAsync(new DateTimeOffset(2017, 01, 10, 00, 00, 00, TimeSpan.Zero));
+ });
+
+ var server = CreateAuthorizationServer(builder =>
+ {
+ builder.Services.AddSingleton(CreateApplicationManager(instance =>
+ {
+ var application = new OpenIddictApplication();
+
+ instance.Setup(mock => mock.FindByClientIdAsync("Fabrikam", It.IsAny()))
+ .ReturnsAsync(application);
+
+ instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny()))
+ .ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
+
+ instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny()))
+ .ReturnsAsync(true);
+ }));
+
+ builder.Services.AddSingleton(manager);
+
+ builder.Configure(options =>
+ {
+ options.SystemClock = Mock.Of(mock => mock.UtcNow ==
+ new DateTimeOffset(2017, 01, 05, 00, 00, 00, TimeSpan.Zero));
+
+ options.RefreshTokenFormat = format.Object;
+ });
+ });
+
+ var client = new OpenIdConnectClient(server.CreateClient());
+
+ // Act
+ var response = await client.PostAsync(IntrospectionEndpoint, new OpenIdConnectRequest
+ {
+ ClientId = "Fabrikam",
+ ClientSecret = "7Fjfp0ZBr1KtDRbnfVdmIw",
+ Token = "2YotnFZFEjr1zCsicMWpAA",
+ TokenTypeHint = OpenIdConnectConstants.TokenTypeHints.RefreshToken
+ });
+
+ // Assert
+ Assert.True((bool) response[OpenIdConnectConstants.Claims.Active]);
+ Assert.Equal("3E228451-1555-46F7-A471-951EFBA23A56", response[OpenIdConnectConstants.Claims.JwtId]);
+ 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());
+ format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once());
+ }
+
[Fact]
public async Task SerializeAccessToken_AccessTokenIsNotPersistedWhenReferenceTokensAreDisabled()
{
@@ -1736,7 +2105,6 @@ namespace OpenIddict.Tests
OpenIdConnectServerDefaults.AuthenticationScheme);
ticket.SetTokenId("60FFF7EA-F98E-437B-937E-5073CC313103");
- ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.RefreshToken);
ticket.SetScopes(OpenIdConnectConstants.Scopes.OpenId, OpenIdConnectConstants.Scopes.OfflineAccess);
var format = new Mock>();
@@ -1754,6 +2122,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
+
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny()))
.ReturnsAsync(false);
diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.cs
index 053c54aa..3be3d1d2 100644
--- a/test/OpenIddict.Tests/OpenIddictProviderTests.cs
+++ b/test/OpenIddict.Tests/OpenIddictProviderTests.cs
@@ -148,6 +148,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
+
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny()))
.ReturnsAsync(false);
@@ -213,6 +216,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny()))
.ReturnsAsync(false);
@@ -286,6 +292,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
+
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny()))
.ReturnsAsync(false);
@@ -401,6 +410,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny()))
.ReturnsAsync(true);
});
@@ -435,7 +447,7 @@ namespace OpenIddict.Tests
});
// Assert
- Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.Exactly(2));
+ Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny()), Times.Once());
}
@@ -467,6 +479,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
+
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny()))
.ReturnsAsync(true);
@@ -507,7 +522,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.Exactly(2));
+ Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny()), Times.Once());
}
@@ -542,6 +557,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
+
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny()))
.ReturnsAsync(false);
@@ -570,7 +588,7 @@ namespace OpenIddict.Tests
// Assert
Assert.NotNull(response.RefreshToken);
- Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.Exactly(2));
+ Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny()), Times.Once());
}
@@ -605,6 +623,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
+
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny()))
.ReturnsAsync(false);
@@ -635,9 +656,9 @@ namespace OpenIddict.Tests
// Assert
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error);
- Assert.Equal("The specified authorization code is no longer valid.", response.ErrorDescription);
+ 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.Exactly(2));
+ Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny()), Times.Once());
}
@@ -695,7 +716,7 @@ namespace OpenIddict.Tests
// Assert
Assert.Null(response.RefreshToken);
- Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.Exactly(2));
+ Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny()), Times.Never());
}
@@ -734,6 +755,12 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()))
.ReturnsAsync(tokens[0]);
+ instance.Setup(mock => mock.GetIdAsync(tokens[0], It.IsAny()))
+ .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
+
+ instance.Setup(mock => mock.GetAuthorizationIdAsync(tokens[0], It.IsAny()))
+ .ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0");
+
instance.Setup(mock => mock.IsRedeemedAsync(tokens[0], It.IsAny()))
.ReturnsAsync(false);
@@ -765,7 +792,7 @@ namespace OpenIddict.Tests
// Assert
Assert.NotNull(response.RefreshToken);
- Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.Exactly(2));
+ Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce());
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());
}
@@ -802,6 +829,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()))
.ReturnsAsync(tokens[0]);
+ instance.Setup(mock => mock.GetIdAsync(tokens[0], It.IsAny()))
+ .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
+
instance.Setup(mock => mock.IsRedeemedAsync(tokens[0], It.IsAny()))
.ReturnsAsync(false);
@@ -831,7 +861,7 @@ namespace OpenIddict.Tests
// Assert
Assert.Null(response.RefreshToken);
- Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.Exactly(2));
+ Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()), Times.AtLeastOnce());
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());
}
@@ -864,6 +894,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
+
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny()))
.ReturnsAsync(false);
@@ -996,6 +1029,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny()))
.ReturnsAsync(token);
+ instance.Setup(mock => mock.GetIdAsync(token, It.IsAny()))
+ .ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
+
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny()))
.ReturnsAsync(false);