Browse Source

Correctly restore the authentication properties of non-reference authorization codes/refresh tokens

pull/500/head
Kévin Chalet 9 years ago
parent
commit
0a409a3013
  1. 158
      src/OpenIddict/OpenIddictProvider.Helpers.cs
  2. 52
      src/OpenIddict/OpenIddictProvider.Serialization.cs
  3. 4
      src/OpenIddict/OpenIddictProvider.cs
  4. 55
      test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs
  5. 10
      test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs
  6. 5
      test/OpenIddict.Tests/OpenIddictProviderTests.Revocation.cs
  7. 423
      test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs
  8. 52
      test/OpenIddict.Tests/OpenIddictProviderTests.cs

158
src/OpenIddict/OpenIddictProvider.Helpers.cs

@ -31,11 +31,6 @@ namespace OpenIddict
[NotNull] AuthenticationTicket ticket, [NotNull] OpenIddictOptions options,
[NotNull] HttpContext context, [NotNull] OpenIdConnectRequest request)
{
if (options.DisableTokenRevocation)
{
return;
}
var descriptor = new OpenIddictAuthorizationDescriptor
{
Status = OpenIddictConstants.Statuses.Valid,
@ -218,71 +213,118 @@ 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)
{
if (!options.UseReferenceTokens)
{
return null;
}
Debug.Assert(!(options.DisableTokenRevocation && options.UseReferenceTokens),
"Token revocation cannot be disabled when using reference tokens.");
string hash;
try
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 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.");
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;
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;
}
@ -298,9 +340,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;
}
@ -351,12 +393,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);

52
src/OpenIddict/OpenIddictProvider.Serialization.cs

@ -18,60 +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.
context.HandleDeserialization();
// its default logic to deserialize reference access tokens.
if (options.UseReferenceTokens)
{
context.HandleDeserialization();
}
}
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.
// Prevent the OpenID Connect server middleware from using its default logic.
context.HandleDeserialization();
}
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.
// Prevent the OpenID Connect server middleware from using its default logic.
context.HandleDeserialization();
}
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.
@ -87,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.
@ -108,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

@ -156,7 +156,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

@ -560,7 +560,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());
}
@ -617,7 +617,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());
}
@ -647,6 +647,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);
});
@ -684,7 +687,7 @@ namespace OpenIddict.Tests
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error);
Assert.Equal("The specified authorization code has already been redeemed.", response.ErrorDescription);
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<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());
}
@ -712,6 +715,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);
});
@ -747,7 +753,7 @@ namespace OpenIddict.Tests
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error);
Assert.Equal("The specified refresh token has already been redeemed.", response.ErrorDescription);
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<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());
}
@ -798,6 +804,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);
@ -875,6 +887,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);
@ -933,6 +951,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);
@ -981,7 +1005,7 @@ namespace OpenIddict.Tests
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error);
Assert.Equal("The specified authorization code has already been redeemed.", response.ErrorDescription);
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<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());
@ -1016,6 +1040,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);
@ -1062,7 +1092,7 @@ namespace OpenIddict.Tests
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error);
Assert.Equal("The specified refresh token has already been redeemed.", response.ErrorDescription);
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<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());
@ -1094,6 +1124,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);
@ -1134,7 +1167,7 @@ namespace OpenIddict.Tests
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error);
Assert.Equal("The specified authorization code is no longer valid.", response.ErrorDescription);
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<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());
}
@ -1163,6 +1196,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);
@ -1201,7 +1237,7 @@ namespace OpenIddict.Tests
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error);
Assert.Equal("The specified refresh token is no longer valid.", response.ErrorDescription);
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<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());
}
@ -1248,6 +1284,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

@ -571,6 +571,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);
});
@ -610,7 +613,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());
}
@ -708,6 +711,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);
});
@ -747,7 +753,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

@ -455,6 +455,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 =>
@ -475,7 +478,7 @@ namespace OpenIddict.Tests
// Assert
Assert.Empty(response.GetParameters());
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<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

@ -86,7 +86,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>>();
@ -122,7 +122,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());
@ -144,7 +144,7 @@ namespace OpenIddict.Tests
}
[Fact]
public async Task DeserializeAccessToken_ReturnsNullForMissingTokenIdentifier()
public async Task DeserializeAccessToken_ReturnsNullForMissingReferenceTokenIdentifier()
{
// Arrange
var token = new OpenIddictToken();
@ -199,7 +199,7 @@ namespace OpenIddict.Tests
}
[Fact]
public async Task DeserializeAccessToken_ReturnsNullForMissingTokenCiphertext()
public async Task DeserializeAccessToken_ReturnsNullForMissingReferenceTokenCiphertext()
{
// Arrange
var token = new OpenIddictToken();
@ -257,7 +257,7 @@ namespace OpenIddict.Tests
}
[Fact]
public async Task DeserializeAccessToken_ReturnsNullForInvalidTokenCiphertext()
public async Task DeserializeAccessToken_ReturnsNullForInvalidReferenceTokenCiphertext()
{
// Arrange
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>();
@ -323,7 +323,7 @@ namespace OpenIddict.Tests
}
[Fact]
public async Task DeserializeAccessToken_ReturnsExpectedToken()
public async Task DeserializeAccessToken_ReturnsExpectedReferenceToken()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
@ -335,7 +335,6 @@ namespace OpenIddict.Tests
OpenIdConnectServerDefaults.AuthenticationScheme);
ticket.SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56");
ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.RefreshToken);
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>();
@ -469,7 +468,7 @@ namespace OpenIddict.Tests
}
[Fact]
public async Task DeserializeAuthorizationCode_AuthorizationCodeIsNotRetrievedFromDatabaseWhenReferenceTokensAreDisabled()
public async Task DeserializeAuthorizationCode_AuthorizationCodeIsNotRetrievedUsingHashWhenReferenceTokensAreDisabled()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
@ -481,7 +480,6 @@ namespace OpenIddict.Tests
OpenIdConnectServerDefaults.AuthenticationScheme);
ticket.SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56");
ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.RefreshToken);
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>();
@ -495,6 +493,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);
});
@ -539,7 +540,7 @@ namespace OpenIddict.Tests
}
[Fact]
public async Task DeserializeAuthorizationCode_ReturnsNullForMissingTokenIdentifier()
public async Task DeserializeAuthorizationCode_ReturnsNullForMissingReferenceTokenIdentifier()
{
// Arrange
var token = new OpenIddictToken();
@ -594,7 +595,7 @@ namespace OpenIddict.Tests
}
[Fact]
public async Task DeserializeAuthorizationCode_ReturnsNullForMissingTokenCiphertext()
public async Task DeserializeAuthorizationCode_ReturnsNullForMissingReferenceTokenCiphertext()
{
// Arrange
var token = new OpenIddictToken();
@ -652,7 +653,7 @@ namespace OpenIddict.Tests
}
[Fact]
public async Task DeserializeAuthorizationCode_ReturnsNullForInvalidTokenCiphertext()
public async Task DeserializeAuthorizationCode_ReturnsNullForInvalidReferenceTokenCiphertext()
{
// Arrange
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>();
@ -718,7 +719,7 @@ namespace OpenIddict.Tests
}
[Fact]
public async Task DeserializeAuthorizationCode_ReturnsExpectedToken()
public async Task DeserializeAuthorizationCode_ReturnsExpectedReferenceToken()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
@ -729,9 +730,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"))
@ -814,6 +812,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()
{
@ -864,7 +1048,7 @@ namespace OpenIddict.Tests
}
[Fact]
public async Task DeserializeRefreshToken_RefreshTokenIsNotRetrievedFromDatabaseWhenReferenceTokensAreDisabled()
public async Task DeserializeRefreshToken_RefreshTokenIsNotRetrievedUsingHashWhenReferenceTokensAreDisabled()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
@ -876,7 +1060,6 @@ namespace OpenIddict.Tests
OpenIdConnectServerDefaults.AuthenticationScheme);
ticket.SetTokenId("3E228451-1555-46F7-A471-951EFBA23A56");
ticket.SetTokenUsage(OpenIdConnectConstants.TokenUsages.RefreshToken);
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>();
@ -890,6 +1073,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);
});
@ -934,7 +1120,7 @@ namespace OpenIddict.Tests
}
[Fact]
public async Task DeserializeRefreshToken_ReturnsNullForMissingTokenIdentifier()
public async Task DeserializeRefreshToken_ReturnsNullForMissingReferenceTokenIdentifier()
{
// Arrange
var token = new OpenIddictToken();
@ -989,7 +1175,7 @@ namespace OpenIddict.Tests
}
[Fact]
public async Task DeserializeRefreshToken_ReturnsNullForMissingTokenCiphertext()
public async Task DeserializeRefreshToken_ReturnsNullForMissingReferenceTokenCiphertext()
{
// Arrange
var token = new OpenIddictToken();
@ -1047,7 +1233,7 @@ namespace OpenIddict.Tests
}
[Fact]
public async Task DeserializeRefreshToken_ReturnsNullForInvalidTokenCiphertext()
public async Task DeserializeRefreshToken_ReturnsNullForInvalidReferenceTokenCiphertext()
{
// Arrange
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>();
@ -1113,7 +1299,7 @@ namespace OpenIddict.Tests
}
[Fact]
public async Task DeserializeRefreshToken_ReturnsExpectedToken()
public async Task DeserializeRefreshToken_ReturnsExpectedReferenceToken()
{
// Arrange
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
@ -1124,9 +1310,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"))
@ -1209,6 +1392,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()
{
@ -1734,7 +2103,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>>();
@ -1752,6 +2120,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

@ -147,6 +147,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);
@ -212,6 +215,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);
@ -285,6 +291,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);
@ -400,6 +409,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);
});
@ -434,7 +446,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());
}
@ -466,6 +478,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);
@ -506,7 +521,7 @@ namespace OpenIddict.Tests
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error);
Assert.Equal("The specified authorization code is no longer valid.", response.ErrorDescription);
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<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());
}
@ -541,6 +556,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);
@ -569,7 +587,7 @@ namespace OpenIddict.Tests
// Assert
Assert.NotNull(response.RefreshToken);
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<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());
}
@ -604,6 +622,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);
@ -634,9 +655,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());
}
@ -694,7 +715,7 @@ namespace OpenIddict.Tests
// Assert
Assert.Null(response.RefreshToken);
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<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());
}
@ -733,6 +754,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);
@ -764,7 +791,7 @@ namespace OpenIddict.Tests
// Assert
Assert.NotNull(response.RefreshToken);
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<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());
}
@ -801,6 +828,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);
@ -830,7 +860,7 @@ namespace OpenIddict.Tests
// Assert
Assert.Null(response.RefreshToken);
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("60FFF7EA-F98E-437B-937E-5073CC313103", It.IsAny<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());
}
@ -863,6 +893,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);
@ -995,6 +1028,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