Browse Source

Rework the applications/authorizations/scopes/tokens stores methods

pull/516/head
Kévin Chalet 9 years ago
parent
commit
07177df0d3
  1. 234
      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. 118
      src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs
  11. 100
      src/OpenIddict.Core/Stores/OpenIddictScopeStore.cs
  12. 151
      src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs
  13. 6
      src/OpenIddict.EntityFramework/OpenIddictExtensions.cs
  14. 64
      src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs
  15. 64
      src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs
  16. 43
      src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs
  17. 77
      src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs
  18. 6
      src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs
  19. 64
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs
  20. 66
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
  21. 43
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs
  22. 79
      src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs

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

@ -129,6 +129,7 @@ namespace OpenIddict.Core
"a confidential or hybrid application."); "a confidential or hybrid application.");
} }
// If a client secret was provided, obfuscate it.
if (!string.IsNullOrEmpty(secret)) if (!string.IsNullOrEmpty(secret))
{ {
secret = await ObfuscateClientSecretAsync(secret, cancellationToken); secret = await ObfuscateClientSecretAsync(secret, cancellationToken);
@ -151,7 +152,7 @@ namespace OpenIddict.Core
} }
/// <summary> /// <summary>
/// Creates a new application. /// Creates a new application based on the specified descriptor.
/// Note: the default implementation automatically hashes the client /// Note: the default implementation automatically hashes the client
/// secret before storing it in the database, for security reasons. /// secret before storing it in the database, for security reasons.
/// </summary> /// </summary>
@ -168,43 +169,22 @@ namespace OpenIddict.Core
throw new ArgumentNullException(nameof(descriptor)); throw new ArgumentNullException(nameof(descriptor));
} }
// If no client type was specified, assume it's a var application = await Store.InstantiateAsync(cancellationToken);
// public application if no secret was provided. if (application == null)
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))
{ {
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) return await CreateAsync(application, cancellationToken);
{
Logger.LogError(exception, "An exception occurred while trying to create a new application.");
throw;
}
} }
/// <summary> /// <summary>
@ -584,6 +564,83 @@ namespace OpenIddict.Core
await UpdateAsync(application, cancellationToken); 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> /// <summary>
/// Validates the client_secret associated with an application. /// Validates the client_secret associated with an application.
/// </summary> /// </summary>
@ -639,8 +696,7 @@ namespace OpenIddict.Core
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation, whose result /// 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 a boolean indicating whether the post_logout_redirect_uri was valid.
/// </returns> /// </returns>
public virtual async Task<bool> ValidatePostLogoutRedirectUriAsync( public virtual async Task<bool> ValidatePostLogoutRedirectUriAsync([NotNull] string address, CancellationToken cancellationToken)
[NotNull] string address, CancellationToken cancellationToken)
{ {
if (string.IsNullOrEmpty(address)) if (string.IsNullOrEmpty(address))
{ {
@ -707,120 +763,96 @@ namespace OpenIddict.Core
} }
/// <summary> /// <summary>
/// Validates the application to ensure it's in a consistent state. /// Populates the application using the specified descriptor.
/// </summary> /// </summary>
/// <param name="application">The application.</param> /// <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> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </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 if (application == null)
{
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))
{ {
// Ensure the address is not null or empty. throw new ArgumentNullException(nameof(application));
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)) if (descriptor == null)
{ {
// Ensure the address is not null or empty. throw new ArgumentNullException(nameof(descriptor));
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 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> /// <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> /// </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> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </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)) var type = await Store.GetClientTypeAsync(application, cancellationToken);
{ if (string.IsNullOrEmpty(type))
throw new ArgumentException("The client identifier cannot be null or empty.", nameof(descriptor));
}
if (string.IsNullOrEmpty(descriptor.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. // Ensure the application type is supported by the manager.
if (!string.Equals(descriptor.Type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase) && if (!string.Equals(type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(descriptor.Type, OpenIddictConstants.ClientTypes.Hybrid, StringComparison.OrdinalIgnoreCase) && !string.Equals(type, OpenIddictConstants.ClientTypes.Hybrid, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(descriptor.Type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) !string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
{ {
throw new ArgumentException("Only 'confidential', 'hybrid' or 'public' applications are " + 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. // Ensure a client secret was specified if the client is a confidential application.
if (string.IsNullOrEmpty(descriptor.ClientSecret) && var secret = await Store.GetClientSecretAsync(application, cancellationToken);
string.Equals(descriptor.Type, OpenIddictConstants.ClientTypes.Confidential, StringComparison.OrdinalIgnoreCase)) 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. // Ensure no client secret was specified if the client is a public application.
else if (!string.IsNullOrEmpty(descriptor.ClientSecret) && else if (!string.IsNullOrEmpty(secret) &&
string.Equals(descriptor.Type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase)) 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. // 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. // 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. // Ensure the address is not null or empty.
if (uri == null) 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. // Ensure the address is a valid absolute URL.
if (!uri.IsAbsoluteUri || !uri.IsWellFormedOriginalString()) if (!Uri.TryCreate(address, UriKind.Absolute, out Uri uri) || !uri.IsWellFormedOriginalString())
{ {
throw new ArgumentException("Callback URLs must be valid absolute URLs."); throw new ArgumentException("Callback URLs must be valid absolute URLs.");
} }
@ -831,8 +863,6 @@ namespace OpenIddict.Core
throw new ArgumentException("Callback URLs cannot contain a fragment."); throw new ArgumentException("Callback URLs cannot contain a fragment.");
} }
} }
return Task.CompletedTask;
} }
/// <summary> /// <summary>

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

@ -108,7 +108,7 @@ namespace OpenIddict.Core
} }
/// <summary> /// <summary>
/// Creates a new authorization. /// Creates a new authorization based on the specified descriptor.
/// </summary> /// </summary>
/// <param name="descriptor">The authorization descriptor.</param> /// <param name="descriptor">The authorization descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</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)); throw new ArgumentNullException(nameof(descriptor));
} }
// If no type was explicitly specified, assume that var authorization = await Store.InstantiateAsync(cancellationToken);
// the authorization is a permanent authorization. if (authorization == null)
if (string.IsNullOrEmpty(descriptor.Type))
{
descriptor.Type = OpenIddictConstants.AuthorizationTypes.Permanent;
}
await ValidateAsync(descriptor, cancellationToken);
try
{ {
return await Store.CreateAsync(descriptor, cancellationToken); throw new InvalidOperationException("An error occurred while trying to create a new authorization.");
} }
catch (Exception exception) await PopulateAsync(authorization, descriptor, cancellationToken);
{ return await CreateAsync(authorization, cancellationToken);
Logger.LogError(exception, "An exception occurred while trying to create a new authorization.");
throw;
}
} }
/// <summary> /// <summary>
@ -462,81 +450,119 @@ namespace OpenIddict.Core
} }
/// <summary> /// <summary>
/// Validates the authorization to ensure it's in a consistent state. /// Updates an existing authorization.
/// </summary> /// </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> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </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 var descriptor = new OpenIddictAuthorizationDescriptor
{ {
ApplicationId = await Store.GetApplicationIdAsync(authorization, cancellationToken),
Status = await Store.GetStatusAsync(authorization, cancellationToken), Status = await Store.GetStatusAsync(authorization, cancellationToken),
Subject = await Store.GetSubjectAsync(authorization, cancellationToken), Subject = await Store.GetSubjectAsync(authorization, cancellationToken),
Type = await Store.GetTypeAsync(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> /// <summary>
/// Validates the authorization descriptor to ensure it's in a consistent state. /// Populates the authorization using the specified descriptor.
/// </summary> /// </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> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </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) if (descriptor == null)
{ {
throw new ArgumentNullException(nameof(descriptor)); 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) && if (!string.Equals(type, OpenIddictConstants.AuthorizationTypes.AdHoc, StringComparison.OrdinalIgnoreCase) &&
!string.Equals(descriptor.Type, OpenIddictConstants.AuthorizationTypes.Permanent, 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. // 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)) 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)) 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.CompletedTask;
} }
} }
} }

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

@ -100,7 +100,7 @@ namespace OpenIddict.Core
} }
/// <summary> /// <summary>
/// Creates a new scope. /// Creates a new scope based on the specified descriptor.
/// </summary> /// </summary>
/// <param name="descriptor">The scope descriptor.</param> /// <param name="descriptor">The scope descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</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)); 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) await PopulateAsync(scope, descriptor, cancellationToken);
{ return await CreateAsync(scope, cancellationToken);
Logger.LogError(exception, "An exception occurred while trying to create a new scope.");
throw;
}
} }
/// <summary> /// <summary>
@ -237,5 +234,59 @@ namespace OpenIddict.Core
throw; 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> /// <summary>
/// Creates a new token, which is associated with a particular subject. /// Creates a new token based on the specified descriptor.
/// </summary> /// </summary>
/// <param name="descriptor">The token descriptor.</param> /// <param name="descriptor">The token descriptor.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</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)); throw new ArgumentNullException(nameof(descriptor));
} }
await ValidateAsync(descriptor, cancellationToken); var token = await Store.InstantiateAsync(cancellationToken);
if (token == null)
try
{ {
return await Store.CreateAsync(descriptor, cancellationToken); throw new InvalidOperationException("An error occurred while trying to create a new token");
} }
catch (Exception exception) await PopulateAsync(token, descriptor, cancellationToken);
{ return await CreateAsync(token, cancellationToken);
Logger.LogError(exception, "An exception occurred while trying to create a new token.");
throw;
}
} }
/// <summary> /// <summary>
@ -674,68 +669,110 @@ namespace OpenIddict.Core
} }
/// <summary> /// <summary>
/// Validates the token to ensure it's in a consistent state. /// Updates an existing token.
/// </summary> /// </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> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </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 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), Status = await Store.GetStatusAsync(token, cancellationToken),
Subject = await Store.GetSubjectAsync(token, cancellationToken), Subject = await Store.GetSubjectAsync(token, cancellationToken),
Type = await Store.GetTokenTypeAsync(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> /// <summary>
/// Validates the token descriptor to ensure it's in a consistent state. /// Populates the token using the specified descriptor.
/// </summary> /// </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> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </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) if (descriptor == null)
{ {
throw new ArgumentNullException(nameof(descriptor)); 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) && var type = await Store.GetTokenTypeAsync(token, cancellationToken);
!string.Equals(descriptor.Type, OpenIddictConstants.TokenTypes.AuthorizationCode, StringComparison.OrdinalIgnoreCase) && if (string.IsNullOrEmpty(type))
!string.Equals(descriptor.Type, OpenIddictConstants.TokenTypes.RefreshToken, StringComparison.OrdinalIgnoreCase))
{ {
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.CompletedTask; 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> /// </returns>
Task<TApplication> CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken); 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> /// <summary>
/// Removes an existing application. /// Removes an existing application.
/// </summary> /// </summary>
@ -207,6 +197,16 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
Task<ImmutableArray<string>> GetRedirectUrisAsync([NotNull] TApplication application, CancellationToken cancellationToken); 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> /// <summary>
/// Executes the specified query and returns all the corresponding elements. /// Executes the specified query and returns all the corresponding elements.
/// </summary> /// </summary>
@ -231,6 +231,17 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
Task<ImmutableArray<TResult>> ListAsync<TResult>([NotNull] Func<IQueryable<TApplication>, IQueryable<TResult>> query, CancellationToken cancellationToken); 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> /// <summary>
/// Sets the client secret associated with an application. /// Sets the client secret associated with an application.
/// Note: depending on the manager used to create the application, /// Note: depending on the manager used to create the application,
@ -253,7 +264,18 @@ namespace OpenIddict.Core
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </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> /// <summary>
/// Sets the logout callback addresses associated with an application. /// Sets the logout callback addresses associated with an application.

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

@ -51,16 +51,6 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
Task<TAuthorization> CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken); 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> /// <summary>
/// Removes an existing authorization. /// Removes an existing authorization.
/// </summary> /// </summary>
@ -129,6 +119,17 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
Task<string> GetIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken); 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> /// <summary>
/// Retrieves the status associated with an authorization. /// Retrieves the status associated with an authorization.
/// </summary> /// </summary>
@ -162,6 +163,16 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
Task<string> GetTypeAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken); 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> /// <summary>
/// Executes the specified query and returns all the corresponding elements. /// Executes the specified query and returns all the corresponding elements.
/// </summary> /// </summary>
@ -208,7 +219,20 @@ namespace OpenIddict.Core
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </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> /// <summary>
/// Sets the status associated with an authorization. /// Sets the status associated with an authorization.
@ -219,7 +243,20 @@ namespace OpenIddict.Core
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </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> /// <summary>
/// Sets the type associated with an authorization. /// Sets the type associated with an authorization.
@ -230,7 +267,8 @@ namespace OpenIddict.Core
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </returns>
Task SetTypeAsync([NotNull] TAuthorization authorization, [NotNull] string type, CancellationToken cancellationToken); Task SetTypeAsync([NotNull] TAuthorization authorization,
[CanBeNull] string type, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Updates an existing authorization. /// Updates an existing authorization.

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

@ -51,16 +51,6 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
Task<TScope> CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken); 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> /// <summary>
/// Removes an existing scope. /// Removes an existing scope.
/// </summary> /// </summary>
@ -83,6 +73,38 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
Task<TResult> GetAsync<TResult>([NotNull] Func<IQueryable<TScope>, IQueryable<TResult>> query, CancellationToken cancellationToken); 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> /// <summary>
/// Executes the specified query and returns all the corresponding elements. /// Executes the specified query and returns all the corresponding elements.
/// </summary> /// </summary>
@ -107,6 +129,28 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
Task<ImmutableArray<TResult>> ListAsync<TResult>([NotNull] Func<IQueryable<TScope>, IQueryable<TResult>> query, CancellationToken cancellationToken); 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> /// <summary>
/// Updates an existing scope. /// Updates an existing scope.
/// </summary> /// </summary>

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

@ -51,16 +51,6 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
Task<TToken> CreateAsync([NotNull] TToken token, CancellationToken cancellationToken); 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> /// <summary>
/// Removes a token. /// Removes a token.
/// </summary> /// </summary>
@ -103,7 +93,7 @@ namespace OpenIddict.Core
Task<TToken> FindByHashAsync([NotNull] string hash, CancellationToken cancellationToken); Task<TToken> FindByHashAsync([NotNull] string hash, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Retrieves an token using its unique identifier. /// Retrieves a token using its unique identifier.
/// </summary> /// </summary>
/// <param name="identifier">The unique identifier associated with the token.</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> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
@ -246,6 +236,16 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
Task<string> GetTokenTypeAsync([NotNull] TToken token, CancellationToken cancellationToken); 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> /// <summary>
/// Executes the specified query and returns all the corresponding elements. /// Executes the specified query and returns all the corresponding elements.
/// </summary> /// </summary>
@ -287,7 +287,7 @@ namespace OpenIddict.Core
/// Sets the application identifier associated with a token. /// Sets the application identifier associated with a token.
/// </summary> /// </summary>
/// <param name="token">The token.</param> /// <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> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// 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. /// Sets the authorization identifier associated with a token.
/// </summary> /// </summary>
/// <param name="token">The token.</param> /// <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> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </returns>
Task SetAuthorizationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken); 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> /// <summary>
/// Sets the expiration date associated with a token. /// Sets the expiration date associated with a token.
/// </summary> /// </summary>
/// <param name="token">The token.</param> /// <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> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </returns>
Task SetExpirationDateAsync([NotNull] TToken token, [CanBeNull] DateTimeOffset? date, CancellationToken cancellationToken); 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> /// <summary>
/// Sets the status associated with a token. /// Sets the status associated with a token.
/// </summary> /// </summary>
@ -325,7 +358,29 @@ namespace OpenIddict.Core
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </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> /// <summary>
/// Updates an existing token. /// Updates an existing token.

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

@ -64,16 +64,6 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
public abstract Task<TApplication> CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken); 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> /// <summary>
/// Removes an existing application. /// Removes an existing application.
/// </summary> /// </summary>
@ -425,6 +415,16 @@ namespace OpenIddict.Core
return Task.FromResult(ImmutableArray.Create(uris)); 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> /// <summary>
/// Executes the specified query and returns all the corresponding elements. /// Executes the specified query and returns all the corresponding elements.
/// </summary> /// </summary>
@ -469,6 +469,28 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
public abstract Task<ImmutableArray<TResult>> ListAsync<TResult>([NotNull] Func<IQueryable<TApplication>, IQueryable<TResult>> query, CancellationToken cancellationToken); 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.CompletedTask;
}
/// <summary> /// <summary>
/// Sets the client secret associated with an application. /// Sets the client secret associated with an application.
/// Note: depending on the manager used to create the application, /// Note: depending on the manager used to create the application,
@ -502,19 +524,37 @@ namespace OpenIddict.Core
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </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) if (application == null)
{ {
throw new ArgumentNullException(nameof(application)); throw new ArgumentNullException(nameof(application));
} }
if (string.IsNullOrEmpty(type)) application.Type = type;
return Task.CompletedTask;
}
/// <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.CompletedTask; return Task.CompletedTask;
} }

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

@ -64,16 +64,6 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
public abstract Task<TAuthorization> CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken); 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> /// <summary>
/// Removes an existing authorization. /// Removes an existing authorization.
/// </summary> /// </summary>
@ -209,6 +199,34 @@ namespace OpenIddict.Core
return Task.FromResult(ConvertIdentifierToString(authorization.Id)); 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> /// <summary>
/// Retrieves the status associated with an authorization. /// Retrieves the status associated with an authorization.
/// </summary> /// </summary>
@ -261,6 +279,16 @@ namespace OpenIddict.Core
return Task.FromResult(authorization.Type); 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> /// <summary>
/// Executes the specified query and returns all the corresponding elements. /// Executes the specified query and returns all the corresponding elements.
/// </summary> /// </summary>
@ -352,7 +380,47 @@ namespace OpenIddict.Core
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </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.CompletedTask;
}
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.CompletedTask;
}
/// <summary> /// <summary>
/// Sets the status associated with an authorization. /// Sets the status associated with an authorization.
@ -363,7 +431,8 @@ namespace OpenIddict.Core
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </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) if (authorization == null)
{ {
@ -375,6 +444,28 @@ namespace OpenIddict.Core
return Task.CompletedTask; return Task.CompletedTask;
} }
/// <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.CompletedTask;
}
/// <summary> /// <summary>
/// Sets the type associated with an authorization. /// Sets the type associated with an authorization.
/// </summary> /// </summary>
@ -384,7 +475,8 @@ namespace OpenIddict.Core
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </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) if (authorization == null)
{ {

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

@ -60,16 +60,6 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
public abstract Task<TScope> CreateAsync([NotNull] TScope scope, CancellationToken cancellationToken); 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> /// <summary>
/// Removes an existing scope. /// Removes an existing scope.
/// </summary> /// </summary>
@ -92,6 +82,54 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
public abstract Task<TResult> GetAsync<TResult>([NotNull] Func<IQueryable<TScope>, IQueryable<TResult>> query, CancellationToken cancellationToken); 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> /// <summary>
/// Executes the specified query and returns all the corresponding elements. /// Executes the specified query and returns all the corresponding elements.
/// </summary> /// </summary>
@ -136,6 +174,48 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
public abstract Task<ImmutableArray<TResult>> ListAsync<TResult>([NotNull] Func<IQueryable<TScope>, IQueryable<TResult>> query, CancellationToken cancellationToken); 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.CompletedTask;
}
/// <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.CompletedTask;
}
/// <summary> /// <summary>
/// Updates an existing scope. /// Updates an existing scope.
/// </summary> /// </summary>

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

@ -64,16 +64,6 @@ namespace OpenIddict.Core
/// </returns> /// </returns>
public abstract Task<TToken> CreateAsync([NotNull] TToken token, CancellationToken cancellationToken); 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> /// <summary>
/// Removes a token. /// Removes a token.
/// </summary> /// </summary>
@ -167,7 +157,7 @@ namespace OpenIddict.Core
} }
/// <summary> /// <summary>
/// Retrieves an token using its unique identifier. /// Retrieves a token using its unique identifier.
/// </summary> /// </summary>
/// <param name="identifier">The unique identifier associated with the token.</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> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
@ -442,6 +432,16 @@ namespace OpenIddict.Core
return Task.FromResult(token.Type); 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> /// <summary>
/// Executes the specified query and returns all the corresponding elements. /// Executes the specified query and returns all the corresponding elements.
/// </summary> /// </summary>
@ -527,7 +527,7 @@ namespace OpenIddict.Core
/// Sets the authorization identifier associated with a token. /// Sets the authorization identifier associated with a token.
/// </summary> /// </summary>
/// <param name="token">The token.</param> /// <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> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
@ -538,18 +538,61 @@ namespace OpenIddict.Core
/// Sets the application identifier associated with a token. /// Sets the application identifier associated with a token.
/// </summary> /// </summary>
/// <param name="token">The token.</param> /// <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> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </returns>
public abstract Task SetApplicationIdAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken); 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.CompletedTask;
}
/// <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.CompletedTask;
}
/// <summary> /// <summary>
/// Sets the expiration date associated with a token. /// Sets the expiration date associated with a token.
/// </summary> /// </summary>
/// <param name="token">The token.</param> /// <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> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
@ -567,6 +610,27 @@ namespace OpenIddict.Core
return Task.CompletedTask; return Task.CompletedTask;
} }
/// <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.CompletedTask;
}
/// <summary> /// <summary>
/// Sets the status associated with a token. /// Sets the status associated with a token.
/// </summary> /// </summary>
@ -576,18 +640,75 @@ namespace OpenIddict.Core
/// <returns> /// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation. /// A <see cref="Task"/> that can be used to monitor the asynchronous operation.
/// </returns> /// </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) if (token == null)
{ {
throw new ArgumentNullException(nameof(token)); throw new ArgumentNullException(nameof(token));
} }
if (string.IsNullOrEmpty(status))
{
throw new ArgumentException("The status cannot be null or empty.", nameof(status));
}
token.Status = status; token.Status = status;
return Task.CompletedTask; return Task.CompletedTask;
} }
/// <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.CompletedTask;
}
/// <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.CompletedTask;
}
/// <summary> /// <summary>
/// Updates an existing token. /// Updates an existing token.
/// </summary> /// </summary>

6
src/OpenIddict.EntityFramework/OpenIddictExtensions.cs

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

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

@ -134,27 +134,6 @@ namespace OpenIddict.EntityFramework
return application; 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> /// <summary>
/// Removes an existing application. /// Removes an existing application.
/// </summary> /// </summary>
@ -291,48 +270,5 @@ namespace OpenIddict.EntityFramework
return Context.SaveChangesAsync(cancellationToken); 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.CompletedTask;
}
} }
} }

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

@ -134,27 +134,6 @@ namespace OpenIddict.EntityFramework
return authorization; 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> /// <summary>
/// Removes an existing authorization. /// Removes an existing authorization.
/// </summary> /// </summary>
@ -345,48 +324,5 @@ namespace OpenIddict.EntityFramework
return Context.SaveChangesAsync(cancellationToken); 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; 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> /// <summary>
/// Removes an existing scope. /// Removes an existing scope.
/// </summary> /// </summary>
@ -215,32 +199,5 @@ namespace OpenIddict.EntityFramework
return Context.SaveChangesAsync(cancellationToken); 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.CompletedTask;
}
} }
} }

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

@ -133,27 +133,6 @@ namespace OpenIddict.EntityFramework
return token; 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> /// <summary>
/// Removes a token. /// Removes a token.
/// </summary> /// </summary>
@ -173,7 +152,7 @@ namespace OpenIddict.EntityFramework
} }
/// <summary> /// <summary>
/// Retrieves an token using its unique identifier. /// Retrieves a token using its unique identifier.
/// </summary> /// </summary>
/// <param name="identifier">The unique identifier associated with the token.</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> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
@ -402,59 +381,5 @@ namespace OpenIddict.EntityFramework
return Context.SaveChangesAsync(cancellationToken); 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

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

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

@ -134,27 +134,6 @@ namespace OpenIddict.EntityFrameworkCore
return application; 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> /// <summary>
/// Removes an existing application. /// Removes an existing application.
/// </summary> /// </summary>
@ -291,48 +270,5 @@ namespace OpenIddict.EntityFrameworkCore
return Context.SaveChangesAsync(cancellationToken); 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.CompletedTask;
}
} }
} }

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

@ -134,27 +134,6 @@ namespace OpenIddict.EntityFrameworkCore
return authorization; 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> /// <summary>
/// Removes an existing authorization. /// Removes an existing authorization.
/// </summary> /// </summary>
@ -339,7 +318,7 @@ namespace OpenIddict.EntityFrameworkCore
if (!string.IsNullOrEmpty(identifier)) if (!string.IsNullOrEmpty(identifier))
{ {
var application = await Applications.FindAsync(cancellationToken, ConvertIdentifierFromString(identifier)); var application = await Applications.FindAsync(new object[] { ConvertIdentifierFromString(identifier) }, cancellationToken);
if (application == null) if (application == null)
{ {
throw new InvalidOperationException("The application associated with the authorization cannot be found."); throw new InvalidOperationException("The application associated with the authorization cannot be found.");
@ -387,48 +366,5 @@ namespace OpenIddict.EntityFrameworkCore
return Context.SaveChangesAsync(cancellationToken); 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(new object[] { ConvertIdentifierFromString(descriptor.ApplicationId) }, cancellationToken);
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; 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> /// <summary>
/// Removes an existing scope. /// Removes an existing scope.
/// </summary> /// </summary>
@ -215,32 +199,5 @@ namespace OpenIddict.EntityFrameworkCore
return Context.SaveChangesAsync(cancellationToken); 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.CompletedTask;
}
} }
} }

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

@ -133,27 +133,6 @@ namespace OpenIddict.EntityFrameworkCore
return token; 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> /// <summary>
/// Removes a token. /// Removes a token.
/// </summary> /// </summary>
@ -241,7 +220,7 @@ namespace OpenIddict.EntityFrameworkCore
} }
/// <summary> /// <summary>
/// Retrieves an token using its unique identifier. /// Retrieves a token using its unique identifier.
/// </summary> /// </summary>
/// <param name="identifier">The unique identifier associated with the token.</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> /// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
@ -381,8 +360,6 @@ namespace OpenIddict.EntityFrameworkCore
if (!string.IsNullOrEmpty(identifier)) if (!string.IsNullOrEmpty(identifier))
{ {
var key = ConvertIdentifierFromString(identifier);
var application = await Applications.FindAsync(new object[] { ConvertIdentifierFromString(identifier) }, cancellationToken); var application = await Applications.FindAsync(new object[] { ConvertIdentifierFromString(identifier) }, cancellationToken);
if (application == null) if (application == null)
{ {
@ -472,59 +449,5 @@ namespace OpenIddict.EntityFrameworkCore
return Context.SaveChangesAsync(cancellationToken); 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(new object[] { ConvertIdentifierFromString(descriptor.ApplicationId) }, cancellationToken);
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(new object[] { ConvertIdentifierFromString(descriptor.AuthorizationId) }, cancellationToken);
if (authorization == null)
{
throw new InvalidOperationException("The authorization associated with the token cannot be found.");
}
token.Authorization = authorization;
}
}
} }
} }
Loading…
Cancel
Save