Browse Source

Backport the non-reference tokens deserialization fix to OpenIddict 1.x

pull/553/head
Kévin Chalet 9 years ago
parent
commit
62328923cd
  1. 2
      build/version.props
  2. 157
      src/OpenIddict/OpenIddictProvider.Helpers.cs
  3. 74
      src/OpenIddict/OpenIddictProvider.Serialization.cs
  4. 4
      src/OpenIddict/OpenIddictProvider.cs
  5. 55
      test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs
  6. 10
      test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs
  7. 5
      test/OpenIddict.Tests/OpenIddictProviderTests.Revocation.cs
  8. 423
      test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs
  9. 52
      test/OpenIddict.Tests/OpenIddictProviderTests.cs

2
build/version.props

@ -2,7 +2,7 @@
<PropertyGroup>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>rc1</VersionSuffix>
<VersionSuffix>rc2</VersionSuffix>
<VersionSuffix Condition=" '$(VersionSuffix)' != '' AND '$(BuildNumber)' != '' ">$(VersionSuffix)-$(BuildNumber)</VersionSuffix>
</PropertyGroup>

157
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<OpenIddictAuthorizationManager<TAuthorization>>();
var logger = context.RequestServices.GetRequiredService<ILogger<OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>>>();
if (options.DisableTokenRevocation)
{
return;
}
var descriptor = new OpenIddictAuthorizationDescriptor
{
Status = OpenIddictConstants.Statuses.Valid,
@ -228,74 +222,119 @@ namespace OpenIddict
}
private async Task<AuthenticationTicket> 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<AuthenticationTicket> format)
{
var logger = context.RequestServices.GetRequiredService<ILogger<OpenIddictProvider<TApplication, TAuthorization, TScope, TToken>>>();
var tokens = context.RequestServices.GetRequiredService<OpenIddictTokenManager<TToken>>();
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);

74
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.

4
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;
}

55
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<CancellationToken>()), 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<CancellationToken>()), Times.Once());
}
@ -649,6 +649,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()))
.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<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()), Times.Once());
}
@ -714,6 +717,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()))
.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<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()), Times.Once());
}
@ -800,6 +806,12 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.GetAuthorizationIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0");
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
@ -877,6 +889,12 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.GetAuthorizationIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0");
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
@ -935,6 +953,12 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(tokens[0]);
instance.Setup(mock => mock.GetIdAsync(tokens[0], It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.GetAuthorizationIdAsync(tokens[0], It.IsAny<CancellationToken>()))
.ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0");
instance.Setup(mock => mock.IsRedeemedAsync(tokens[0], It.IsAny<CancellationToken>()))
.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<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.IsRedeemedAsync(tokens[0], It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[0], It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[1], It.IsAny<CancellationToken>()), Times.Once());
@ -1018,6 +1042,12 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(tokens[0]);
instance.Setup(mock => mock.GetIdAsync(tokens[0], It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.GetAuthorizationIdAsync(tokens[0], It.IsAny<CancellationToken>()))
.ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0");
instance.Setup(mock => mock.IsRedeemedAsync(tokens[0], It.IsAny<CancellationToken>()))
.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<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.IsRedeemedAsync(tokens[0], It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[0], It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[1], It.IsAny<CancellationToken>()), Times.Once());
@ -1096,6 +1126,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()))
.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<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.IsValidAsync(token, It.IsAny<CancellationToken>()), Times.Once());
}
@ -1165,6 +1198,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()))
.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<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.IsValidAsync(token, It.IsAny<CancellationToken>()), Times.Once());
}
@ -1250,6 +1286,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
});

10
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<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny<CancellationToken>()))
.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<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.IsValidAsync(token, It.IsAny<CancellationToken>()), Times.Once());
}
@ -710,6 +713,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny<CancellationToken>()))
.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<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.IsValidAsync(token, It.IsAny<CancellationToken>()), Times.Once());
}
}

5
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<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.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<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.RevokeAsync(token, It.IsAny<CancellationToken>()), Times.Once());
}
}

423
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<ISecureDataFormat<AuthenticationTicket>>();
@ -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<ISecureDataFormat<AuthenticationTicket>>();
@ -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<ISecureDataFormat<AuthenticationTicket>>();
@ -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<ISecureDataFormat<AuthenticationTicket>>();
@ -497,6 +495,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny<CancellationToken>()))
.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<ISecureDataFormat<AuthenticationTicket>>();
@ -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<ISecureDataFormat<AuthenticationTicket>>();
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<ISecureDataFormat<AuthenticationTicket>>();
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<CancellationToken>()))
.ReturnsAsync(application);
instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.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<ISecureDataFormat<AuthenticationTicket>>();
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<CancellationToken>()))
.ReturnsAsync(application);
instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.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<ISecureDataFormat<AuthenticationTicket>>();
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<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
instance.Setup(mock => mock.GetCreationDateAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(new DateTimeOffset(2017, 01, 01, 00, 00, 00, TimeSpan.Zero));
instance.Setup(mock => mock.GetExpirationDateAsync(token, It.IsAny<CancellationToken>()))
.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<CancellationToken>()))
.ReturnsAsync(application);
instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
builder.Services.AddSingleton(manager);
builder.Configure(options =>
{
options.SystemClock = Mock.Of<ISystemClock>(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<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()), 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<ISecureDataFormat<AuthenticationTicket>>();
@ -892,6 +1075,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny<CancellationToken>()))
.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<ISecureDataFormat<AuthenticationTicket>>();
@ -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<ISecureDataFormat<AuthenticationTicket>>();
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<ISecureDataFormat<AuthenticationTicket>>();
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<CancellationToken>()))
.ReturnsAsync(application);
instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.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<ISecureDataFormat<AuthenticationTicket>>();
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<CancellationToken>()))
.ReturnsAsync(application);
instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.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<ISecureDataFormat<AuthenticationTicket>>();
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<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
instance.Setup(mock => mock.GetCreationDateAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(new DateTimeOffset(2017, 01, 01, 00, 00, 00, TimeSpan.Zero));
instance.Setup(mock => mock.GetExpirationDateAsync(token, It.IsAny<CancellationToken>()))
.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<CancellationToken>()))
.ReturnsAsync(application);
instance.Setup(mock => mock.GetClientTypeAsync(application, It.IsAny<CancellationToken>()))
.ReturnsAsync(OpenIddictConstants.ClientTypes.Confidential);
instance.Setup(mock => mock.ValidateClientSecretAsync(application, "7Fjfp0ZBr1KtDRbnfVdmIw", It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
builder.Services.AddSingleton(manager);
builder.Configure(options =>
{
options.SystemClock = Mock.Of<ISystemClock>(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<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()), 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<ISecureDataFormat<AuthenticationTicket>>();
@ -1754,6 +2122,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);

52
test/OpenIddict.Tests/OpenIddictProviderTests.cs

@ -148,6 +148,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
@ -213,6 +216,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
@ -286,6 +292,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
@ -401,6 +410,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
});
@ -435,7 +447,7 @@ namespace OpenIddict.Tests
});
// Assert
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()), Times.Exactly(2));
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny<CancellationToken>()), Times.Once());
}
@ -467,6 +479,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.IsValidAsync(token, It.IsAny<CancellationToken>()))
.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<CancellationToken>()), Times.Exactly(2));
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny<CancellationToken>()), Times.Once());
}
@ -542,6 +557,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()))
.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<CancellationToken>()), Times.Exactly(2));
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny<CancellationToken>()), Times.Once());
}
@ -605,6 +623,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()))
.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<CancellationToken>()), Times.Exactly(2));
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny<CancellationToken>()), 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<CancellationToken>()), Times.Exactly(2));
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.RedeemAsync(token, It.IsAny<CancellationToken>()), Times.Never());
}
@ -734,6 +755,12 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()))
.ReturnsAsync(tokens[0]);
instance.Setup(mock => mock.GetIdAsync(tokens[0], It.IsAny<CancellationToken>()))
.ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
instance.Setup(mock => mock.GetAuthorizationIdAsync(tokens[0], It.IsAny<CancellationToken>()))
.ReturnsAsync("18D15F73-BE2B-6867-DC01-B3C1E8AFDED0");
instance.Setup(mock => mock.IsRedeemedAsync(tokens[0], It.IsAny<CancellationToken>()))
.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<CancellationToken>()), Times.Exactly(2));
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[1], It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[2], It.IsAny<CancellationToken>()), Times.Once());
}
@ -802,6 +829,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()))
.ReturnsAsync(tokens[0]);
instance.Setup(mock => mock.GetIdAsync(tokens[0], It.IsAny<CancellationToken>()))
.ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
instance.Setup(mock => mock.IsRedeemedAsync(tokens[0], It.IsAny<CancellationToken>()))
.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<CancellationToken>()), Times.Exactly(2));
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[1], It.IsAny<CancellationToken>()), Times.Never());
Mock.Get(manager).Verify(mock => mock.RevokeAsync(tokens[2], It.IsAny<CancellationToken>()), Times.Never());
}
@ -864,6 +894,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
@ -996,6 +1029,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("60FFF7EA-F98E-437B-937E-5073CC313103");
instance.Setup(mock => mock.IsRedeemedAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(false);

Loading…
Cancel
Save