Browse Source

Backport the stores changes to OpenIddict 1.x

pull/553/head
Kévin Chalet 8 years ago
parent
commit
0b44a6674f
  1. 233
      src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs
  2. 108
      src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
  3. 69
      src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs
  4. 99
      src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
  5. 44
      src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs
  6. 64
      src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
  7. 64
      src/OpenIddict.Core/Stores/IOpenIddictScopeStore.cs
  8. 85
      src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs
  9. 68
      src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs
  10. 131
      src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs
  11. 100
      src/OpenIddict.Core/Stores/OpenIddictScopeStore.cs
  12. 177
      src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs
  13. 6
      src/OpenIddict.EntityFramework/OpenIddictExtensions.cs
  14. 64
      src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs
  15. 121
      src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs
  16. 43
      src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs
  17. 177
      src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs
  18. 6
      src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs
  19. 64
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs
  20. 102
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
  21. 43
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs
  22. 120
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs

233
src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs

@ -129,6 +129,7 @@ namespace OpenIddict.Core
"a confidential or hybrid application.");
}
// If a client secret was provided, obfuscate it.
if (!string.IsNullOrEmpty(secret))
{
secret = await ObfuscateClientSecretAsync(secret, cancellationToken);
@ -151,7 +152,7 @@ namespace OpenIddict.Core
}
/// <summary>
/// Creates a new application.
/// Creates a new application based on the specified descriptor.
/// Note: the default implementation automatically hashes the client
/// secret before storing it in the database, for security reasons.
/// </summary>
@ -168,43 +169,23 @@ namespace OpenIddict.Core
throw new ArgumentNullException(nameof(descriptor));
}
// If no client type was specified, assume it's a
// public application if no secret was provided.
if (string.IsNullOrEmpty(descriptor.Type))
{
descriptor.Type = string.IsNullOrEmpty(descriptor.ClientSecret) ?
OpenIddictConstants.ClientTypes.Public :
OpenIddictConstants.ClientTypes.Confidential;
}
// If the client is not a public application, throw an
// exception as the client secret is required in this case.
if (string.IsNullOrEmpty(descriptor.ClientSecret) &&
!string.Equals(descriptor.Type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("A client secret must be provided when creating " +
"a confidential or hybrid application.");
}
// Obfuscate the provided client secret.
if (!string.IsNullOrEmpty(descriptor.ClientSecret))
var application = await Store.InstantiateAsync(cancellationToken);
if (application == null)
{
descriptor.ClientSecret = await ObfuscateClientSecretAsync(descriptor.ClientSecret, cancellationToken);
throw new InvalidOperationException("An error occurred while trying to create a new application");
}
await ValidateAsync(descriptor, cancellationToken);
await PopulateAsync(application, descriptor, cancellationToken);
try
var secret = await Store.GetClientSecretAsync(application, cancellationToken);
if (!string.IsNullOrEmpty(secret))
{
return await Store.CreateAsync(descriptor, cancellationToken);
await Store.SetClientSecretAsync(application, /* secret: */ null, cancellationToken);
return await CreateAsync(application, secret, cancellationToken);
}
catch (Exception exception)
{
Logger.LogError(0, exception, "An exception occurred while trying to create a new application.");
throw;
}
return await CreateAsync(application, cancellationToken);
}
/// <summary>
@ -584,6 +565,83 @@ namespace OpenIddict.Core
await UpdateAsync(application, cancellationToken);
}
/// <summary>
/// Updates an existing application.
/// </summary>
/// <param name="application">The application to update.</param>
/// <param name="operation">The delegate used to update the application based on the given descriptor.</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 async Task UpdateAsync([NotNull] TApplication application,
[NotNull] Func<OpenIddictApplicationDescriptor, Task> operation, CancellationToken cancellationToken)
{
if (operation == null)
{
throw new ArgumentNullException(nameof(operation));
}
// Store the original client secret for later comparison.
var secret = await Store.GetClientSecretAsync(application, cancellationToken);
var descriptor = new OpenIddictApplicationDescriptor
{
ClientId = await Store.GetClientIdAsync(application, cancellationToken),
ClientSecret = secret,
DisplayName = await Store.GetDisplayNameAsync(application, cancellationToken),
Type = await Store.GetClientTypeAsync(application, cancellationToken)
};
foreach (var address in await Store.GetPostLogoutRedirectUrisAsync(application, cancellationToken))
{
// Ensure the address is not null or empty.
if (string.IsNullOrEmpty(address))
{
throw new ArgumentException("Callback URLs cannot be null or empty.");
}
// Ensure the address is a valid absolute URL.
if (!Uri.TryCreate(address, UriKind.Absolute, out Uri uri) || !uri.IsWellFormedOriginalString())
{
throw new ArgumentException("Callback URLs must be valid absolute URLs.");
}
descriptor.PostLogoutRedirectUris.Add(uri);
}
foreach (var address in await Store.GetRedirectUrisAsync(application, cancellationToken))
{
// Ensure the address is not null or empty.
if (string.IsNullOrEmpty(address))
{
throw new ArgumentException("Callback URLs cannot be null or empty.");
}
// Ensure the address is a valid absolute URL.
if (!Uri.TryCreate(address, UriKind.Absolute, out Uri uri) || !uri.IsWellFormedOriginalString())
{
throw new ArgumentException("Callback URLs must be valid absolute URLs.");
}
descriptor.RedirectUris.Add(uri);
}
await operation(descriptor);
await PopulateAsync(application, descriptor, cancellationToken);
// If the client secret was updated, re-obfuscate it before persisting the changes.
var comparand = await Store.GetClientSecretAsync(application, cancellationToken);
if (!string.Equals(secret, comparand, StringComparison.Ordinal))
{
await UpdateAsync(application, comparand, cancellationToken);
return;
}
await UpdateAsync(application, cancellationToken);
}
/// <summary>
/// Validates the client_secret associated with an application.
/// </summary>
@ -639,8 +697,7 @@ namespace OpenIddict.Core
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result
/// returns a boolean indicating whether the post_logout_redirect_uri was valid.
/// </returns>
public virtual async Task<bool> ValidatePostLogoutRedirectUriAsync(
[NotNull] string address, CancellationToken cancellationToken)
public virtual async Task<bool> ValidatePostLogoutRedirectUriAsync([NotNull] string address, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(address))
{
@ -707,120 +764,96 @@ namespace OpenIddict.Core
}
/// <summary>
/// Validates the application to ensure it's in a consistent state.
/// Populates the application using the specified descriptor.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="descriptor">The descriptor.</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>
protected virtual async Task ValidateAsync([NotNull] TApplication application, CancellationToken cancellationToken)
protected virtual async Task PopulateAsync([NotNull] TApplication application,
[NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken)
{
var descriptor = new OpenIddictApplicationDescriptor
{
ClientId = await Store.GetClientIdAsync(application, cancellationToken),
ClientSecret = await Store.GetClientSecretAsync(application, cancellationToken),
DisplayName = await Store.GetDisplayNameAsync(application, cancellationToken),
Type = await Store.GetClientTypeAsync(application, cancellationToken)
};
foreach (var address in await Store.GetPostLogoutRedirectUrisAsync(application, cancellationToken))
if (application == null)
{
// Ensure the address is not null or empty.
if (string.IsNullOrEmpty(address))
{
throw new ArgumentException("Callback URLs cannot be null or empty.");
}
// Ensure the address is a valid absolute URL.
if (!Uri.TryCreate(address, UriKind.Absolute, out Uri uri) || !uri.IsWellFormedOriginalString())
{
throw new ArgumentException("Callback URLs must be valid absolute URLs.");
}
descriptor.PostLogoutRedirectUris.Add(uri);
throw new ArgumentNullException(nameof(application));
}
foreach (var address in await Store.GetRedirectUrisAsync(application, cancellationToken))
if (descriptor == null)
{
// Ensure the address is not null or empty.
if (string.IsNullOrEmpty(address))
{
throw new ArgumentException("Callback URLs cannot be null or empty.");
}
// Ensure the address is a valid absolute URL.
if (!Uri.TryCreate(address, UriKind.Absolute, out Uri uri) || !uri.IsWellFormedOriginalString())
{
throw new ArgumentException("Callback URLs must be valid absolute URLs.");
}
descriptor.RedirectUris.Add(uri);
throw new ArgumentNullException(nameof(descriptor));
}
await ValidateAsync(descriptor, cancellationToken);
await Store.SetClientIdAsync(application, descriptor.ClientId, cancellationToken);
await Store.SetClientSecretAsync(application, descriptor.ClientSecret, cancellationToken);
await Store.SetClientTypeAsync(application, descriptor.Type, cancellationToken);
await Store.SetDisplayNameAsync(application, descriptor.DisplayName, cancellationToken);
await Store.SetPostLogoutRedirectUrisAsync(application, ImmutableArray.CreateRange(
descriptor.PostLogoutRedirectUris.Select(address => address.OriginalString)), cancellationToken);
await Store.SetRedirectUrisAsync(application, ImmutableArray.CreateRange(
descriptor.RedirectUris.Select(address => address.OriginalString)), cancellationToken);
}
/// <summary>
/// Validates the application descriptor to ensure it's in a consistent state.
/// Validates the application to ensure it's in a consistent state.
/// </summary>
/// <param name="descriptor">The application descriptor.</param>
/// <param name="application">The application.</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>
protected virtual Task ValidateAsync([NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken)
protected virtual async Task ValidateAsync([NotNull] TApplication application, CancellationToken cancellationToken)
{
if (descriptor == null)
if (string.IsNullOrEmpty(await Store.GetClientIdAsync(application, cancellationToken)))
{
throw new ArgumentNullException(nameof(descriptor));
throw new ArgumentException("The client identifier cannot be null or empty.", nameof(application));
}
if (string.IsNullOrEmpty(descriptor.ClientId))
{
throw new ArgumentException("The client identifier cannot be null or empty.", nameof(descriptor));
}
if (string.IsNullOrEmpty(descriptor.Type))
var type = await Store.GetClientTypeAsync(application, cancellationToken);
if (string.IsNullOrEmpty(type))
{
throw new ArgumentException("The client type cannot be null or empty.", nameof(descriptor));
throw new ArgumentException("The client type cannot be null or empty.", nameof(application));
}
// Ensure the application type is supported by the manager.
if (!string.Equals(descriptor.Type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(descriptor.Type, OpenIddictConstants.ClientTypes.Hybrid, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(descriptor.Type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
if (!string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(type, OpenIddictConstants.ClientTypes.Hybrid, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("Only 'confidential', 'hybrid' or 'public' applications are " +
"supported by the default application manager.", nameof(descriptor));
"supported by the default application manager.", nameof(application));
}
// Ensure a client secret was specified if the client is a confidential application.
if (string.IsNullOrEmpty(descriptor.ClientSecret) &&
string.Equals(descriptor.Type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase))
var secret = await Store.GetClientSecretAsync(application, cancellationToken);
if (string.IsNullOrEmpty(secret) &&
string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("The client secret cannot be null or empty for a confidential application.", nameof(descriptor));
throw new ArgumentException("The client secret cannot be null or empty for a confidential application.", nameof(application));
}
// Ensure no client secret was specified if the client is a public application.
else if (!string.IsNullOrEmpty(descriptor.ClientSecret) &&
string.Equals(descriptor.Type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
else if (!string.IsNullOrEmpty(secret) &&
string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("A client secret cannot be associated with a public application.", nameof(descriptor));
throw new ArgumentException("A client secret cannot be associated with a public application.", nameof(application));
}
// When callback URLs are specified, ensure they are valid and spec-compliant.
// See https://tools.ietf.org/html/rfc6749#section-3.1 for more information.
foreach (var uri in descriptor.PostLogoutRedirectUris.Concat(descriptor.RedirectUris))
foreach (var address in ImmutableArray.Create<string>()
.AddRange(await Store.GetPostLogoutRedirectUrisAsync(application, cancellationToken))
.AddRange(await Store.GetRedirectUrisAsync(application, cancellationToken)))
{
// Ensure the address is not null.
if (uri == null)
// Ensure the address is not null or empty.
if (string.IsNullOrEmpty(address))
{
throw new ArgumentException("Callback URLs cannot be null.");
throw new ArgumentException("Callback URLs cannot be null or empty.");
}
// Ensure the address is a valid and absolute URL.
if (!uri.IsAbsoluteUri || !uri.IsWellFormedOriginalString())
// Ensure the address is a valid absolute URL.
if (!Uri.TryCreate(address, UriKind.Absolute, out Uri uri) || !uri.IsWellFormedOriginalString())
{
throw new ArgumentException("Callback URLs must be valid absolute URLs.");
}
@ -831,8 +864,6 @@ namespace OpenIddict.Core
throw new ArgumentException("Callback URLs cannot contain a fragment.");
}
}
return Task.FromResult(0);
}
/// <summary>

108
src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs

@ -108,7 +108,7 @@ namespace OpenIddict.Core
}
/// <summary>
/// Creates a new authorization.
/// Creates a new authorization based on the specified descriptor.
/// </summary>
/// <param name="descriptor">The authorization descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
@ -122,26 +122,14 @@ namespace OpenIddict.Core
throw new ArgumentNullException(nameof(descriptor));
}
// If no type was explicitly specified, assume that
// the authorization is a permanent authorization.
if (string.IsNullOrEmpty(descriptor.Type))
{
descriptor.Type = OpenIddictConstants.AuthorizationTypes.Permanent;
}
await ValidateAsync(descriptor, cancellationToken);
try
var authorization = await Store.InstantiateAsync(cancellationToken);
if (authorization == null)
{
return await Store.CreateAsync(descriptor, cancellationToken);
throw new InvalidOperationException("An error occurred while trying to create a new authorization.");
}
catch (Exception exception)
{
Logger.LogError(0, exception, "An exception occurred while trying to create a new authorization.");
throw;
}
await PopulateAsync(authorization, descriptor, cancellationToken);
return await CreateAsync(authorization, cancellationToken);
}
/// <summary>
@ -462,81 +450,119 @@ namespace OpenIddict.Core
}
/// <summary>
/// Validates the authorization to ensure it's in a consistent state.
/// Updates an existing authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="authorization">The authorization to update.</param>
/// <param name="operation">The delegate used to update the authorization based on the given descriptor.</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>
protected virtual async Task ValidateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
public virtual async Task UpdateAsync([NotNull] TAuthorization authorization,
[NotNull] Func<OpenIddictAuthorizationDescriptor, Task> operation, CancellationToken cancellationToken)
{
if (authorization == null)
if (operation == null)
{
throw new ArgumentNullException(nameof(authorization));
throw new ArgumentNullException(nameof(operation));
}
var descriptor = new OpenIddictAuthorizationDescriptor
{
ApplicationId = await Store.GetApplicationIdAsync(authorization, cancellationToken),
Status = await Store.GetStatusAsync(authorization, cancellationToken),
Subject = await Store.GetSubjectAsync(authorization, cancellationToken),
Type = await Store.GetTypeAsync(authorization, cancellationToken)
};
await ValidateAsync(descriptor, cancellationToken);
foreach (var scope in await Store.GetScopesAsync(authorization, cancellationToken))
{
descriptor.Scopes.Add(scope);
}
await operation(descriptor);
await PopulateAsync(authorization, descriptor, cancellationToken);
await UpdateAsync(authorization, cancellationToken);
}
/// <summary>
/// Validates the authorization descriptor to ensure it's in a consistent state.
/// Populates the authorization using the specified descriptor.
/// </summary>
/// <param name="descriptor">The authorization descriptor.</param>
/// <param name="authorization">The authorization.</param>
/// <param name="descriptor">The descriptor.</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>
protected virtual Task ValidateAsync([NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken)
protected virtual async Task PopulateAsync([NotNull] TAuthorization authorization,
[NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
if (string.IsNullOrEmpty(descriptor.Type))
await Store.SetApplicationIdAsync(authorization, descriptor.ApplicationId, cancellationToken);
await Store.SetScopesAsync(authorization, ImmutableArray.CreateRange(descriptor.Scopes), cancellationToken);
await Store.SetStatusAsync(authorization, descriptor.Status, cancellationToken);
await Store.SetSubjectAsync(authorization, descriptor.Subject, cancellationToken);
await Store.SetTypeAsync(authorization, descriptor.Type, cancellationToken);
}
/// <summary>
/// Validates the authorization to ensure it's in a consistent state.
/// </summary>
/// <param name="authorization">The authorization.</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>
protected virtual async Task ValidateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
var type = await Store.GetTypeAsync(authorization, cancellationToken);
if (string.IsNullOrEmpty(type))
{
throw new ArgumentException("The authorization type cannot be null or empty.", nameof(descriptor));
throw new ArgumentException("The authorization type cannot be null or empty.", nameof(authorization));
}
if (!string.Equals(descriptor.Type, OpenIddictConstants.AuthorizationTypes.AdHoc, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(descriptor.Type, OpenIddictConstants.AuthorizationTypes.Permanent, StringComparison.OrdinalIgnoreCase))
if (!string.Equals(type, OpenIddictConstants.AuthorizationTypes.AdHoc, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(type, OpenIddictConstants.AuthorizationTypes.Permanent, StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("The specified authorization type is not supported by the default token manager.");
throw new ArgumentException("The specified authorization type is not supported by the default token manager.", nameof(authorization));
}
if (string.IsNullOrEmpty(descriptor.Status))
if (string.IsNullOrEmpty(await Store.GetStatusAsync(authorization, cancellationToken)))
{
throw new ArgumentException("The status cannot be null or empty.");
throw new ArgumentException("The status cannot be null or empty.", nameof(authorization));
}
if (string.IsNullOrEmpty(descriptor.Subject))
if (string.IsNullOrEmpty(await Store.GetSubjectAsync(authorization, cancellationToken)))
{
throw new ArgumentException("The subject cannot be null or empty.");
throw new ArgumentException("The subject cannot be null or empty.", nameof(authorization));
}
// Ensure that the scopes are not null or empty and do not contain spaces.
foreach (var scope in descriptor.Scopes)
foreach (var scope in await Store.GetScopesAsync(authorization, cancellationToken))
{
if (string.IsNullOrEmpty(scope))
{
throw new ArgumentException("Scopes cannot be null or empty.", nameof(descriptor));
throw new ArgumentException("Scopes cannot be null or empty.", nameof(authorization));
}
if (scope.Contains(OpenIddictConstants.Separators.Space))
{
throw new ArgumentException("Scopes cannot contain spaces.", nameof(descriptor));
throw new ArgumentException("Scopes cannot contain spaces.", nameof(authorization));
}
}
return Task.FromResult(0);
}
}
}

69
src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs

@ -100,7 +100,7 @@ namespace OpenIddict.Core
}
/// <summary>
/// Creates a new scope.
/// Creates a new scope based on the specified descriptor.
/// </summary>
/// <param name="descriptor">The scope descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
@ -114,17 +114,14 @@ namespace OpenIddict.Core
throw new ArgumentNullException(nameof(descriptor));
}
try
var scope = await Store.InstantiateAsync(cancellationToken);
if (scope == null)
{
return await Store.CreateAsync(descriptor, cancellationToken);
throw new InvalidOperationException("An error occurred while trying to create a new scope.");
}
catch (Exception exception)
{
Logger.LogError(0, exception, "An exception occurred while trying to create a new scope.");
throw;
}
await PopulateAsync(scope, descriptor, cancellationToken);
return await CreateAsync(scope, cancellationToken);
}
/// <summary>
@ -237,5 +234,59 @@ namespace OpenIddict.Core
throw;
}
}
/// <summary>
/// Updates an existing scope.
/// </summary>
/// <param name="scope">The scope to update.</param>
/// <param name="operation">The delegate used to update the scope based on the given descriptor.</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 async Task UpdateAsync([NotNull] TScope scope,
[NotNull] Func<OpenIddictScopeDescriptor, Task> operation, CancellationToken cancellationToken)
{
if (operation == null)
{
throw new ArgumentNullException(nameof(operation));
}
var descriptor = new OpenIddictScopeDescriptor
{
Description = await Store.GetDescriptionAsync(scope, cancellationToken),
Name = await Store.GetNameAsync(scope, cancellationToken)
};
await operation(descriptor);
await PopulateAsync(scope, descriptor, cancellationToken);
await UpdateAsync(scope, cancellationToken);
}
/// <summary>
/// Populates the scope using the specified descriptor.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="descriptor">The descriptor.</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>
protected virtual async Task PopulateAsync([NotNull] TScope scope,
[NotNull] OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
await Store.SetDescriptionAsync(scope, descriptor.Description, cancellationToken);
await Store.SetNameAsync(scope, descriptor.Description, cancellationToken);
}
}
}

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

@ -102,7 +102,7 @@ namespace OpenIddict.Core
}
/// <summary>
/// Creates a new token, which is associated with a particular subject.
/// Creates a new token based on the specified descriptor.
/// </summary>
/// <param name="descriptor">The token descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
@ -116,19 +116,14 @@ namespace OpenIddict.Core
throw new ArgumentNullException(nameof(descriptor));
}
await ValidateAsync(descriptor, cancellationToken);
try
var token = await Store.InstantiateAsync(cancellationToken);
if (token == null)
{
return await Store.CreateAsync(descriptor, cancellationToken);
throw new InvalidOperationException("An error occurred while trying to create a new token");
}
catch (Exception exception)
{
Logger.LogError(0, exception, "An exception occurred while trying to create a new token.");
throw;
}
await PopulateAsync(token, descriptor, cancellationToken);
return await CreateAsync(token, cancellationToken);
}
/// <summary>
@ -674,68 +669,110 @@ namespace OpenIddict.Core
}
/// <summary>
/// Validates the token to ensure it's in a consistent state.
/// Updates an existing token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="token">The token to update.</param>
/// <param name="operation">The delegate used to update the token based on the given descriptor.</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>
protected virtual async Task ValidateAsync([NotNull] TToken token, CancellationToken cancellationToken)
public virtual async Task UpdateAsync([NotNull] TToken token,
[NotNull] Func<OpenIddictTokenDescriptor, Task> operation, CancellationToken cancellationToken)
{
if (token == null)
if (operation == null)
{
throw new ArgumentNullException(nameof(token));
throw new ArgumentNullException(nameof(operation));
}
var descriptor = new OpenIddictTokenDescriptor
{
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),
Status = await Store.GetStatusAsync(token, cancellationToken),
Subject = await Store.GetSubjectAsync(token, cancellationToken),
Type = await Store.GetTokenTypeAsync(token, cancellationToken)
};
await ValidateAsync(descriptor, cancellationToken);
await operation(descriptor);
await PopulateAsync(token, descriptor, cancellationToken);
await UpdateAsync(token, cancellationToken);
}
/// <summary>
/// Validates the token descriptor to ensure it's in a consistent state.
/// Populates the token using the specified descriptor.
/// </summary>
/// <param name="descriptor">The token descriptor.</param>
/// <param name="token">The token.</param>
/// <param name="descriptor">The descriptor.</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>
protected virtual Task ValidateAsync([NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken)
protected virtual async Task PopulateAsync([NotNull] TToken token,
[NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
if (string.IsNullOrEmpty(descriptor.Type))
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.SetStatusAsync(token, descriptor.Status, cancellationToken);
await Store.SetSubjectAsync(token, descriptor.Subject, cancellationToken);
await Store.SetTokenTypeAsync(token, descriptor.Type, cancellationToken);
}
/// <summary>
/// Validates the token to ensure it's in a consistent state.
/// </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.
/// </returns>
protected virtual async Task ValidateAsync([NotNull] TToken token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentException("The token type cannot be null or empty.", nameof(descriptor));
throw new ArgumentNullException(nameof(token));
}
if (!string.Equals(descriptor.Type, OpenIddictConstants.TokenTypes.AccessToken, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(descriptor.Type, OpenIddictConstants.TokenTypes.AuthorizationCode, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(descriptor.Type, OpenIddictConstants.TokenTypes.RefreshToken, StringComparison.OrdinalIgnoreCase))
var type = await Store.GetTokenTypeAsync(token, cancellationToken);
if (string.IsNullOrEmpty(type))
{
throw new ArgumentException("The specified token type is not supported by the default token manager.");
throw new ArgumentException("The token type cannot be null or empty.", nameof(token));
}
if (string.IsNullOrEmpty(descriptor.Status))
if (!string.Equals(type, OpenIddictConstants.TokenTypes.AccessToken, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(type, OpenIddictConstants.TokenTypes.AuthorizationCode, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(type, OpenIddictConstants.TokenTypes.RefreshToken, StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("The status cannot be null or empty.");
throw new ArgumentException("The specified token type is not supported by the default token manager.", nameof(token));
}
if (string.IsNullOrEmpty(descriptor.Subject))
if (string.IsNullOrEmpty(await Store.GetStatusAsync(token, cancellationToken)))
{
throw new ArgumentException("The subject cannot be null or empty.");
throw new ArgumentException("The status cannot be null or empty.", nameof(token));
}
return Task.FromResult(0);
if (string.IsNullOrEmpty(await Store.GetSubjectAsync(token, cancellationToken)))
{
throw new ArgumentException("The subject cannot be null or empty.", nameof(token));
}
}
}
}

44
src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs

@ -52,16 +52,6 @@ namespace OpenIddict.Core
/// </returns>
Task<TApplication> CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Creates a new application.
/// </summary>
/// <param name="descriptor">The application descriptor.</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 application.
/// </returns>
Task<TApplication> CreateAsync([NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken);
/// <summary>
/// Removes an existing application.
/// </summary>
@ -207,6 +197,16 @@ namespace OpenIddict.Core
/// </returns>
Task<ImmutableArray<string>> GetRedirectUrisAsync([NotNull] TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Instantiates a new application.
/// </summary>
/// <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 instantiated application, that can be persisted in the database.
/// </returns>
Task<TApplication> InstantiateAsync(CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
@ -231,6 +231,17 @@ namespace OpenIddict.Core
/// </returns>
Task<ImmutableArray<TResult>> ListAsync<TResult>([NotNull] Func<IQueryable<TApplication>, IQueryable<TResult>> query, CancellationToken cancellationToken);
/// <summary>
/// Sets the client identifier associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="identifier">The client identifier associated with the application.</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 SetClientIdAsync([NotNull] TApplication application, [CanBeNull] string identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the client secret associated with an application.
/// Note: depending on the manager used to create the application,
@ -253,7 +264,18 @@ namespace OpenIddict.Core
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
Task SetClientTypeAsync([NotNull] TApplication application, [NotNull] string type, CancellationToken cancellationToken);
Task SetClientTypeAsync([NotNull] TApplication application, [CanBeNull] string type, CancellationToken cancellationToken);
/// <summary>
/// Sets the display name associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="name">The display name associated with the application.</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 SetDisplayNameAsync([NotNull] TApplication application, [CanBeNull] string name, CancellationToken cancellationToken);
/// <summary>
/// Sets the logout callback addresses associated with an application.

64
src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs

@ -51,16 +51,6 @@ namespace OpenIddict.Core
/// </returns>
Task<TAuthorization> CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Creates a new authorization.
/// </summary>
/// <param name="descriptor">The authorization descriptor.</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 authorization.
/// </returns>
Task<TAuthorization> CreateAsync([NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken);
/// <summary>
/// Removes an existing authorization.
/// </summary>
@ -129,6 +119,17 @@ namespace OpenIddict.Core
/// </returns>
Task<string> GetIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the scopes associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</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 scopes associated with the specified authorization.
/// </returns>
Task<ImmutableArray<string>> GetScopesAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the status associated with an authorization.
/// </summary>
@ -162,6 +163,16 @@ namespace OpenIddict.Core
/// </returns>
Task<string> GetTypeAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Instantiates a new authorization.
/// </summary>
/// <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 instantiated authorization, that can be persisted in the database.
/// </returns>
Task<TAuthorization> InstantiateAsync(CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
@ -208,7 +219,20 @@ namespace OpenIddict.Core
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
Task SetApplicationIdAsync([NotNull] TAuthorization authorization, [CanBeNull] string identifier, CancellationToken cancellationToken);
Task SetApplicationIdAsync([NotNull] TAuthorization authorization,
[CanBeNull] string identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the scopes associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="scopes">The scopes associated with the authorization.</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 SetScopesAsync([NotNull] TAuthorization authorization,
ImmutableArray<string> scopes, CancellationToken cancellationToken);
/// <summary>
/// Sets the status associated with an authorization.
@ -219,7 +243,20 @@ namespace OpenIddict.Core
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
Task SetStatusAsync([NotNull] TAuthorization authorization, [NotNull] string status, CancellationToken cancellationToken);
Task SetStatusAsync([NotNull] TAuthorization authorization,
[CanBeNull] string status, CancellationToken cancellationToken);
/// <summary>
/// Sets the subject associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="subject">The subject associated with the authorization.</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 SetSubjectAsync([NotNull] TAuthorization authorization,
[CanBeNull] string subject, CancellationToken cancellationToken);
/// <summary>
/// Sets the type associated with an authorization.
@ -230,7 +267,8 @@ namespace OpenIddict.Core
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
Task SetTypeAsync([NotNull] TAuthorization authorization, [NotNull] string type, CancellationToken cancellationToken);
Task SetTypeAsync([NotNull] TAuthorization authorization,
[CanBeNull] string type, CancellationToken cancellationToken);
/// <summary>
/// Updates an existing authorization.

64
src/OpenIddict.Core/Stores/IOpenIddictScopeStore.cs

@ -51,16 +51,6 @@ namespace OpenIddict.Core
/// </returns>
Task<TScope> CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Creates a new scope.
/// </summary>
/// <param name="descriptor">The scope descriptor.</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 scope.
/// </returns>
Task<TScope> CreateAsync([NotNull] OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken);
/// <summary>
/// Removes an existing scope.
/// </summary>
@ -83,6 +73,38 @@ namespace OpenIddict.Core
/// </returns>
Task<TResult> GetAsync<TResult>([NotNull] Func<IQueryable<TScope>, IQueryable<TResult>> query, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the description associated with a scope.
/// </summary>
/// <param name="scope">The scope.</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 description associated with the specified scope.
/// </returns>
Task<string> GetDescriptionAsync([NotNull] TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</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 name associated with the specified scope.
/// </returns>
Task<string> GetNameAsync([NotNull] TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Instantiates a new scope.
/// </summary>
/// <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 instantiated scope, that can be persisted in the database.
/// </returns>
Task<TScope> InstantiateAsync(CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
@ -107,6 +129,28 @@ namespace OpenIddict.Core
/// </returns>
Task<ImmutableArray<TResult>> ListAsync<TResult>([NotNull] Func<IQueryable<TScope>, IQueryable<TResult>> query, CancellationToken cancellationToken);
/// <summary>
/// Sets the description associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="description">The description associated with the authorization.</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 SetDescriptionAsync([NotNull] TScope scope, [CanBeNull] string description, CancellationToken cancellationToken);
/// <summary>
/// Sets the name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="name">The name associated with the authorization.</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 SetNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken);
/// <summary>
/// Updates an existing scope.
/// </summary>

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

@ -51,16 +51,6 @@ namespace OpenIddict.Core
/// </returns>
Task<TToken> CreateAsync([NotNull] TToken token, CancellationToken cancellationToken);
/// <summary>
/// Creates a new token.
/// </summary>
/// <param name="descriptor">The token descriptor.</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.
/// </returns>
Task<TToken> CreateAsync([NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken);
/// <summary>
/// Removes a token.
/// </summary>
@ -103,7 +93,7 @@ namespace OpenIddict.Core
Task<TToken> FindByHashAsync([NotNull] string hash, CancellationToken cancellationToken);
/// <summary>
/// Retrieves an token using its unique identifier.
/// Retrieves a token using its unique identifier.
/// </summary>
/// <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>
@ -246,6 +236,16 @@ namespace OpenIddict.Core
/// </returns>
Task<string> GetTokenTypeAsync([NotNull] TToken token, CancellationToken cancellationToken);
/// <summary>
/// Instantiates a new token.
/// </summary>
/// <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 instantiated token, that can be persisted in the database.
/// </returns>
Task<TToken> InstantiateAsync(CancellationToken cancellationToken);
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
@ -287,7 +287,7 @@ namespace OpenIddict.Core
/// Sets the application identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="identifier">The unique identifier associated with the client application.</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.
@ -298,24 +298,57 @@ namespace OpenIddict.Core
/// Sets the authorization identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="identifier">The unique identifier associated with the authorization.</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.
/// </returns>
Task SetAuthorizationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the ciphertext associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="ciphertext">The ciphertext 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 SetCiphertextAsync([NotNull] TToken token, [CanBeNull] string ciphertext, CancellationToken cancellationToken);
/// <summary>
/// Sets the creation date associated with a token.
/// </summary>
/// <param name="token">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 SetCreationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken);
/// <summary>
/// Sets the expiration date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="date">The date on which the token will no longer be considered valid.</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 SetExpirationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken);
/// <summary>
/// Sets the hash associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="hash">The hash 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);
/// <summary>
/// Sets the status associated with a token.
/// </summary>
@ -325,7 +358,29 @@ namespace OpenIddict.Core
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
Task SetStatusAsync([NotNull] TToken token, [NotNull] string status, CancellationToken cancellationToken);
Task SetStatusAsync([NotNull] TToken token, [CanBeNull] string status, CancellationToken cancellationToken);
/// <summary>
/// Sets the subject associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="subject">The subject 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 SetSubjectAsync([NotNull] TToken token, [CanBeNull] string subject, CancellationToken cancellationToken);
/// <summary>
/// Sets the token type associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="type">The token type 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 SetTokenTypeAsync([NotNull] TToken token, [CanBeNull] string type, CancellationToken cancellationToken);
/// <summary>
/// Updates an existing token.

68
src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs

@ -64,16 +64,6 @@ namespace OpenIddict.Core
/// </returns>
public abstract Task<TApplication> CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken);
/// <summary>
/// Creates a new application.
/// </summary>
/// <param name="descriptor">The application descriptor.</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 application.
/// </returns>
public abstract Task<TApplication> CreateAsync([NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken);
/// <summary>
/// Removes an existing application.
/// </summary>
@ -425,6 +415,16 @@ namespace OpenIddict.Core
return Task.FromResult(ImmutableArray.Create(uris));
}
/// <summary>
/// Instantiates a new application.
/// </summary>
/// <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 instantiated application, that can be persisted in the database.
/// </returns>
public virtual Task<TApplication> InstantiateAsync(CancellationToken cancellationToken) => Task.FromResult(new TApplication());
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
@ -469,6 +469,28 @@ namespace OpenIddict.Core
/// </returns>
public abstract Task<ImmutableArray<TResult>> ListAsync<TResult>([NotNull] Func<IQueryable<TApplication>, IQueryable<TResult>> query, CancellationToken cancellationToken);
/// <summary>
/// Sets the client identifier associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="identifier">The client identifier associated with the application.</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 SetClientIdAsync([NotNull] TApplication application,
[CanBeNull] string identifier, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
application.ClientId = identifier;
return Task.FromResult(0);
}
/// <summary>
/// Sets the client secret associated with an application.
/// Note: depending on the manager used to create the application,
@ -502,19 +524,37 @@ namespace OpenIddict.Core
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual Task SetClientTypeAsync([NotNull] TApplication application, [NotNull] string type, CancellationToken cancellationToken)
public virtual Task SetClientTypeAsync([NotNull] TApplication application,
[CanBeNull] string type, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
if (string.IsNullOrEmpty(type))
application.Type = type;
return Task.FromResult(0);
}
/// <summary>
/// Sets the display name associated with an application.
/// </summary>
/// <param name="application">The application.</param>
/// <param name="name">The display name associated with the application.</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 SetDisplayNameAsync([NotNull] TApplication application,
[CanBeNull] string name, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentException("The client type cannot be null or empty.", nameof(type));
throw new ArgumentNullException(nameof(application));
}
application.Type = type;
application.DisplayName = name;
return Task.FromResult(0);
}

131
src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs

@ -64,16 +64,6 @@ namespace OpenIddict.Core
/// </returns>
public abstract Task<TAuthorization> CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken);
/// <summary>
/// Creates a new authorization.
/// </summary>
/// <param name="descriptor">The authorization descriptor.</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 authorization.
/// </returns>
public abstract Task<TAuthorization> CreateAsync([NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken);
/// <summary>
/// Removes an existing authorization.
/// </summary>
@ -170,12 +160,15 @@ namespace OpenIddict.Core
return ConvertIdentifierToString(authorization.Application.Id);
}
var key = await GetAsync(authorizations =>
from element in authorizations
where element.Id.Equals(authorization.Id)
select element.Application.Id, cancellationToken);
IQueryable<TKey> Query(IQueryable<TAuthorization> authorizations)
{
return from element in authorizations
where element.Id.Equals(authorization.Id)
where element.Application != null
select element.Application.Id;
}
return ConvertIdentifierToString(key);
return ConvertIdentifierToString(await GetAsync(Query, cancellationToken));
}
/// <summary>
@ -209,6 +202,34 @@ namespace OpenIddict.Core
return Task.FromResult(ConvertIdentifierToString(authorization.Id));
}
/// <summary>
/// Retrieves the scopes associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</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 scopes associated with the specified authorization.
/// </returns>
public virtual Task<ImmutableArray<string>> GetScopesAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
if (string.IsNullOrEmpty(authorization.Scopes))
{
return Task.FromResult(ImmutableArray.Create<string>());
}
var scopes = authorization.Scopes.Split(
new[] { OpenIddictConstants.Separators.Space },
StringSplitOptions.RemoveEmptyEntries);
return Task.FromResult(ImmutableArray.Create(scopes));
}
/// <summary>
/// Retrieves the status associated with an authorization.
/// </summary>
@ -261,6 +282,16 @@ namespace OpenIddict.Core
return Task.FromResult(authorization.Type);
}
/// <summary>
/// Instantiates a new authorization.
/// </summary>
/// <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 instantiated authorization, that can be persisted in the database.
/// </returns>
public virtual Task<TAuthorization> InstantiateAsync(CancellationToken cancellationToken) => Task.FromResult(new TAuthorization());
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
@ -352,7 +383,47 @@ namespace OpenIddict.Core
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public abstract Task SetApplicationIdAsync([NotNull] TAuthorization authorization, [CanBeNull] string identifier, CancellationToken cancellationToken);
public abstract Task SetApplicationIdAsync([NotNull] TAuthorization authorization,
[CanBeNull] string identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the scopes associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="scopes">The scopes associated with the authorization.</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 SetScopesAsync([NotNull] TAuthorization authorization,
ImmutableArray<string> scopes, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
if (scopes.IsDefaultOrEmpty)
{
authorization.Scopes = null;
return Task.FromResult(0);
}
if (scopes.Any(scope => string.IsNullOrEmpty(scope)))
{
throw new ArgumentException("Scopes cannot be null or empty.", nameof(authorization));
}
if (scopes.Any(scope => scope.Contains(OpenIddictConstants.Separators.Space)))
{
throw new ArgumentException("Scopes cannot contain spaces.", nameof(authorization));
}
authorization.Scopes = string.Join(OpenIddictConstants.Separators.Space, scopes);
return Task.FromResult(0);
}
/// <summary>
/// Sets the status associated with an authorization.
@ -363,7 +434,8 @@ namespace OpenIddict.Core
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual Task SetStatusAsync([NotNull] TAuthorization authorization, [NotNull] string status, CancellationToken cancellationToken)
public virtual Task SetStatusAsync([NotNull] TAuthorization authorization,
[CanBeNull] string status, CancellationToken cancellationToken)
{
if (authorization == null)
{
@ -375,6 +447,28 @@ namespace OpenIddict.Core
return Task.FromResult(0);
}
/// <summary>
/// Sets the subject associated with an authorization.
/// </summary>
/// <param name="authorization">The authorization.</param>
/// <param name="subject">The subject associated with the authorization.</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 SetSubjectAsync([NotNull] TAuthorization authorization,
[CanBeNull] string subject, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
authorization.Subject = subject;
return Task.FromResult(0);
}
/// <summary>
/// Sets the type associated with an authorization.
/// </summary>
@ -384,7 +478,8 @@ namespace OpenIddict.Core
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual Task SetTypeAsync([NotNull] TAuthorization authorization, [NotNull] string type, CancellationToken cancellationToken)
public virtual Task SetTypeAsync([NotNull] TAuthorization authorization,
[CanBeNull] string type, CancellationToken cancellationToken)
{
if (authorization == null)
{

100
src/OpenIddict.Core/Stores/OpenIddictScopeStore.cs

@ -60,16 +60,6 @@ namespace OpenIddict.Core
/// </returns>
public abstract Task<TScope> CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken);
/// <summary>
/// Creates a new scope.
/// </summary>
/// <param name="descriptor">The scope descriptor.</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 scope.
/// </returns>
public abstract Task<TScope> CreateAsync([NotNull] OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken);
/// <summary>
/// Removes an existing scope.
/// </summary>
@ -92,6 +82,54 @@ namespace OpenIddict.Core
/// </returns>
public abstract Task<TResult> GetAsync<TResult>([NotNull] Func<IQueryable<TScope>, IQueryable<TResult>> query, CancellationToken cancellationToken);
/// <summary>
/// Retrieves the description associated with a scope.
/// </summary>
/// <param name="scope">The scope.</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 description associated with the specified scope.
/// </returns>
public virtual Task<string> GetDescriptionAsync([NotNull] TScope scope, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
return Task.FromResult(scope.Description);
}
/// <summary>
/// Retrieves the name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</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 name associated with the specified scope.
/// </returns>
public virtual Task<string> GetNameAsync([NotNull] TScope scope, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
return Task.FromResult(scope.Name);
}
/// <summary>
/// Instantiates a new scope.
/// </summary>
/// <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 instantiated scope, that can be persisted in the database.
/// </returns>
public virtual Task<TScope> InstantiateAsync(CancellationToken cancellationToken) => Task.FromResult(new TScope());
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
@ -136,6 +174,48 @@ namespace OpenIddict.Core
/// </returns>
public abstract Task<ImmutableArray<TResult>> ListAsync<TResult>([NotNull] Func<IQueryable<TScope>, IQueryable<TResult>> query, CancellationToken cancellationToken);
/// <summary>
/// Sets the description associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="description">The description associated with the authorization.</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 SetDescriptionAsync([NotNull] TScope scope, [CanBeNull] string description, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
scope.Description = description;
return Task.FromResult(0);
}
/// <summary>
/// Sets the name associated with a scope.
/// </summary>
/// <param name="scope">The scope.</param>
/// <param name="name">The name associated with the authorization.</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 SetNameAsync([NotNull] TScope scope, [CanBeNull] string name, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
scope.Name = name;
return Task.FromResult(0);
}
/// <summary>
/// Updates an existing scope.
/// </summary>

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

@ -64,16 +64,6 @@ namespace OpenIddict.Core
/// </returns>
public abstract Task<TToken> CreateAsync([NotNull] TToken token, CancellationToken cancellationToken);
/// <summary>
/// Creates a new token.
/// </summary>
/// <param name="descriptor">The token descriptor.</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.
/// </returns>
public abstract Task<TToken> CreateAsync([NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken);
/// <summary>
/// Removes a token.
/// </summary>
@ -167,7 +157,7 @@ namespace OpenIddict.Core
}
/// <summary>
/// Retrieves an token using its unique identifier.
/// Retrieves a token using its unique identifier.
/// </summary>
/// <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>
@ -253,12 +243,15 @@ namespace OpenIddict.Core
return ConvertIdentifierToString(token.Application.Id);
}
var key = await GetAsync(tokens =>
from element in tokens
where element.Id.Equals(token.Id)
select element.Application.Id, cancellationToken);
IQueryable<TKey> Query(IQueryable<TToken> tokens)
{
return from element in tokens
where element.Id.Equals(token.Id)
where element.Application != null
select element.Application.Id;
}
return ConvertIdentifierToString(key);
return ConvertIdentifierToString(await GetAsync(Query, cancellationToken));
}
/// <summary>
@ -282,12 +275,15 @@ namespace OpenIddict.Core
return ConvertIdentifierToString(token.Authorization.Id);
}
var key = await GetAsync(tokens =>
from element in tokens
where element.Id.Equals(token.Id)
select element.Authorization.Id, cancellationToken);
IQueryable<TKey> Query(IQueryable<TToken> tokens)
{
return from element in tokens
where element.Id.Equals(token.Id)
where element.Authorization != null
select element.Authorization.Id;
}
return ConvertIdentifierToString(key);
return ConvertIdentifierToString(await GetAsync(Query, cancellationToken));
}
/// <summary>
@ -442,6 +438,16 @@ namespace OpenIddict.Core
return Task.FromResult(token.Type);
}
/// <summary>
/// Instantiates a new token.
/// </summary>
/// <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 instantiated token, that can be persisted in the database.
/// </returns>
public virtual Task<TToken> InstantiateAsync(CancellationToken cancellationToken) => Task.FromResult(new TToken());
/// <summary>
/// Executes the specified query and returns all the corresponding elements.
/// </summary>
@ -527,7 +533,7 @@ namespace OpenIddict.Core
/// Sets the authorization identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="identifier">The unique identifier associated with the authorization.</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.
@ -538,18 +544,61 @@ namespace OpenIddict.Core
/// Sets the application identifier associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="identifier">The unique identifier associated with the client application.</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.
/// </returns>
public abstract Task SetApplicationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken);
/// <summary>
/// Sets the ciphertext associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="ciphertext">The ciphertext 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 SetCiphertextAsync([NotNull] TToken token, [CanBeNull] string ciphertext, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
token.Ciphertext = ciphertext;
return Task.FromResult(0);
}
/// <summary>
/// Sets the creation date associated with a token.
/// </summary>
/// <param name="token">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 SetCreationDateAsync([NotNull] TToken token,
[CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
token.CreationDate = date;
return Task.FromResult(0);
}
/// <summary>
/// Sets the expiration date associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="date">The date on which the token will no longer be considered valid.</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.
@ -567,6 +616,27 @@ namespace OpenIddict.Core
return Task.FromResult(0);
}
/// <summary>
/// Sets the hash associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="hash">The hash 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)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
token.Hash = hash;
return Task.FromResult(0);
}
/// <summary>
/// Sets the status associated with a token.
/// </summary>
@ -576,18 +646,75 @@ namespace OpenIddict.Core
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns>
public virtual Task SetStatusAsync([NotNull] TToken token, [NotNull] string status, CancellationToken cancellationToken)
public virtual Task SetStatusAsync([NotNull] TToken token, [CanBeNull] string status, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
if (string.IsNullOrEmpty(status))
{
throw new ArgumentException("The status cannot be null or empty.", nameof(status));
}
token.Status = status;
return Task.FromResult(0);
}
/// <summary>
/// Sets the subject associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="subject">The subject 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 SetSubjectAsync([NotNull] TToken token, [CanBeNull] string subject, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
if (string.IsNullOrEmpty(subject))
{
throw new ArgumentException("The subject cannot be null or empty.", nameof(subject));
}
token.Subject = subject;
return Task.FromResult(0);
}
/// <summary>
/// Sets the token type associated with a token.
/// </summary>
/// <param name="token">The token.</param>
/// <param name="type">The token type 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 SetTokenTypeAsync([NotNull] TToken token, [CanBeNull] string type, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
if (string.IsNullOrEmpty(type))
{
throw new ArgumentException("The token type cannot be null or empty.", nameof(type));
}
token.Type = type;
return Task.FromResult(0);
}
/// <summary>
/// Updates an existing token.
/// </summary>

6
src/OpenIddict.EntityFramework/OpenIddictExtensions.cs

@ -208,8 +208,7 @@ namespace Microsoft.Extensions.DependencyInjection
builder.Entity<TApplication>()
.HasMany(application => application.Tokens)
.WithOptional(token => token.Application)
.Map(association => association.MapKey("ApplicationId"))
.WillCascadeOnDelete();
.Map(association => association.MapKey("ApplicationId"));
builder.Entity<TApplication>()
.ToTable("OpenIddictApplications");
@ -237,8 +236,7 @@ namespace Microsoft.Extensions.DependencyInjection
builder.Entity<TAuthorization>()
.HasMany(application => application.Tokens)
.WithOptional(token => token.Authorization)
.Map(association => association.MapKey("AuthorizationId"))
.WillCascadeOnDelete();
.Map(association => association.MapKey("AuthorizationId"));
builder.Entity<TAuthorization>()
.ToTable("OpenIddictAuthorizations");

64
src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs

@ -134,27 +134,6 @@ namespace OpenIddict.EntityFramework
return application;
}
/// <summary>
/// Creates a new application.
/// </summary>
/// <param name="descriptor">The application descriptor.</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 application.
/// </returns>
public override async Task<TApplication> CreateAsync([NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken)
{
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
var application = new TApplication();
await BindAsync(application, descriptor, cancellationToken);
return await CreateAsync(application, cancellationToken);
}
/// <summary>
/// Removes an existing application.
/// </summary>
@ -291,48 +270,5 @@ namespace OpenIddict.EntityFramework
return Context.SaveChangesAsync(cancellationToken);
}
/// <summary>
/// Sets the application properties based on the specified descriptor.
/// </summary>
/// <param name="application">The application to update.</param>
/// <param name="descriptor">The application descriptor.</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>
protected virtual Task BindAsync([NotNull] TApplication application, [NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
application.ClientId = descriptor.ClientId;
application.ClientSecret = descriptor.ClientSecret;
application.DisplayName = descriptor.DisplayName;
application.Type = descriptor.Type;
if (descriptor.PostLogoutRedirectUris.Count != 0)
{
application.PostLogoutRedirectUris = string.Join(
OpenIddictConstants.Separators.Space,
descriptor.PostLogoutRedirectUris.Select(uri => uri.OriginalString));
}
if (descriptor.RedirectUris.Count != 0)
{
application.RedirectUris = string.Join(
OpenIddictConstants.Separators.Space,
descriptor.RedirectUris.Select(uri => uri.OriginalString));
}
return Task.FromResult(0);
}
}
}

121
src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs

@ -134,27 +134,6 @@ namespace OpenIddict.EntityFramework
return authorization;
}
/// <summary>
/// Creates a new authorization.
/// </summary>
/// <param name="descriptor">The authorization descriptor.</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 authorization.
/// </returns>
public override async Task<TAuthorization> CreateAsync([NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken)
{
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
var authorization = new TAuthorization();
await BindAsync(authorization, descriptor, cancellationToken);
return await CreateAsync(authorization, cancellationToken);
}
/// <summary>
/// Removes an existing authorization.
/// </summary>
@ -204,7 +183,17 @@ namespace OpenIddict.EntityFramework
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
return Authorizations.FindAsync(cancellationToken, ConvertIdentifierFromString(identifier));
var authorization = (from entry in Context.ChangeTracker.Entries<TAuthorization>()
where entry.Entity != null
where entry.Entity.Id.Equals(ConvertIdentifierFromString(identifier))
select entry.Entity).FirstOrDefault();
if (authorization != null)
{
return Task.FromResult(authorization);
}
return base.FindByIdAsync(identifier, cancellationToken);
}
/// <summary>
@ -223,17 +212,21 @@ namespace OpenIddict.EntityFramework
throw new ArgumentNullException(nameof(authorization));
}
// If the application is not attached to the authorization instance (which is expected
// if the token was retrieved using the default FindBy*Async APIs as they don't
// eagerly load the application from the database), try to load it manually.
// If the application is not attached to the authorization, try to load it manually.
if (authorization.Application == null)
{
var reference = Context.Entry(authorization).Reference(entry => entry.Application);
if (reference.EntityEntry.State == EntityState.Detached)
{
return null;
}
await reference.LoadAsync(cancellationToken);
}
if (authorization.Application == null)
{
return ConvertIdentifierToString(
await Context.Entry(authorization)
.Reference(entry => entry.Application)
.Query()
.Select(application => application.Id)
.FirstOrDefaultAsync());
return null;
}
return ConvertIdentifierToString(authorization.Application.Id);
@ -256,7 +249,7 @@ namespace OpenIddict.EntityFramework
throw new ArgumentNullException(nameof(query));
}
return query(Authorizations).FirstOrDefaultAsync(cancellationToken);
return query(Authorizations.Include(authorization => authorization.Application)).FirstOrDefaultAsync(cancellationToken);
}
/// <summary>
@ -276,7 +269,8 @@ namespace OpenIddict.EntityFramework
throw new ArgumentNullException(nameof(query));
}
return ImmutableArray.CreateRange(await query(Authorizations).ToListAsync(cancellationToken));
return ImmutableArray.CreateRange(await query(
Authorizations.Include(authorization => authorization.Application)).ToListAsync(cancellationToken));
}
/// <summary>
@ -308,15 +302,19 @@ namespace OpenIddict.EntityFramework
else
{
var key = await GetIdAsync(authorization, cancellationToken);
// Try to retrieve the application associated with the authorization.
// If none can be found, assume that no application is attached.
var application = await Applications.FirstOrDefaultAsync(element => element.Authorizations.Any(t => t.Id.Equals(key)));
if (application != null)
// If the application is not attached to the authorization, try to load it manually.
if (authorization.Application == null)
{
application.Authorizations.Remove(authorization);
var reference = Context.Entry(authorization).Reference(entry => entry.Application);
if (reference.EntityEntry.State == EntityState.Detached)
{
return;
}
await reference.LoadAsync(cancellationToken);
}
authorization.Application = null;
}
}
@ -345,48 +343,5 @@ namespace OpenIddict.EntityFramework
return Context.SaveChangesAsync(cancellationToken);
}
/// <summary>
/// Sets the authorization properties based on the specified descriptor.
/// </summary>
/// <param name="authorization">The authorization to update.</param>
/// <param name="descriptor">The authorization descriptor.</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>
protected virtual async Task BindAsync([NotNull] TAuthorization authorization, [NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
authorization.Status = descriptor.Status;
authorization.Subject = descriptor.Subject;
authorization.Type = descriptor.Type;
if (descriptor.Scopes.Count != 0)
{
authorization.Scopes = string.Join(OpenIddictConstants.Separators.Space, descriptor.Scopes);
}
// Bind the authorization to the specified application, if applicable.
if (!string.IsNullOrEmpty(descriptor.ApplicationId))
{
var application = await Applications.FindAsync(cancellationToken, ConvertIdentifierFromString(descriptor.ApplicationId));
if (application == null)
{
throw new InvalidOperationException("The application associated with the authorization cannot be found.");
}
authorization.Application = application;
}
}
}
}

43
src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs

@ -114,22 +114,6 @@ namespace OpenIddict.EntityFramework
return scope;
}
/// <summary>
/// Creates a new scope.
/// </summary>
/// <param name="descriptor">The scope descriptor.</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 scope.
/// </returns>
public override async Task<TScope> CreateAsync([NotNull] OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken)
{
var scope = new TScope();
await BindAsync(scope, descriptor, cancellationToken);
return await CreateAsync(scope, cancellationToken);
}
/// <summary>
/// Removes an existing scope.
/// </summary>
@ -215,32 +199,5 @@ namespace OpenIddict.EntityFramework
return Context.SaveChangesAsync(cancellationToken);
}
/// <summary>
/// Sets the scope properties based on the specified descriptor.
/// </summary>
/// <param name="scope">The scope to update.</param>
/// <param name="descriptor">The scope descriptor.</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>
protected virtual Task BindAsync([NotNull] TScope scope, [NotNull] OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
scope.Description = descriptor.Description;
scope.Name = descriptor.Name;
return Task.FromResult(0);
}
}
}

177
src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs

@ -133,27 +133,6 @@ namespace OpenIddict.EntityFramework
return token;
}
/// <summary>
/// Creates a new token.
/// </summary>
/// <param name="descriptor">The token descriptor.</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.
/// </returns>
public override async Task<TToken> CreateAsync([NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken)
{
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
var token = new TToken();
await BindAsync(token, descriptor, cancellationToken);
return await CreateAsync(token, cancellationToken);
}
/// <summary>
/// Removes a token.
/// </summary>
@ -173,7 +152,7 @@ namespace OpenIddict.EntityFramework
}
/// <summary>
/// Retrieves an token using its unique identifier.
/// Retrieves a token using its unique identifier.
/// </summary>
/// <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>
@ -188,7 +167,17 @@ namespace OpenIddict.EntityFramework
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
return Tokens.FindAsync(cancellationToken, ConvertIdentifierFromString(identifier));
var token = (from entry in Context.ChangeTracker.Entries<TToken>()
where entry.Entity != null
where entry.Entity.Id.Equals(ConvertIdentifierFromString(identifier))
select entry.Entity).FirstOrDefault();
if (token != null)
{
return Task.FromResult(token);
}
return base.FindByIdAsync(identifier, cancellationToken);
}
/// <summary>
@ -207,17 +196,21 @@ namespace OpenIddict.EntityFramework
throw new ArgumentNullException(nameof(token));
}
// If the application is not attached to the token instance (which is expected
// if the token was retrieved using the default FindBy*Async APIs as they don't
// eagerly load the application from the database), try to load it manually.
// If the application is not attached to the token, try to load it manually.
if (token.Application == null)
{
return ConvertIdentifierToString(
await Context.Entry(token)
.Reference(entry => entry.Application)
.Query()
.Select(application => application.Id)
.FirstOrDefaultAsync());
var reference = Context.Entry(token).Reference(entry => entry.Application);
if (reference.EntityEntry.State == EntityState.Detached)
{
return null;
}
await reference.LoadAsync(cancellationToken);
}
if (token.Application == null)
{
return null;
}
return ConvertIdentifierToString(token.Application.Id);
@ -240,7 +233,9 @@ namespace OpenIddict.EntityFramework
throw new ArgumentNullException(nameof(query));
}
return query(Tokens).FirstOrDefaultAsync(cancellationToken);
return query(
Tokens.Include(token => token.Application)
.Include(token => token.Authorization)).FirstOrDefaultAsync(cancellationToken);
}
/// <summary>
@ -259,17 +254,21 @@ namespace OpenIddict.EntityFramework
throw new ArgumentNullException(nameof(token));
}
// If the authorization is not attached to the token instance (which is expected
// if the token was retrieved using the default FindBy*Async APIs as they don't
// eagerly load the authorization from the database), try to load it manually.
// If the authorization is not attached to the token, try to load it manually.
if (token.Authorization == null)
{
var reference = Context.Entry(token).Reference(entry => entry.Authorization);
if (reference.EntityEntry.State == EntityState.Detached)
{
return null;
}
await reference.LoadAsync(cancellationToken);
}
if (token.Authorization == null)
{
return ConvertIdentifierToString(
await Context.Entry(token)
.Reference(entry => entry.Authorization)
.Query()
.Select(authorization => authorization.Id)
.FirstOrDefaultAsync());
return null;
}
return ConvertIdentifierToString(token.Authorization.Id);
@ -292,7 +291,9 @@ namespace OpenIddict.EntityFramework
throw new ArgumentNullException(nameof(query));
}
return ImmutableArray.CreateRange(await query(Tokens).ToListAsync(cancellationToken));
return ImmutableArray.CreateRange(await query(
Tokens.Include(token => token.Application)
.Include(token => token.Authorization)).ToListAsync(cancellationToken));
}
/// <summary>
@ -324,15 +325,19 @@ namespace OpenIddict.EntityFramework
else
{
var key = await GetIdAsync(token, cancellationToken);
// Try to retrieve the application associated with the token.
// If none can be found, assume that no application is attached.
var application = await Applications.FirstOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key)));
if (application != null)
// If the application is not attached to the token, try to load it manually.
if (token.Application == null)
{
application.Tokens.Remove(token);
var reference = Context.Entry(token).Reference(entry => entry.Application);
if (reference.EntityEntry.State == EntityState.Detached)
{
return;
}
await reference.LoadAsync(cancellationToken);
}
token.Application = null;
}
}
@ -365,15 +370,19 @@ namespace OpenIddict.EntityFramework
else
{
var key = await GetIdAsync(token, cancellationToken);
// Try to retrieve the authorization associated with the token.
// If none can be found, assume that no authorization is attached.
var authorization = await Authorizations.FirstOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key)));
if (authorization != null)
// If the authorization is not attached to the token, try to load it manually.
if (token.Authorization == null)
{
authorization.Tokens.Remove(token);
var reference = Context.Entry(token).Reference(entry => entry.Authorization);
if (reference.EntityEntry.State == EntityState.Detached)
{
return;
}
await reference.LoadAsync(cancellationToken);
}
token.Authorization = null;
}
}
@ -402,59 +411,5 @@ namespace OpenIddict.EntityFramework
return Context.SaveChangesAsync(cancellationToken);
}
/// <summary>
/// Sets the token properties based on the specified descriptor.
/// </summary>
/// <param name="token">The token to update.</param>
/// <param name="descriptor">The token descriptor.</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>
protected virtual async Task BindAsync([NotNull] TToken token, [NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
token.Ciphertext = descriptor.Ciphertext;
token.CreationDate = descriptor.CreationDate;
token.ExpirationDate = descriptor.ExpirationDate;
token.Hash = descriptor.Hash;
token.Status = descriptor.Status;
token.Subject = descriptor.Subject;
token.Type = descriptor.Type;
// Bind the token to the specified client application, if applicable.
if (!string.IsNullOrEmpty(descriptor.ApplicationId))
{
var application = await Applications.FindAsync(cancellationToken, ConvertIdentifierFromString(descriptor.ApplicationId));
if (application == null)
{
throw new InvalidOperationException("The application associated with the token cannot be found.");
}
token.Application = application;
}
// Bind the token to the specified authorization, if applicable.
if (!string.IsNullOrEmpty(descriptor.AuthorizationId))
{
var authorization = await Authorizations.FindAsync(cancellationToken, ConvertIdentifierFromString(descriptor.AuthorizationId));
if (authorization == null)
{
throw new InvalidOperationException("The authorization associated with the token cannot be found.");
}
token.Authorization = authorization;
}
}
}
}

6
src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs

@ -243,8 +243,7 @@ namespace Microsoft.Extensions.DependencyInjection
entity.HasMany(application => application.Tokens)
.WithOne(token => token.Application)
.HasForeignKey("ApplicationId")
.IsRequired(required: false)
.OnDelete(DeleteBehavior.Cascade);
.IsRequired(required: false);
entity.ToTable("OpenIddictApplications");
});
@ -269,8 +268,7 @@ namespace Microsoft.Extensions.DependencyInjection
entity.HasMany(authorization => authorization.Tokens)
.WithOne(token => token.Authorization)
.HasForeignKey("AuthorizationId")
.IsRequired(required: false)
.OnDelete(DeleteBehavior.Cascade);
.IsRequired(required: false);
entity.ToTable("OpenIddictAuthorizations");
});

64
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs

@ -134,27 +134,6 @@ namespace OpenIddict.EntityFrameworkCore
return application;
}
/// <summary>
/// Creates a new application.
/// </summary>
/// <param name="descriptor">The application descriptor.</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 application.
/// </returns>
public override async Task<TApplication> CreateAsync([NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken)
{
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
var application = new TApplication();
await BindAsync(application, descriptor, cancellationToken);
return await CreateAsync(application, cancellationToken);
}
/// <summary>
/// Removes an existing application.
/// </summary>
@ -272,48 +251,5 @@ namespace OpenIddict.EntityFrameworkCore
return Context.SaveChangesAsync(cancellationToken);
}
/// <summary>
/// Sets the application properties based on the specified descriptor.
/// </summary>
/// <param name="application">The application to update.</param>
/// <param name="descriptor">The application descriptor.</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>
protected virtual Task BindAsync([NotNull] TApplication application, [NotNull] OpenIddictApplicationDescriptor descriptor, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
application.ClientId = descriptor.ClientId;
application.ClientSecret = descriptor.ClientSecret;
application.DisplayName = descriptor.DisplayName;
application.Type = descriptor.Type;
if (descriptor.PostLogoutRedirectUris.Count != 0)
{
application.PostLogoutRedirectUris = string.Join(
OpenIddictConstants.Separators.Space,
descriptor.PostLogoutRedirectUris.Select(uri => uri.OriginalString));
}
if (descriptor.RedirectUris.Count != 0)
{
application.RedirectUris = string.Join(
OpenIddictConstants.Separators.Space,
descriptor.RedirectUris.Select(uri => uri.OriginalString));
}
return Task.FromResult(0);
}
}
}

102
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs

@ -134,27 +134,6 @@ namespace OpenIddict.EntityFrameworkCore
return authorization;
}
/// <summary>
/// Creates a new authorization.
/// </summary>
/// <param name="descriptor">The authorization descriptor.</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 authorization.
/// </returns>
public override async Task<TAuthorization> CreateAsync([NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken)
{
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
var authorization = new TAuthorization();
await BindAsync(authorization, descriptor, cancellationToken);
return await CreateAsync(authorization, cancellationToken);
}
/// <summary>
/// Removes an existing authorization.
/// </summary>
@ -220,7 +199,7 @@ namespace OpenIddict.EntityFrameworkCore
{
var key = ConvertIdentifierFromString(client);
return from authorization in authorizations
return from authorization in authorizations.Include(authorization => authorization.Application)
where authorization.Subject == subject
join application in applications on authorization.Application.Id equals application.Id
where application.Id.Equals(key)
@ -230,6 +209,35 @@ namespace OpenIddict.EntityFrameworkCore
return ImmutableArray.CreateRange(await Query(Authorizations, Applications).ToListAsync(cancellationToken));
}
/// <summary>
/// Retrieves an authorization using its unique identifier.
/// </summary>
/// <param name="identifier">The unique identifier associated with the authorization.</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 authorization corresponding to the identifier.
/// </returns>
public override Task<TAuthorization> FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(identifier))
{
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
var authorization = (from entry in Context.ChangeTracker.Entries<TAuthorization>()
where entry.Entity != null
where entry.Entity.Id.Equals(ConvertIdentifierFromString(identifier))
select entry.Entity).FirstOrDefault();
if (authorization != null)
{
return Task.FromResult(authorization);
}
return base.FindByIdAsync(identifier, cancellationToken);
}
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
@ -247,7 +255,7 @@ namespace OpenIddict.EntityFrameworkCore
throw new ArgumentNullException(nameof(query));
}
return query(Authorizations).FirstOrDefaultAsync(cancellationToken);
return query(Authorizations.Include(authorization => authorization.Application)).FirstOrDefaultAsync(cancellationToken);
}
/// <summary>
@ -267,7 +275,8 @@ namespace OpenIddict.EntityFrameworkCore
throw new ArgumentNullException(nameof(query));
}
return ImmutableArray.CreateRange(await query(Authorizations).ToListAsync(cancellationToken));
return ImmutableArray.CreateRange(await query(
Authorizations.Include(authorization => authorization.Application)).ToListAsync(cancellationToken));
}
/// <summary>
@ -338,50 +347,5 @@ namespace OpenIddict.EntityFrameworkCore
return Context.SaveChangesAsync(cancellationToken);
}
/// <summary>
/// Sets the authorization properties based on the specified descriptor.
/// </summary>
/// <param name="authorization">The authorization to update.</param>
/// <param name="descriptor">The authorization descriptor.</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>
protected virtual async Task BindAsync([NotNull] TAuthorization authorization, [NotNull] OpenIddictAuthorizationDescriptor descriptor, CancellationToken cancellationToken)
{
if (authorization == null)
{
throw new ArgumentNullException(nameof(authorization));
}
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
authorization.Status = descriptor.Status;
authorization.Subject = descriptor.Subject;
authorization.Type = descriptor.Type;
if (descriptor.Scopes.Count != 0)
{
authorization.Scopes = string.Join(OpenIddictConstants.Separators.Space, descriptor.Scopes);
}
// Bind the authorization to the specified application, if applicable.
if (!string.IsNullOrEmpty(descriptor.ApplicationId))
{
var key = ConvertIdentifierFromString(descriptor.ApplicationId);
var application = await Applications.SingleOrDefaultAsync(entity => entity.Id.Equals(key));
if (application == null)
{
throw new InvalidOperationException("The application associated with the authorization cannot be found.");
}
authorization.Application = application;
}
}
}
}

43
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs

@ -114,22 +114,6 @@ namespace OpenIddict.EntityFrameworkCore
return scope;
}
/// <summary>
/// Creates a new scope.
/// </summary>
/// <param name="descriptor">The scope descriptor.</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 scope.
/// </returns>
public override async Task<TScope> CreateAsync([NotNull] OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken)
{
var scope = new TScope();
await BindAsync(scope, descriptor, cancellationToken);
return await CreateAsync(scope, cancellationToken);
}
/// <summary>
/// Removes an existing scope.
/// </summary>
@ -215,32 +199,5 @@ namespace OpenIddict.EntityFrameworkCore
return Context.SaveChangesAsync(cancellationToken);
}
/// <summary>
/// Sets the scope properties based on the specified descriptor.
/// </summary>
/// <param name="scope">The scope to update.</param>
/// <param name="descriptor">The scope descriptor.</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>
protected virtual Task BindAsync([NotNull] TScope scope, [NotNull] OpenIddictScopeDescriptor descriptor, CancellationToken cancellationToken)
{
if (scope == null)
{
throw new ArgumentNullException(nameof(scope));
}
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
scope.Description = descriptor.Description;
scope.Name = descriptor.Name;
return Task.FromResult(0);
}
}
}

120
src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs

@ -133,27 +133,6 @@ namespace OpenIddict.EntityFrameworkCore
return token;
}
/// <summary>
/// Creates a new token.
/// </summary>
/// <param name="descriptor">The token descriptor.</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.
/// </returns>
public override async Task<TToken> CreateAsync([NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken)
{
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
var token = new TToken();
await BindAsync(token, descriptor, cancellationToken);
return await CreateAsync(token, cancellationToken);
}
/// <summary>
/// Removes a token.
/// </summary>
@ -197,7 +176,7 @@ namespace OpenIddict.EntityFrameworkCore
{
var key = ConvertIdentifierFromString(identifier);
return from token in tokens
return from token in tokens.Include(token => token.Application).Include(token => token.Authorization)
join application in applications on token.Application.Id equals application.Id
where application.Id.Equals(key)
select token;
@ -231,7 +210,7 @@ namespace OpenIddict.EntityFrameworkCore
{
var key = ConvertIdentifierFromString(identifier);
return from token in tokens
return from token in tokens.Include(token => token.Application).Include(token => token.Authorization)
join authorization in authorizations on token.Authorization.Id equals authorization.Id
where authorization.Id.Equals(key)
select token;
@ -240,6 +219,35 @@ namespace OpenIddict.EntityFrameworkCore
return ImmutableArray.CreateRange(await Query(Authorizations, Tokens).ToListAsync(cancellationToken));
}
/// <summary>
/// Retrieves a token using its unique identifier.
/// </summary>
/// <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 token corresponding to the unique identifier.
/// </returns>
public override Task<TToken> FindByIdAsync([NotNull] string identifier, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(identifier))
{
throw new ArgumentException("The identifier cannot be null or empty.", nameof(identifier));
}
var token = (from entry in Context.ChangeTracker.Entries<TToken>()
where entry.Entity != null
where entry.Entity.Id.Equals(ConvertIdentifierFromString(identifier))
select entry.Entity).FirstOrDefault();
if (token != null)
{
return Task.FromResult(token);
}
return base.FindByIdAsync(identifier, cancellationToken);
}
/// <summary>
/// Executes the specified query and returns the first element.
/// </summary>
@ -257,7 +265,9 @@ namespace OpenIddict.EntityFrameworkCore
throw new ArgumentNullException(nameof(query));
}
return query(Tokens).FirstOrDefaultAsync(cancellationToken);
return query(
Tokens.Include(token => token.Application)
.Include(token => token.Authorization)).FirstOrDefaultAsync(cancellationToken);
}
/// <summary>
@ -277,7 +287,9 @@ namespace OpenIddict.EntityFrameworkCore
throw new ArgumentNullException(nameof(query));
}
return ImmutableArray.CreateRange(await query(Tokens).ToListAsync(cancellationToken));
return ImmutableArray.CreateRange(await query(
Tokens.Include(token => token.Application)
.Include(token => token.Authorization)).ToListAsync(cancellationToken));
}
/// <summary>
@ -391,63 +403,5 @@ namespace OpenIddict.EntityFrameworkCore
return Context.SaveChangesAsync(cancellationToken);
}
/// <summary>
/// Sets the token properties based on the specified descriptor.
/// </summary>
/// <param name="token">The token to update.</param>
/// <param name="descriptor">The token descriptor.</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>
protected virtual async Task BindAsync([NotNull] TToken token, [NotNull] OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
if (descriptor == null)
{
throw new ArgumentNullException(nameof(descriptor));
}
token.Ciphertext = descriptor.Ciphertext;
token.CreationDate = descriptor.CreationDate;
token.ExpirationDate = descriptor.ExpirationDate;
token.Hash = descriptor.Hash;
token.Status = descriptor.Status;
token.Subject = descriptor.Subject;
token.Type = descriptor.Type;
// Bind the token to the specified client application, if applicable.
if (!string.IsNullOrEmpty(descriptor.ApplicationId))
{
var key = ConvertIdentifierFromString(descriptor.ApplicationId);
var application = await Applications.SingleOrDefaultAsync(entity => entity.Id.Equals(key));
if (application == null)
{
throw new InvalidOperationException("The application associated with the token cannot be found.");
}
token.Application = application;
}
// Bind the token to the specified authorization, if applicable.
if (!string.IsNullOrEmpty(descriptor.AuthorizationId))
{
var key = ConvertIdentifierFromString(descriptor.AuthorizationId);
var authorization = await Authorizations.SingleOrDefaultAsync(entity => entity.Id.Equals(key));
if (authorization == null)
{
throw new InvalidOperationException("The authorization associated with the token cannot be found.");
}
token.Authorization = authorization;
}
}
}
}
Loading…
Cancel
Save