Browse Source

Rename OpenIddictToken.Ciphertext/Hash to Payload/ReferenceId and add new methods to allow overriding the default obfuscation

pull/525/head
Kévin Chalet 8 years ago
parent
commit
a19f5e81ff
  1. 14
      src/OpenIddict.Core/Descriptors/OpenIddictTokenDescriptor.cs
  2. 92
      src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
  3. 75
      src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs
  4. 111
      src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs
  5. 2
      src/OpenIddict.EntityFramework/OpenIddictExtensions.cs
  6. 2
      src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs
  7. 29
      src/OpenIddict.Models/OpenIddictToken.cs
  8. 35
      src/OpenIddict/OpenIddictProvider.Helpers.cs
  9. 10
      test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs
  10. 110
      test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs

14
src/OpenIddict.Core/Descriptors/OpenIddictTokenDescriptor.cs

@ -19,11 +19,6 @@ namespace OpenIddict.Core
/// </summary>
public string AuthorizationId { get; set; }
/// <summary>
/// Gets or sets the encrypted payload associated with the token.
/// </summary>
public string Ciphertext { get; set; }
/// <summary>
/// Gets or sets the creation date associated with the token.
/// </summary>
@ -35,9 +30,9 @@ namespace OpenIddict.Core
public DateTimeOffset? ExpirationDate { get; set; }
/// <summary>
/// Gets or sets the cryptographic hash associated with the token.
/// Gets or sets the payload associated with the token.
/// </summary>
public string Hash { get; set; }
public string Payload { get; set; }
/// <summary>
/// Gets or sets the optional principal associated with the token.
@ -52,6 +47,11 @@ namespace OpenIddict.Core
public IDictionary<string, string> Properties { get; } =
new Dictionary<string, string>(StringComparer.Ordinal);
/// <summary>
/// Gets or sets the reference identifier associated with the token.
/// </summary>
public string ReferenceId { get; set; }
/// <summary>
/// Gets or sets the status associated with the token.
/// </summary>

92
src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs

@ -7,6 +7,8 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@ -213,22 +215,24 @@ namespace OpenIddict.Core
}
/// <summary>
/// Retrieves the list of tokens corresponding to the specified hash.
/// Retrieves the list of tokens corresponding to the specified reference identifier.
/// Note: the reference identifier may be hashed or encrypted for security reasons.
/// </summary>
/// <param name="hash">The hashed crypto-secure random identifier associated with the tokens.</param>
/// <param name="identifier">The reference identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the tokens corresponding to the specified hash.
/// whose result returns the tokens corresponding to the specified reference identifier.
/// </returns>
public virtual Task<TToken> FindByHashAsync([NotNull] string hash, CancellationToken cancellationToken)
public virtual async Task<TToken> FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(hash))
if (string.IsNullOrEmpty(identifier))
{
throw new ArgumentException("The hash cannot be null or empty.", nameof(hash));
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
return Store.FindByHashAsync(hash, cancellationToken);
identifier = await ObfuscateReferenceIdAsync(identifier, cancellationToken);
return await Store.FindByReferenceIdAsync(identifier, cancellationToken);
}
/// <summary>
@ -347,98 +351,100 @@ namespace OpenIddict.Core
}
/// <summary>
/// Retrieves the ciphertext associated with a token.
/// Retrieves the creation date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the ciphertext associated with the specified token.
/// whose result returns the creation date associated with the specified token.
/// </returns>
public virtual Task<string> GetCiphertextAsync([NotNull] TToken token, CancellationToken cancellationToken)
public virtual Task<DateTimeOffset?> GetCreationDateAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
return Store.GetCiphertextAsync(token, cancellationToken);
return Store.GetCreationDateAsync(token, cancellationToken);
}
/// <summary>
/// Retrieves the creation date associated with a token.
/// Retrieves the expiration date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the creation date associated with the specified token.
/// whose result returns the expiration date associated with the specified token.
/// </returns>
public virtual Task<DateTimeOffset?> GetCreationDateAsync([NotNull] TToken token, CancellationToken cancellationToken)
public virtual Task<DateTimeOffset?> GetExpirationDateAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
return Store.GetCreationDateAsync(token, cancellationToken);
return Store.GetExpirationDateAsync(token, cancellationToken);
}
/// <summary>
/// Retrieves the expiration date associated with a token.
/// Retrieves the reference identifier associated with a token.
/// Note: depending on the manager used to create the token,
/// the reference identifier may be hashed for security reasons.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the expiration date associated with the specified token.
/// whose result returns the reference identifier associated with the specified token.
/// </returns>
public virtual Task<DateTimeOffset?> GetExpirationDateAsync([NotNull] TToken token, CancellationToken cancellationToken)
public virtual Task<string> GetReferenceIdAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
return Store.GetExpirationDateAsync(token, cancellationToken);
return Store.GetReferenceIdAsync(token, cancellationToken);
}
/// <summary>
/// Retrieves the hashed identifier associated with a token.
/// Retrieves the unique identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the hashed identifier associated with the specified token.
/// whose result returns the unique identifier associated with the token.
/// </returns>
public virtual Task<string> GetHashAsync([NotNull] TToken token, CancellationToken cancellationToken)
public virtual Task<string> GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
return Store.GetHashAsync(token, cancellationToken);
return Store.GetIdAsync(token, cancellationToken);
}
/// <summary>
/// Retrieves the unique identifier associated with a token.
/// Retrieves the payload associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the token.
/// whose result returns the payload associated with the specified token.
/// </returns>
public virtual Task<string> GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken)
public virtual Task<string> GetPayloadAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
return Store.GetIdAsync(token, cancellationToken);
return Store.GetPayloadAsync(token, cancellationToken);
}
/// <summary>
@ -596,6 +602,30 @@ namespace OpenIddict.Core
return Store.ListInvalidAsync(count, offset, cancellationToken);
}
/// <summary>
/// Obfuscates the specified reference identifier so it can be safely stored in a database.
/// By default, this method returns a simple hashed representation computed using SHA256.
/// </summary>
/// <param name="identifier">The client identifier.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual Task<string> ObfuscateReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(identifier))
{
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
using (var algorithm = SHA256.Create())
{
// Compute the digest of the generated identifier and use it as the hashed identifier of the reference token.
// Doing that prevents token identifiers stolen from the database from being used as valid reference tokens.
return Task.FromResult(Convert.ToBase64String(algorithm.ComputeHash(Encoding.UTF8.GetBytes(identifier))));
}
}
/// <summary>
/// Redeems a token.
/// </summary>
@ -727,10 +757,10 @@ namespace OpenIddict.Core
{
ApplicationId = await Store.GetApplicationIdAsync(token, cancellationToken),
AuthorizationId = await Store.GetAuthorizationIdAsync(token, cancellationToken),
Ciphertext = await Store.GetCiphertextAsync(token, cancellationToken),
CreationDate = await Store.GetCreationDateAsync(token, cancellationToken),
ExpirationDate = await Store.GetExpirationDateAsync(token, cancellationToken),
Hash = await Store.GetHashAsync(token, cancellationToken),
Payload = await Store.GetPayloadAsync(token, cancellationToken),
ReferenceId = await Store.GetReferenceIdAsync(token, cancellationToken),
Status = await Store.GetStatusAsync(token, cancellationToken),
Subject = await Store.GetSubjectAsync(token, cancellationToken),
Type = await Store.GetTokenTypeAsync(token, cancellationToken)
@ -765,10 +795,10 @@ namespace OpenIddict.Core
await Store.SetApplicationIdAsync(token, descriptor.ApplicationId, cancellationToken);
await Store.SetAuthorizationIdAsync(token, descriptor.AuthorizationId, cancellationToken);
await Store.SetCiphertextAsync(token, descriptor.Ciphertext, cancellationToken);
await Store.SetCreationDateAsync(token, descriptor.CreationDate, cancellationToken);
await Store.SetExpirationDateAsync(token, descriptor.ExpirationDate, cancellationToken);
await Store.SetHashAsync(token, descriptor.Hash, cancellationToken);
await Store.SetPayloadAsync(token, descriptor.Payload, cancellationToken);
await Store.SetReferenceIdAsync(token, descriptor.ReferenceId, cancellationToken);
await Store.SetStatusAsync(token, descriptor.Status, cancellationToken);
await Store.SetSubjectAsync(token, descriptor.Subject, cancellationToken);
await Store.SetTokenTypeAsync(token, descriptor.Type, cancellationToken);

75
src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs

@ -82,26 +82,27 @@ namespace OpenIddict.Core
Task<ImmutableArray<TToken>> FindByAuthorizationIdAsync([NotNull] string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified hash.
/// Retrieves a token using its unique identifier.
/// </summary>
/// <param name="hash">The hashed crypto-secure random identifier associated with the tokens.</param>
/// <param name="identifier">The unique identifier associated with the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the tokens corresponding to the specified hash.
/// whose result returns the token corresponding to the unique identifier.
/// </returns>
Task<TToken> FindByHashAsync([NotNull] string hash, CancellationToken cancellationToken);
Task<TToken> FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves a token using its unique identifier.
/// Retrieves the list of tokens corresponding to the specified reference identifier.
/// Note: the reference identifier may be hashed or encrypted for security reasons.
/// </summary>
/// <param name="identifier">The unique identifier associated with the token.</param>
/// <param name="identifier">The reference identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the token corresponding to the unique identifier.
/// whose result returns the tokens corresponding to the specified reference identifier.
/// </returns>
Task<TToken> FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken);
Task<TToken> FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the list of tokens corresponding to the specified subject.
@ -153,59 +154,61 @@ namespace OpenIddict.Core
Task<string> GetAuthorizationIdAsync([NotNull] TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the ciphertext associated with a token.
/// Retrieves the creation date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the ciphertext associated with the specified token.
/// whose result returns the creation date associated with the specified token.
/// </returns>
Task<string> GetCiphertextAsync([NotNull] TToken token, CancellationToken cancellationToken);
Task<DateTimeOffset?> GetCreationDateAsync([NotNull] TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the creation date associated with a token.
/// Retrieves the expiration date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the creation date associated with the specified token.
/// whose result returns the expiration date associated with the specified token.
/// </returns>
Task<DateTimeOffset?> GetCreationDateAsync([NotNull] TToken token, CancellationToken cancellationToken);
Task<DateTimeOffset?> GetExpirationDateAsync([NotNull] TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the expiration date associated with a token.
/// Retrieves the unique identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the expiration date associated with the specified token.
/// whose result returns the unique identifier associated with the token.
/// </returns>
Task<DateTimeOffset?> GetExpirationDateAsync([NotNull] TToken token, CancellationToken cancellationToken);
Task<string> GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the hashed identifier associated with a token.
/// Retrieves the payload associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the hashed identifier associated with the specified token.
/// whose result returns the payload associated with the specified token.
/// </returns>
Task<string> GetHashAsync([NotNull] TToken token, CancellationToken cancellationToken);
Task<string> GetPayloadAsync([NotNull] TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the unique identifier associated with a token.
/// Retrieves the reference identifier associated with a token.
/// Note: depending on the manager used to create the token,
/// the reference identifier may be hashed for security reasons.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the token.
/// whose result returns the reference identifier associated with the specified token.
/// </returns>
Task<string> GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken);
Task<string> GetReferenceIdAsync([NotNull] TToken token, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the status associated with a token.
@ -314,48 +317,50 @@ namespace OpenIddict.Core
Task SetAuthorizationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the ciphertext associated with a token.
/// Sets the creation date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="ciphertext">The ciphertext associated with the token.</param>
/// <param name="date">The creation date.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
Task SetCiphertextAsync([NotNull] TToken token, [CanBeNull] string ciphertext, CancellationToken cancellationToken);
Task SetCreationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken);
/// <summary>
/// Sets the creation date associated with a token.
/// Sets the expiration date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="date">The creation date.</param>
/// <param name="date">The expiration date.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
Task SetCreationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken);
Task SetExpirationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken);
/// <summary>
/// Sets the expiration date associated with a token.
/// Sets the payload associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="date">The expiration date.</param>
/// <param name="payload">The payload associated with the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
Task SetExpirationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken);
Task SetPayloadAsync([NotNull] TToken token, [CanBeNull] string payload, CancellationToken cancellationToken);
/// <summary>
/// Sets the hash associated with a token.
/// Sets the reference identifier associated with a token.
/// Note: depending on the manager used to create the token,
/// the reference identifier may be hashed for security reasons.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="hash">The hash associated with the token.</param>
/// <param name="identifier">The reference identifier associated with the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
Task SetHashAsync([NotNull] TToken token, [CanBeNull] string hash, CancellationToken cancellationToken);
Task SetReferenceIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the status associated with a token.

111
src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs

@ -123,51 +123,52 @@ namespace OpenIddict.Core
}
/// <summary>
/// Retrieves the list of tokens corresponding to the specified hash.
/// Retrieves a token using its unique identifier.
/// </summary>
/// <param name="hash">The hashed crypto-secure random identifier associated with the tokens.</param>
/// <param name="identifier">The unique identifier associated with the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the tokens corresponding to the specified hash.
/// whose result returns the token corresponding to the unique identifier.
/// </returns>
public virtual Task<TToken> FindByHashAsync([NotNull] string hash, CancellationToken cancellationToken)
public virtual Task<TToken> FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(hash))
if (string.IsNullOrEmpty(identifier))
{
throw new ArgumentException("The hash cannot be null or empty.", nameof(hash));
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
IQueryable<TToken> Query(IQueryable<TToken> tokens, string digest)
IQueryable<TToken> Query(IQueryable<TToken> tokens, TKey key)
=> from token in tokens
where token.Hash == digest
where token.Id.Equals(key)
select token;
return GetAsync((tokens, digest) => Query(tokens, digest), hash, cancellationToken);
return GetAsync((tokens, key) => Query(tokens, key), ConvertIdentifierFromString(identifier), cancellationToken);
}
/// <summary>
/// Retrieves a token using its unique identifier.
/// Retrieves the list of tokens corresponding to the specified reference identifier.
/// Note: the reference identifier may be hashed or encrypted for security reasons.
/// </summary>
/// <param name="identifier">The unique identifier associated with the token.</param>
/// <param name="identifier">The reference identifier associated with the tokens.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the token corresponding to the unique identifier.
/// whose result returns the tokens corresponding to the specified reference identifier.
/// </returns>
public virtual Task<TToken> FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken)
public virtual Task<TToken> FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(identifier))
{
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
IQueryable<TToken> Query(IQueryable<TToken> tokens, TKey key)
IQueryable<TToken> Query(IQueryable<TToken> tokens, string id)
=> from token in tokens
where token.Id.Equals(key)
where token.ReferenceId == id
select token;
return GetAsync((tokens, key) => Query(tokens, key), ConvertIdentifierFromString(identifier), cancellationToken);
return GetAsync((tokens, id) => Query(tokens, identifier), identifier, cancellationToken);
}
/// <summary>
@ -271,98 +272,100 @@ namespace OpenIddict.Core
}
/// <summary>
/// Retrieves the ciphertext associated with a token.
/// Retrieves the creation date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the ciphertext associated with the specified token.
/// whose result returns the creation date associated with the specified token.
/// </returns>
public virtual Task<string> GetCiphertextAsync([NotNull] TToken token, CancellationToken cancellationToken)
public virtual Task<DateTimeOffset?> GetCreationDateAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
return Task.FromResult(token.Ciphertext);
return Task.FromResult(token.CreationDate);
}
/// <summary>
/// Retrieves the creation date associated with a token.
/// Retrieves the expiration date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the creation date associated with the specified token.
/// whose result returns the expiration date associated with the specified token.
/// </returns>
public virtual Task<DateTimeOffset?> GetCreationDateAsync([NotNull] TToken token, CancellationToken cancellationToken)
public virtual Task<DateTimeOffset?> GetExpirationDateAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
return Task.FromResult(token.CreationDate);
return Task.FromResult(token.ExpirationDate);
}
/// <summary>
/// Retrieves the expiration date associated with a token.
/// Retrieves the unique identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the expiration date associated with the specified token.
/// whose result returns the unique identifier associated with the token.
/// </returns>
public virtual Task<DateTimeOffset?> GetExpirationDateAsync([NotNull] TToken token, CancellationToken cancellationToken)
public virtual Task<string> GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
return Task.FromResult(token.ExpirationDate);
return Task.FromResult(ConvertIdentifierToString(token.Id));
}
/// <summary>
/// Retrieves the hashed identifier associated with a token.
/// Retrieves the payload associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the hashed identifier associated with the specified token.
/// whose result returns the payload associated with the specified token.
/// </returns>
public virtual Task<string> GetHashAsync([NotNull] TToken token, CancellationToken cancellationToken)
public virtual Task<string> GetPayloadAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
return Task.FromResult(token.Hash);
return Task.FromResult(token.Payload);
}
/// <summary>
/// Retrieves the unique identifier associated with a token.
/// Retrieves the reference identifier associated with a token.
/// Note: depending on the manager used to create the token,
/// the reference identifier may be hashed for security reasons.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the token.
/// whose result returns the reference identifier associated with the specified token.
/// </returns>
public virtual Task<string> GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken)
public virtual Task<string> GetReferenceIdAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
return Task.FromResult(ConvertIdentifierToString(token.Id));
return Task.FromResult(token.ReferenceId);
}
/// <summary>
@ -540,36 +543,37 @@ namespace OpenIddict.Core
public abstract Task SetApplicationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the ciphertext associated with a token.
/// Sets the creation date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="ciphertext">The ciphertext associated with the token.</param>
/// <param name="date">The creation date.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual Task SetCiphertextAsync([NotNull] TToken token, [CanBeNull] string ciphertext, CancellationToken cancellationToken)
public virtual Task SetCreationDateAsync([NotNull] TToken token,
[CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
token.Ciphertext = ciphertext;
token.CreationDate = date;
return Task.CompletedTask;
}
/// <summary>
/// Sets the creation date associated with a token.
/// Sets the expiration date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="date">The creation date.</param>
/// <param name="date">The expiration date.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual Task SetCreationDateAsync([NotNull] TToken token,
public virtual Task SetExpirationDateAsync([NotNull] TToken token,
[CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken)
{
if (token == null)
@ -577,50 +581,51 @@ namespace OpenIddict.Core
throw new ArgumentNullException(nameof(token));
}
token.CreationDate = date;
token.ExpirationDate = date;
return Task.CompletedTask;
}
/// <summary>
/// Sets the expiration date associated with a token.
/// Sets the payload associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="date">The expiration date.</param>
/// <param name="payload">The payload associated with the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual Task SetExpirationDateAsync([NotNull] TToken token,
[CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken)
public virtual Task SetPayloadAsync([NotNull] TToken token, [CanBeNull] string payload, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
token.ExpirationDate = date;
token.Payload = payload;
return Task.CompletedTask;
}
/// <summary>
/// Sets the hash associated with a token.
/// Sets the reference identifier associated with a token.
/// Note: depending on the manager used to create the token,
/// the reference identifier may be hashed for security reasons.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="hash">The hash associated with the token.</param>
/// <param name="identifier">The reference identifier associated with the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual Task SetHashAsync([NotNull] TToken token, [CanBeNull] string hash, CancellationToken cancellationToken)
public virtual Task SetReferenceIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
token.Hash = hash;
token.ReferenceId = identifier;
return Task.CompletedTask;
}

2
src/OpenIddict.EntityFramework/OpenIddictExtensions.cs

@ -265,7 +265,7 @@ namespace Microsoft.Extensions.DependencyInjection
.IsConcurrencyToken();
builder.Entity<TToken>()
.Property(token => token.Hash)
.Property(token => token.ReferenceId)
.HasMaxLength(450)
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute()));

2
src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs

@ -291,7 +291,7 @@ namespace Microsoft.Extensions.DependencyInjection
{
entity.HasKey(token => token.Id);
entity.HasIndex(token => token.Hash)
entity.HasIndex(token => token.ReferenceId)
.IsUnique();
entity.Property(token => token.ConcurrencyToken)

29
src/OpenIddict.Models/OpenIddictToken.cs

@ -43,13 +43,6 @@ namespace OpenIddict.Models
/// </summary>
public virtual TAuthorization Authorization { get; set; }
/// <summary>
/// Gets or sets the encrypted payload
/// of the current token, if applicable.
/// This property is only used for reference tokens.
/// </summary>
public virtual string Ciphertext { get; set; }
/// <summary>
/// Gets or sets the concurrency token.
/// </summary>
@ -67,19 +60,27 @@ namespace OpenIddict.Models
/// </summary>
public virtual DateTimeOffset? ExpirationDate { get; set; }
/// <summary>
/// Gets or sets the hashed identifier associated
/// with the current token, if applicable.
/// This property is only used for reference tokens.
/// </summary>
public virtual string Hash { get; set; }
/// <summary>
/// Gets or sets the unique identifier
/// associated with the current token.
/// </summary>
public virtual TKey Id { get; set; }
/// <summary>
/// Gets or sets the payload of the current token, if applicable.
/// Note: this property is only used for reference tokens
/// and may be encrypted for security reasons.
/// </summary>
public virtual string Payload { get; set; }
/// <summary>
/// Gets or sets the reference identifier associated
/// with the current token, if applicable.
/// Note: this property is only used for reference tokens
/// and may be hashed or encrypted for security reasons.
/// </summary>
public virtual string ReferenceId { get; set; }
/// <summary>
/// Gets or sets the status of the current token.
/// </summary>

35
src/OpenIddict/OpenIddictProvider.Helpers.cs

@ -152,7 +152,7 @@ namespace OpenIddict
// Note: the data format is automatically replaced at startup time to ensure
// that encrypted tokens stored in the database cannot be considered as
// valid tokens if the developer decides to disable reference tokens support.
descriptor.Ciphertext = format.Protect(ticket);
descriptor.Payload = format.Protect(ticket);
// Generate a new crypto-secure random identifier that will be
// substituted to the ciphertext returned by the data format.
@ -160,14 +160,8 @@ namespace OpenIddict
options.RandomNumberGenerator.GetBytes(bytes);
result = Base64UrlEncoder.Encode(bytes);
// Compute the digest of the generated identifier and use
// it as the hashed identifier of the reference token.
// Doing that prevents token identifiers stolen from
// the database from being used as valid reference tokens.
using (var algorithm = SHA256.Create())
{
descriptor.Hash = Convert.ToBase64String(algorithm.ComputeHash(bytes));
}
// Obfuscate the reference identifier so it can be safely stored in the databse.
descriptor.ReferenceId = await Tokens.ObfuscateReferenceIdAsync(result, context.RequestAborted);
}
// Otherwise, only create a token metadata entry for authorization codes and refresh tokens.
@ -244,15 +238,11 @@ namespace OpenIddict
if (options.UseReferenceTokens)
{
string hash;
// Retrieve the token entry from the database.
// If it cannot be found, assume the token is not valid.
try
{
// 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)));
}
token = await Tokens.FindByReferenceIdAsync(value, context.RequestAborted);
}
// Swallow format-related exceptions to ensure badly formed
@ -262,13 +252,10 @@ namespace OpenIddict
return null;
}
// 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);
Logger.LogInformation("The reference token corresponding to the '{Identifier}' " +
"reference identifier cannot be found in the database.", value);
return null;
}
@ -284,8 +271,8 @@ namespace OpenIddict
// 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))
var payload = await Tokens.GetPayloadAsync(token, context.RequestAborted);
if (string.IsNullOrEmpty(payload))
{
Logger.LogWarning("The ciphertext associated with the token '{Identifier}' cannot be retrieved. " +
"This may indicate that the token is not a reference token.", identifier);
@ -293,7 +280,7 @@ namespace OpenIddict
return null;
}
ticket = format.Unprotect(ciphertext);
ticket = format.Unprotect(payload);
if (ticket == null)
{
Logger.LogWarning("The ciphertext associated with the token '{Identifier}' cannot be decrypted. " +

10
test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs

@ -355,7 +355,7 @@ namespace OpenIddict.Tests
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByHashAsync("coYFMTIt6jDp2O41qaUfV+XGhPsils3Z3YfmUvudrVw=", It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.FindByReferenceIdAsync("QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI", It.IsAny<CancellationToken>()))
.ReturnsAsync(value: null);
});
@ -395,7 +395,7 @@ namespace OpenIddict.Tests
Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]);
Mock.Get(manager).Verify(mock => mock.FindByHashAsync("coYFMTIt6jDp2O41qaUfV+XGhPsils3Z3YfmUvudrVw=", It.IsAny<CancellationToken>()), Times.Exactly(3));
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI", It.IsAny<CancellationToken>()), Times.Exactly(3));
}
[Fact]
@ -422,13 +422,13 @@ namespace OpenIddict.Tests
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByHashAsync("coYFMTIt6jDp2O41qaUfV+XGhPsils3Z3YfmUvudrVw=", It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.FindByReferenceIdAsync("QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("2YotnFZFEjr1zCsicMWpAA");
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
@ -475,7 +475,7 @@ namespace OpenIddict.Tests
Assert.Single(response.GetParameters());
Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]);
Mock.Get(manager).Verify(mock => mock.FindByHashAsync("coYFMTIt6jDp2O41qaUfV+XGhPsils3Z3YfmUvudrVw=", It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI", It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.IsValidAsync(token, It.IsAny<CancellationToken>()), Times.Once());
}

110
test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs

@ -140,7 +140,7 @@ namespace OpenIddict.Tests
Assert.True((bool) response[OpenIdConnectConstants.Claims.Active]);
format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByHashAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never());
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never());
}
[Fact]
@ -151,7 +151,7 @@ namespace OpenIddict.Tests
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
@ -194,7 +194,7 @@ namespace OpenIddict.Tests
Assert.Single(response.GetParameters());
Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]);
Mock.Get(manager).Verify(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()), Times.AtLeastOnce());
}
@ -206,13 +206,13 @@ namespace OpenIddict.Tests
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(value: null);
});
@ -252,7 +252,7 @@ namespace OpenIddict.Tests
Assert.Single(response.GetParameters());
Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]);
Mock.Get(manager).Verify(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()), Times.AtLeastOnce());
}
@ -269,13 +269,13 @@ namespace OpenIddict.Tests
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("2YotnFZFEjr1zCsicMWpAA");
});
@ -317,7 +317,7 @@ namespace OpenIddict.Tests
Assert.Single(response.GetParameters());
Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]);
Mock.Get(manager).Verify(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", 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());
}
@ -345,13 +345,13 @@ namespace OpenIddict.Tests
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("2YotnFZFEjr1zCsicMWpAA");
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
@ -413,7 +413,7 @@ namespace OpenIddict.Tests
Assert.Equal(1483228800, (long) response[OpenIdConnectConstants.Claims.IssuedAt]);
Assert.Equal(1484006400, (long) response[OpenIdConnectConstants.Claims.ExpiresAt]);
Mock.Get(manager).Verify(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()), Times.Once());
format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once());
}
@ -536,7 +536,7 @@ namespace OpenIddict.Tests
Assert.True((bool) response[OpenIdConnectConstants.Claims.Active]);
format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByHashAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never());
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never());
}
[Fact]
@ -547,7 +547,7 @@ namespace OpenIddict.Tests
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
@ -590,7 +590,7 @@ namespace OpenIddict.Tests
Assert.Single(response.GetParameters());
Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]);
Mock.Get(manager).Verify(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()), Times.AtLeastOnce());
}
@ -602,13 +602,13 @@ namespace OpenIddict.Tests
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(value: null);
});
@ -648,7 +648,7 @@ namespace OpenIddict.Tests
Assert.Single(response.GetParameters());
Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]);
Mock.Get(manager).Verify(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()), Times.AtLeastOnce());
}
@ -665,13 +665,13 @@ namespace OpenIddict.Tests
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("2YotnFZFEjr1zCsicMWpAA");
});
@ -713,7 +713,7 @@ namespace OpenIddict.Tests
Assert.Single(response.GetParameters());
Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]);
Mock.Get(manager).Verify(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", 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());
}
@ -739,13 +739,13 @@ namespace OpenIddict.Tests
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("2YotnFZFEjr1zCsicMWpAA");
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
@ -807,7 +807,7 @@ namespace OpenIddict.Tests
Assert.Equal(1483228800, (long) response[OpenIdConnectConstants.Claims.IssuedAt]);
Assert.Equal(1484006400, (long) response[OpenIdConnectConstants.Claims.ExpiresAt]);
Mock.Get(manager).Verify(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()), Times.Once());
format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once());
}
@ -1116,7 +1116,7 @@ namespace OpenIddict.Tests
Assert.True((bool) response[OpenIdConnectConstants.Claims.Active]);
format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByHashAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never());
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never());
}
[Fact]
@ -1127,7 +1127,7 @@ namespace OpenIddict.Tests
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
@ -1170,7 +1170,7 @@ namespace OpenIddict.Tests
Assert.Single(response.GetParameters());
Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]);
Mock.Get(manager).Verify(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()), Times.AtLeastOnce());
}
@ -1182,13 +1182,13 @@ namespace OpenIddict.Tests
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync(value: null);
});
@ -1228,7 +1228,7 @@ namespace OpenIddict.Tests
Assert.Single(response.GetParameters());
Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]);
Mock.Get(manager).Verify(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()), Times.AtLeastOnce());
}
@ -1245,13 +1245,13 @@ namespace OpenIddict.Tests
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("2YotnFZFEjr1zCsicMWpAA");
});
@ -1293,7 +1293,7 @@ namespace OpenIddict.Tests
Assert.Single(response.GetParameters());
Assert.False((bool) response[OpenIdConnectConstants.Claims.Active]);
Mock.Get(manager).Verify(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()), Times.AtLeastOnce());
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", 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());
}
@ -1319,13 +1319,13 @@ namespace OpenIddict.Tests
var manager = CreateTokenManager(instance =>
{
instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()))
.ReturnsAsync(token);
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny<CancellationToken>()))
instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("2YotnFZFEjr1zCsicMWpAA");
instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny<CancellationToken>()))
@ -1387,7 +1387,7 @@ namespace OpenIddict.Tests
Assert.Equal(1483228800, (long) response[OpenIdConnectConstants.Claims.IssuedAt]);
Assert.Equal(1484006400, (long) response[OpenIdConnectConstants.Claims.ExpiresAt]);
Mock.Get(manager).Verify(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()), Times.Once());
format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once());
}
@ -1627,6 +1627,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.ObfuscateReferenceIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync("B1F0D503-55A4-4B03-B05B-EF07713C18E1");
});
var server = CreateAuthorizationServer(builder =>
@ -1656,12 +1659,15 @@ namespace OpenIddict.Tests
// Assert
Assert.NotNull(response.AccessToken);
Mock.Get(manager).Verify(mock => mock.ObfuscateReferenceIdAsync(
It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Exactly(2));
Mock.Get(manager).Verify(mock => mock.CreateAsync(
It.Is<OpenIddictTokenDescriptor>(descriptor =>
descriptor.Ciphertext != null &&
descriptor.Hash != null &&
descriptor.ExpirationDate == token.ExpirationDate &&
descriptor.CreationDate == token.CreationDate &&
descriptor.Payload != null &&
descriptor.ReferenceId != null &&
descriptor.Subject == "Bob le Magnifique" &&
descriptor.Type == OpenIdConnectConstants.TokenTypeHints.AccessToken),
It.IsAny<CancellationToken>()), Times.Once());
@ -1887,10 +1893,10 @@ namespace OpenIddict.Tests
Mock.Get(manager).Verify(mock => mock.CreateAsync(
It.Is<OpenIddictTokenDescriptor>(descriptor =>
descriptor.Ciphertext == null &&
descriptor.Hash == null &&
descriptor.ExpirationDate == token.ExpirationDate &&
descriptor.CreationDate == token.CreationDate &&
descriptor.Payload == null &&
descriptor.ReferenceId == null &&
descriptor.Subject == "Bob le Magnifique" &&
descriptor.Type == OpenIdConnectConstants.TokenTypeHints.AuthorizationCode),
It.IsAny<CancellationToken>()), Times.Once());
@ -1913,6 +1919,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.ObfuscateReferenceIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync("B1F0D503-55A4-4B03-B05B-EF07713C18E1");
});
var server = CreateAuthorizationServer(builder =>
@ -1958,12 +1967,15 @@ namespace OpenIddict.Tests
// Assert
Assert.NotNull(response.Code);
Mock.Get(manager).Verify(mock => mock.ObfuscateReferenceIdAsync(
It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once());
Mock.Get(manager).Verify(mock => mock.CreateAsync(
It.Is<OpenIddictTokenDescriptor>(descriptor =>
descriptor.Ciphertext != null &&
descriptor.Hash != null &&
descriptor.ExpirationDate == token.ExpirationDate &&
descriptor.CreationDate == token.CreationDate &&
descriptor.Payload != null &&
descriptor.ReferenceId != null &&
descriptor.Subject == "Bob le Magnifique" &&
descriptor.Type == OpenIdConnectConstants.TokenTypeHints.AuthorizationCode),
It.IsAny<CancellationToken>()), Times.Once());
@ -2244,10 +2256,10 @@ namespace OpenIddict.Tests
Mock.Get(manager).Verify(mock => mock.CreateAsync(
It.Is<OpenIddictTokenDescriptor>(descriptor =>
descriptor.Ciphertext == null &&
descriptor.Hash == null &&
descriptor.ExpirationDate == token.ExpirationDate &&
descriptor.CreationDate == token.CreationDate &&
descriptor.Payload == null &&
descriptor.ReferenceId == null &&
descriptor.Subject == "Bob le Magnifique" &&
descriptor.Type == OpenIdConnectConstants.TokenTypeHints.RefreshToken),
It.IsAny<CancellationToken>()), Times.Once());
@ -2270,6 +2282,9 @@ namespace OpenIddict.Tests
instance.Setup(mock => mock.GetIdAsync(token, It.IsAny<CancellationToken>()))
.ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56");
instance.Setup(mock => mock.ObfuscateReferenceIdAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync("B1F0D503-55A4-4B03-B05B-EF07713C18E1");
});
var server = CreateAuthorizationServer(builder =>
@ -2299,12 +2314,15 @@ namespace OpenIddict.Tests
// Assert
Assert.NotNull(response.RefreshToken);
Mock.Get(manager).Verify(mock => mock.ObfuscateReferenceIdAsync(
It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Exactly(2));
Mock.Get(manager).Verify(mock => mock.CreateAsync(
It.Is<OpenIddictTokenDescriptor>(descriptor =>
descriptor.Ciphertext != null &&
descriptor.Hash != null &&
descriptor.ExpirationDate == token.ExpirationDate &&
descriptor.CreationDate == token.CreationDate &&
descriptor.Payload != null &&
descriptor.ReferenceId == "B1F0D503-55A4-4B03-B05B-EF07713C18E1" &&
descriptor.Subject == "Bob le Magnifique" &&
descriptor.Type == OpenIdConnectConstants.TokenTypeHints.RefreshToken),
It.IsAny<CancellationToken>()), Times.Once());

Loading…
Cancel
Save