From a19f5e81ff07127f71107651786082a916acc056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Sun, 10 Dec 2017 17:19:59 +0100 Subject: [PATCH] Rename OpenIddictToken.Ciphertext/Hash to Payload/ReferenceId and add new methods to allow overriding the default obfuscation --- .../Descriptors/OpenIddictTokenDescriptor.cs | 14 +-- .../Managers/OpenIddictTokenManager.cs | 92 ++++++++++----- .../Stores/IOpenIddictTokenStore.cs | 75 ++++++------ .../Stores/OpenIddictTokenStore.cs | 111 +++++++++--------- .../OpenIddictExtensions.cs | 2 +- .../OpenIddictExtensions.cs | 2 +- src/OpenIddict.Models/OpenIddictToken.cs | 29 ++--- src/OpenIddict/OpenIddictProvider.Helpers.cs | 35 ++---- .../OpenIddictProviderTests.Introspection.cs | 10 +- .../OpenIddictProviderTests.Serialization.cs | 110 +++++++++-------- 10 files changed, 263 insertions(+), 217 deletions(-) diff --git a/src/OpenIddict.Core/Descriptors/OpenIddictTokenDescriptor.cs b/src/OpenIddict.Core/Descriptors/OpenIddictTokenDescriptor.cs index 370ccd07..53fdb819 100644 --- a/src/OpenIddict.Core/Descriptors/OpenIddictTokenDescriptor.cs +++ b/src/OpenIddict.Core/Descriptors/OpenIddictTokenDescriptor.cs @@ -19,11 +19,6 @@ namespace OpenIddict.Core /// public string AuthorizationId { get; set; } - /// - /// Gets or sets the encrypted payload associated with the token. - /// - public string Ciphertext { get; set; } - /// /// Gets or sets the creation date associated with the token. /// @@ -35,9 +30,9 @@ namespace OpenIddict.Core public DateTimeOffset? ExpirationDate { get; set; } /// - /// Gets or sets the cryptographic hash associated with the token. + /// Gets or sets the payload associated with the token. /// - public string Hash { get; set; } + public string Payload { get; set; } /// /// Gets or sets the optional principal associated with the token. @@ -52,6 +47,11 @@ namespace OpenIddict.Core public IDictionary Properties { get; } = new Dictionary(StringComparer.Ordinal); + /// + /// Gets or sets the reference identifier associated with the token. + /// + public string ReferenceId { get; set; } + /// /// Gets or sets the status associated with the token. /// diff --git a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs index 3f0377f2..91282f10 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs +++ b/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 } /// - /// 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. /// - /// The hashed crypto-secure random identifier associated with the tokens. + /// The reference identifier associated with the tokens. /// The that can be used to abort the operation. /// /// A 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. /// - public virtual Task FindByHashAsync([NotNull] string hash, CancellationToken cancellationToken) + public virtual async Task 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); } /// @@ -347,98 +351,100 @@ namespace OpenIddict.Core } /// - /// Retrieves the ciphertext associated with a token. + /// Retrieves the creation date associated with a token. /// /// The token. /// The that can be used to abort the operation. /// /// A 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. /// - public virtual Task GetCiphertextAsync([NotNull] TToken token, CancellationToken cancellationToken) + public virtual Task GetCreationDateAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { throw new ArgumentNullException(nameof(token)); } - return Store.GetCiphertextAsync(token, cancellationToken); + return Store.GetCreationDateAsync(token, cancellationToken); } /// - /// Retrieves the creation date associated with a token. + /// Retrieves the expiration date associated with a token. /// /// The token. /// The that can be used to abort the operation. /// /// A 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. /// - public virtual Task GetCreationDateAsync([NotNull] TToken token, CancellationToken cancellationToken) + public virtual Task GetExpirationDateAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { throw new ArgumentNullException(nameof(token)); } - return Store.GetCreationDateAsync(token, cancellationToken); + return Store.GetExpirationDateAsync(token, cancellationToken); } /// - /// 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. /// /// The token. /// The that can be used to abort the operation. /// /// A 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. /// - public virtual Task GetExpirationDateAsync([NotNull] TToken token, CancellationToken cancellationToken) + public virtual Task GetReferenceIdAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { throw new ArgumentNullException(nameof(token)); } - return Store.GetExpirationDateAsync(token, cancellationToken); + return Store.GetReferenceIdAsync(token, cancellationToken); } /// - /// Retrieves the hashed identifier associated with a token. + /// Retrieves the unique identifier associated with a token. /// /// The token. /// The that can be used to abort the operation. /// /// A 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. /// - public virtual Task GetHashAsync([NotNull] TToken token, CancellationToken cancellationToken) + public virtual Task GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { throw new ArgumentNullException(nameof(token)); } - return Store.GetHashAsync(token, cancellationToken); + return Store.GetIdAsync(token, cancellationToken); } /// - /// Retrieves the unique identifier associated with a token. + /// Retrieves the payload associated with a token. /// /// The token. /// The that can be used to abort the operation. /// /// A 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. /// - public virtual Task GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken) + public virtual Task GetPayloadAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { throw new ArgumentNullException(nameof(token)); } - return Store.GetIdAsync(token, cancellationToken); + return Store.GetPayloadAsync(token, cancellationToken); } /// @@ -596,6 +602,30 @@ namespace OpenIddict.Core return Store.ListInvalidAsync(count, offset, cancellationToken); } + /// + /// 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. + /// + /// The client identifier. + /// The that can be used to abort the operation. + /// + /// A that can be used to monitor the asynchronous operation. + /// + public virtual Task 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)))); + } + } + /// /// Redeems a token. /// @@ -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); diff --git a/src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs b/src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs index dc53ff1d..566b78ad 100644 --- a/src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs +++ b/src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs @@ -82,26 +82,27 @@ namespace OpenIddict.Core Task> FindByAuthorizationIdAsync([NotNull] string identifier, CancellationToken cancellationToken); /// - /// Retrieves the list of tokens corresponding to the specified hash. + /// Retrieves a token using its unique identifier. /// - /// The hashed crypto-secure random identifier associated with the tokens. + /// The unique identifier associated with the token. /// The that can be used to abort the operation. /// /// A 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. /// - Task FindByHashAsync([NotNull] string hash, CancellationToken cancellationToken); + Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken); /// - /// 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. /// - /// The unique identifier associated with the token. + /// The reference identifier associated with the tokens. /// The that can be used to abort the operation. /// /// A 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. /// - Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken); + Task FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken); /// /// Retrieves the list of tokens corresponding to the specified subject. @@ -153,59 +154,61 @@ namespace OpenIddict.Core Task GetAuthorizationIdAsync([NotNull] TToken token, CancellationToken cancellationToken); /// - /// Retrieves the ciphertext associated with a token. + /// Retrieves the creation date associated with a token. /// /// The token. /// The that can be used to abort the operation. /// /// A 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. /// - Task GetCiphertextAsync([NotNull] TToken token, CancellationToken cancellationToken); + Task GetCreationDateAsync([NotNull] TToken token, CancellationToken cancellationToken); /// - /// Retrieves the creation date associated with a token. + /// Retrieves the expiration date associated with a token. /// /// The token. /// The that can be used to abort the operation. /// /// A 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. /// - Task GetCreationDateAsync([NotNull] TToken token, CancellationToken cancellationToken); + Task GetExpirationDateAsync([NotNull] TToken token, CancellationToken cancellationToken); /// - /// Retrieves the expiration date associated with a token. + /// Retrieves the unique identifier associated with a token. /// /// The token. /// The that can be used to abort the operation. /// /// A 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. /// - Task GetExpirationDateAsync([NotNull] TToken token, CancellationToken cancellationToken); + Task GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken); /// - /// Retrieves the hashed identifier associated with a token. + /// Retrieves the payload associated with a token. /// /// The token. /// The that can be used to abort the operation. /// /// A 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. /// - Task GetHashAsync([NotNull] TToken token, CancellationToken cancellationToken); + Task GetPayloadAsync([NotNull] TToken token, CancellationToken cancellationToken); /// - /// 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. /// /// The token. /// The that can be used to abort the operation. /// /// A 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. /// - Task GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken); + Task GetReferenceIdAsync([NotNull] TToken token, CancellationToken cancellationToken); /// /// Retrieves the status associated with a token. @@ -314,48 +317,50 @@ namespace OpenIddict.Core Task SetAuthorizationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken); /// - /// Sets the ciphertext associated with a token. + /// Sets the creation date associated with a token. /// /// The token. - /// The ciphertext associated with the token. + /// The creation date. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// - Task SetCiphertextAsync([NotNull] TToken token, [CanBeNull] string ciphertext, CancellationToken cancellationToken); + Task SetCreationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken); /// - /// Sets the creation date associated with a token. + /// Sets the expiration date associated with a token. /// /// The token. - /// The creation date. + /// The expiration date. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// - Task SetCreationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken); + Task SetExpirationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken); /// - /// Sets the expiration date associated with a token. + /// Sets the payload associated with a token. /// /// The token. - /// The expiration date. + /// The payload associated with the token. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// - Task SetExpirationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken); + Task SetPayloadAsync([NotNull] TToken token, [CanBeNull] string payload, CancellationToken cancellationToken); /// - /// 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. /// /// The token. - /// The hash associated with the token. + /// The reference identifier associated with the token. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// - Task SetHashAsync([NotNull] TToken token, [CanBeNull] string hash, CancellationToken cancellationToken); + Task SetReferenceIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken); /// /// Sets the status associated with a token. diff --git a/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs index 1f3cf4d8..dffd034e 100644 --- a/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs +++ b/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs @@ -123,51 +123,52 @@ namespace OpenIddict.Core } /// - /// Retrieves the list of tokens corresponding to the specified hash. + /// Retrieves a token using its unique identifier. /// - /// The hashed crypto-secure random identifier associated with the tokens. + /// The unique identifier associated with the token. /// The that can be used to abort the operation. /// /// A 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. /// - public virtual Task FindByHashAsync([NotNull] string hash, CancellationToken cancellationToken) + public virtual Task 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 Query(IQueryable tokens, string digest) + IQueryable Query(IQueryable 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); } /// - /// 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. /// - /// The unique identifier associated with the token. + /// The reference identifier associated with the tokens. /// The that can be used to abort the operation. /// /// A 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. /// - public virtual Task FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken) + public virtual Task FindByReferenceIdAsync([NotNull] string identifier, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier)); } - IQueryable Query(IQueryable tokens, TKey key) + IQueryable Query(IQueryable 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); } /// @@ -271,98 +272,100 @@ namespace OpenIddict.Core } /// - /// Retrieves the ciphertext associated with a token. + /// Retrieves the creation date associated with a token. /// /// The token. /// The that can be used to abort the operation. /// /// A 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. /// - public virtual Task GetCiphertextAsync([NotNull] TToken token, CancellationToken cancellationToken) + public virtual Task GetCreationDateAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { throw new ArgumentNullException(nameof(token)); } - return Task.FromResult(token.Ciphertext); + return Task.FromResult(token.CreationDate); } /// - /// Retrieves the creation date associated with a token. + /// Retrieves the expiration date associated with a token. /// /// The token. /// The that can be used to abort the operation. /// /// A 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. /// - public virtual Task GetCreationDateAsync([NotNull] TToken token, CancellationToken cancellationToken) + public virtual Task GetExpirationDateAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { throw new ArgumentNullException(nameof(token)); } - return Task.FromResult(token.CreationDate); + return Task.FromResult(token.ExpirationDate); } /// - /// Retrieves the expiration date associated with a token. + /// Retrieves the unique identifier associated with a token. /// /// The token. /// The that can be used to abort the operation. /// /// A 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. /// - public virtual Task GetExpirationDateAsync([NotNull] TToken token, CancellationToken cancellationToken) + public virtual Task 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)); } /// - /// Retrieves the hashed identifier associated with a token. + /// Retrieves the payload associated with a token. /// /// The token. /// The that can be used to abort the operation. /// /// A 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. /// - public virtual Task GetHashAsync([NotNull] TToken token, CancellationToken cancellationToken) + public virtual Task GetPayloadAsync([NotNull] TToken token, CancellationToken cancellationToken) { if (token == null) { throw new ArgumentNullException(nameof(token)); } - return Task.FromResult(token.Hash); + return Task.FromResult(token.Payload); } /// - /// 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. /// /// The token. /// The that can be used to abort the operation. /// /// A 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. /// - public virtual Task GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken) + public virtual Task 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); } /// @@ -540,36 +543,37 @@ namespace OpenIddict.Core public abstract Task SetApplicationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken); /// - /// Sets the ciphertext associated with a token. + /// Sets the creation date associated with a token. /// /// The token. - /// The ciphertext associated with the token. + /// The creation date. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// - 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; } /// - /// Sets the creation date associated with a token. + /// Sets the expiration date associated with a token. /// /// The token. - /// The creation date. + /// The expiration date. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// - 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; } /// - /// Sets the expiration date associated with a token. + /// Sets the payload associated with a token. /// /// The token. - /// The expiration date. + /// The payload associated with the token. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// - 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; } /// - /// 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. /// /// The token. - /// The hash associated with the token. + /// The reference identifier associated with the token. /// The that can be used to abort the operation. /// /// A that can be used to monitor the asynchronous operation. /// - 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; } diff --git a/src/OpenIddict.EntityFramework/OpenIddictExtensions.cs b/src/OpenIddict.EntityFramework/OpenIddictExtensions.cs index f215c10e..f195e345 100644 --- a/src/OpenIddict.EntityFramework/OpenIddictExtensions.cs +++ b/src/OpenIddict.EntityFramework/OpenIddictExtensions.cs @@ -265,7 +265,7 @@ namespace Microsoft.Extensions.DependencyInjection .IsConcurrencyToken(); builder.Entity() - .Property(token => token.Hash) + .Property(token => token.ReferenceId) .HasMaxLength(450) .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute())); diff --git a/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs b/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs index ae22404d..b01d996b 100644 --- a/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs +++ b/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) diff --git a/src/OpenIddict.Models/OpenIddictToken.cs b/src/OpenIddict.Models/OpenIddictToken.cs index 73834fcf..ce10c1fc 100644 --- a/src/OpenIddict.Models/OpenIddictToken.cs +++ b/src/OpenIddict.Models/OpenIddictToken.cs @@ -43,13 +43,6 @@ namespace OpenIddict.Models /// public virtual TAuthorization Authorization { get; set; } - /// - /// Gets or sets the encrypted payload - /// of the current token, if applicable. - /// This property is only used for reference tokens. - /// - public virtual string Ciphertext { get; set; } - /// /// Gets or sets the concurrency token. /// @@ -67,19 +60,27 @@ namespace OpenIddict.Models /// public virtual DateTimeOffset? ExpirationDate { get; set; } - /// - /// Gets or sets the hashed identifier associated - /// with the current token, if applicable. - /// This property is only used for reference tokens. - /// - public virtual string Hash { get; set; } - /// /// Gets or sets the unique identifier /// associated with the current token. /// public virtual TKey Id { get; set; } + /// + /// 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. + /// + public virtual string Payload { get; set; } + + /// + /// 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. + /// + public virtual string ReferenceId { get; set; } + /// /// Gets or sets the status of the current token. /// diff --git a/src/OpenIddict/OpenIddictProvider.Helpers.cs b/src/OpenIddict/OpenIddictProvider.Helpers.cs index 7bc501f8..369edd68 100644 --- a/src/OpenIddict/OpenIddictProvider.Helpers.cs +++ b/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. " + diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs index 077f36bc..a329aa86 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs +++ b/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())) + instance.Setup(mock => mock.FindByReferenceIdAsync("QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI", It.IsAny())) .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()), Times.Exactly(3)); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI", It.IsAny()), Times.Exactly(3)); } [Fact] @@ -422,13 +422,13 @@ namespace OpenIddict.Tests var manager = CreateTokenManager(instance => { - instance.Setup(mock => mock.FindByHashAsync("coYFMTIt6jDp2O41qaUfV+XGhPsils3Z3YfmUvudrVw=", It.IsAny())) + instance.Setup(mock => mock.FindByReferenceIdAsync("QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI", It.IsAny())) .ReturnsAsync(token); instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); - instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny())) + instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny())) .ReturnsAsync("2YotnFZFEjr1zCsicMWpAA"); instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny())) @@ -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()), Times.Once()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("QaTk2f6UPe9trKismGBJr0OIs0KqpvNrqRsJqGuJAAI", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.IsValidAsync(token, It.IsAny()), Times.Once()); } diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs b/test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs index 8fef4e9d..46e711e1 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs +++ b/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(), It.IsAny()), Times.Never()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync(It.IsAny(), It.IsAny()), Times.Never()); } [Fact] @@ -151,7 +151,7 @@ namespace OpenIddict.Tests var manager = CreateTokenManager(instance => { - instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny())) + instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny())) .ReturnsAsync(token); instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) @@ -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()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); } @@ -206,13 +206,13 @@ namespace OpenIddict.Tests var manager = CreateTokenManager(instance => { - instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny())) + instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny())) .ReturnsAsync(token); instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); - instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny())) + instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny())) .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()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); } @@ -269,13 +269,13 @@ namespace OpenIddict.Tests var manager = CreateTokenManager(instance => { - instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny())) + instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny())) .ReturnsAsync(token); instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); - instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny())) + instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny())) .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()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), 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())) + instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny())) .ReturnsAsync(token); instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); - instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny())) + instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny())) .ReturnsAsync("2YotnFZFEjr1zCsicMWpAA"); instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny())) @@ -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()), Times.Once()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.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(), It.IsAny()), Times.Never()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync(It.IsAny(), It.IsAny()), Times.Never()); } [Fact] @@ -547,7 +547,7 @@ namespace OpenIddict.Tests var manager = CreateTokenManager(instance => { - instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny())) + instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny())) .ReturnsAsync(token); instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) @@ -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()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); } @@ -602,13 +602,13 @@ namespace OpenIddict.Tests var manager = CreateTokenManager(instance => { - instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny())) + instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny())) .ReturnsAsync(token); instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); - instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny())) + instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny())) .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()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); } @@ -665,13 +665,13 @@ namespace OpenIddict.Tests var manager = CreateTokenManager(instance => { - instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny())) + instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny())) .ReturnsAsync(token); instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); - instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny())) + instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny())) .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()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), 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())) + instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny())) .ReturnsAsync(token); instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); - instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny())) + instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny())) .ReturnsAsync("2YotnFZFEjr1zCsicMWpAA"); instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny())) @@ -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()), Times.Once()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.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(), It.IsAny()), Times.Never()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync(It.IsAny(), It.IsAny()), Times.Never()); } [Fact] @@ -1127,7 +1127,7 @@ namespace OpenIddict.Tests var manager = CreateTokenManager(instance => { - instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny())) + instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny())) .ReturnsAsync(token); instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) @@ -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()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); } @@ -1182,13 +1182,13 @@ namespace OpenIddict.Tests var manager = CreateTokenManager(instance => { - instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny())) + instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny())) .ReturnsAsync(token); instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); - instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny())) + instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny())) .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()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.AtLeastOnce()); } @@ -1245,13 +1245,13 @@ namespace OpenIddict.Tests var manager = CreateTokenManager(instance => { - instance.Setup(mock => mock.FindByHashAsync("jTwUlKz7IT5tRnmMnxYW26OdS28cPG2rM04zQr0ez70=", It.IsAny())) + instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny())) .ReturnsAsync(token); instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); - instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny())) + instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny())) .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()), Times.AtLeastOnce()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.AtLeastOnce()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), 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())) + instance.Setup(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny())) .ReturnsAsync(token); instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); - instance.Setup(mock => mock.GetCiphertextAsync(token, It.IsAny())) + instance.Setup(mock => mock.GetPayloadAsync(token, It.IsAny())) .ReturnsAsync("2YotnFZFEjr1zCsicMWpAA"); instance.Setup(mock => mock.FindByIdAsync("3E228451-1555-46F7-A471-951EFBA23A56", It.IsAny())) @@ -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()), Times.Once()); + Mock.Get(manager).Verify(mock => mock.FindByReferenceIdAsync("HQnldPTjH_9m85GcS-5PPYaCxmJTt1umxOa2y9ggVUQ", It.IsAny()), Times.Once()); Mock.Get(manager).Verify(mock => mock.GetIdAsync(token, It.IsAny()), Times.Once()); format.Verify(mock => mock.Unprotect("2YotnFZFEjr1zCsicMWpAA"), Times.Once()); } @@ -1627,6 +1627,9 @@ namespace OpenIddict.Tests instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); + + instance.Setup(mock => mock.ObfuscateReferenceIdAsync(It.IsAny(), It.IsAny())) + .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(), It.IsAny()), Times.Exactly(2)); + Mock.Get(manager).Verify(mock => mock.CreateAsync( It.Is(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()), Times.Once()); @@ -1887,10 +1893,10 @@ namespace OpenIddict.Tests Mock.Get(manager).Verify(mock => mock.CreateAsync( It.Is(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()), Times.Once()); @@ -1913,6 +1919,9 @@ namespace OpenIddict.Tests instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); + + instance.Setup(mock => mock.ObfuscateReferenceIdAsync(It.IsAny(), It.IsAny())) + .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(), It.IsAny()), Times.Once()); + Mock.Get(manager).Verify(mock => mock.CreateAsync( It.Is(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()), Times.Once()); @@ -2244,10 +2256,10 @@ namespace OpenIddict.Tests Mock.Get(manager).Verify(mock => mock.CreateAsync( It.Is(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()), Times.Once()); @@ -2270,6 +2282,9 @@ namespace OpenIddict.Tests instance.Setup(mock => mock.GetIdAsync(token, It.IsAny())) .ReturnsAsync("3E228451-1555-46F7-A471-951EFBA23A56"); + + instance.Setup(mock => mock.ObfuscateReferenceIdAsync(It.IsAny(), It.IsAny())) + .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(), It.IsAny()), Times.Exactly(2)); + Mock.Get(manager).Verify(mock => mock.CreateAsync( It.Is(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()), Times.Once());