diff --git a/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs
index 7a61d0e0..824e07e8 100644
--- a/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs
+++ b/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs
@@ -44,7 +44,7 @@ namespace OpenIddict.Core {
/// A that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the application.
///
- public virtual async Task CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
+ public virtual async Task CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
@@ -73,7 +73,7 @@ namespace OpenIddict.Core {
/// A that can be used to monitor the asynchronous operation,
/// whose result returns the unique identifier associated with the application.
///
- public virtual async Task CreateAsync(
+ public virtual async Task CreateAsync(
[NotNull] TApplication application,
[NotNull] string secret, CancellationToken cancellationToken) {
if (application == null) {
@@ -196,6 +196,23 @@ namespace OpenIddict.Core {
return Store.GetDisplayNameAsync(application, cancellationToken);
}
+ ///
+ /// Retrieves the unique identifier associated with an application.
+ ///
+ /// The application.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the unique identifier associated with the application.
+ ///
+ public virtual Task GetIdAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
+ if (application == null) {
+ throw new ArgumentNullException(nameof(application));
+ }
+
+ return Store.GetIdAsync(application, cancellationToken);
+ }
+
///
/// Retrieves the token identifiers associated with an application.
///
diff --git a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
index d0a70380..c377075c 100644
--- a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
+++ b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs
@@ -4,6 +4,9 @@
* the license and the contributors participating to this project.
*/
+using System;
+using System.Threading;
+using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
@@ -29,5 +32,83 @@ namespace OpenIddict.Core {
/// Gets the store associated with the current manager.
///
protected IOpenIddictAuthorizationStore Store { get; }
+
+ ///
+ /// Creates a new authorization.
+ ///
+ /// The application to create.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public virtual Task CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) {
+ if (authorization == null) {
+ throw new ArgumentNullException(nameof(authorization));
+ }
+
+ return Store.CreateAsync(authorization, cancellationToken);
+ }
+
+ ///
+ /// Retrieves an authorization using its associated subject/client.
+ ///
+ /// The subject associated with the authorization.
+ /// The client associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the authorization corresponding to the subject/client.
+ ///
+ public virtual Task FindAsync(string subject, string client, CancellationToken cancellationToken) {
+ return Store.FindAsync(subject, client, cancellationToken);
+ }
+
+ ///
+ /// Retrieves an authorization using its unique identifier.
+ ///
+ /// The unique identifier associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the authorization corresponding to the identifier.
+ ///
+ public virtual Task FindByIdAsync(string identifier, CancellationToken cancellationToken) {
+ return Store.FindByIdAsync(identifier, cancellationToken);
+ }
+
+ ///
+ /// Retrieves the unique identifier associated with an authorization.
+ ///
+ /// The authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the unique identifier associated with the authorization.
+ ///
+ public virtual Task GetIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) {
+ if (authorization == null) {
+ throw new ArgumentNullException(nameof(authorization));
+ }
+
+ return Store.GetIdAsync(authorization, cancellationToken);
+ }
+
+ ///
+ /// Validates the authorization to ensure it's in a consistent state.
+ ///
+ /// The authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ protected virtual async Task ValidateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) {
+ if (authorization == null) {
+ throw new ArgumentNullException(nameof(authorization));
+ }
+
+ if (string.IsNullOrEmpty(await Store.GetSubjectAsync(authorization, cancellationToken))) {
+ throw new ArgumentException("The subject cannot be null or empty.");
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
index d7736a1e..95ef036a 100644
--- a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
+++ b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs
@@ -7,6 +7,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
+using AspNet.Security.OpenIdConnect.Primitives;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
@@ -34,20 +35,46 @@ namespace OpenIddict.Core {
protected IOpenIddictTokenStore Store { get; }
///
- /// Creates a new token, which is not associated with a particular user or client.
+ /// Creates a new token.
+ ///
+ /// The token.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation, whose result returns the token.
+ ///
+ public virtual async Task CreateAsync([NotNull] TToken token, CancellationToken cancellationToken) {
+ if (token == null) {
+ throw new ArgumentNullException(nameof(token));
+ }
+
+ await ValidateAsync(token, cancellationToken);
+ return await Store.CreateAsync(token, cancellationToken);
+ }
+
+ ///
+ /// Creates a new token, which is associated with a particular subject.
///
/// The token type.
+ /// The subject associated with the token.
/// The that can be used to abort the operation.
///
- /// A that can be used to monitor the asynchronous operation,
- /// whose result returns the unique identifier associated with the token.
+ /// A that can be used to monitor the asynchronous operation, whose result returns the token.
///
- public virtual Task CreateAsync(string type, CancellationToken cancellationToken) {
+ public virtual async Task CreateAsync([NotNull] string type, [NotNull] string subject, CancellationToken cancellationToken) {
if (string.IsNullOrEmpty(type)) {
throw new ArgumentException("The token type cannot be null or empty.", nameof(type));
}
- return Store.CreateAsync(type, cancellationToken);
+ if (!string.Equals(type, OpenIdConnectConstants.TokenTypeHints.AuthorizationCode, StringComparison.OrdinalIgnoreCase) &&
+ !string.Equals(type, OpenIdConnectConstants.TokenTypeHints.RefreshToken, StringComparison.OrdinalIgnoreCase)) {
+ throw new ArgumentException("The specified token type is not supported by the default token manager.");
+ }
+
+ if (string.IsNullOrEmpty(subject)) {
+ throw new ArgumentException("The subject cannot be null or empty.");
+ }
+
+ return await Store.CreateAsync(type, subject, cancellationToken);
}
///
@@ -63,18 +90,128 @@ namespace OpenIddict.Core {
return Store.FindByIdAsync(identifier, cancellationToken);
}
+ ///
+ /// Retrieves the list of tokens corresponding to the specified subject.
+ ///
+ /// The subject associated with the tokens.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the tokens corresponding to the specified subject.
+ ///
+ public virtual Task FindBySubjectAsync(string subject, CancellationToken cancellationToken) {
+ return Store.FindBySubjectAsync(subject, cancellationToken);
+ }
+
+ ///
+ /// Retrieves the unique identifier associated with a token.
+ ///
+ /// The token.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the unique identifier associated with the token.
+ ///
+ public virtual Task GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken) {
+ if (token == null) {
+ throw new ArgumentNullException(nameof(token));
+ }
+
+ return Store.GetIdAsync(token, cancellationToken);
+ }
+
///
/// Revokes a token.
///
/// The token to revoke.
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
- public virtual Task RevokeAsync(TToken token, CancellationToken cancellationToken) {
+ public virtual Task RevokeAsync([NotNull] TToken token, CancellationToken cancellationToken) {
if (token == null) {
throw new ArgumentNullException(nameof(token));
}
return Store.RevokeAsync(token, cancellationToken);
}
+
+ ///
+ /// Sets the authorization associated with a token.
+ ///
+ /// The token.
+ /// The unique identifier associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public virtual async Task SetAuthorizationAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) {
+ if (token == null) {
+ throw new ArgumentNullException(nameof(token));
+ }
+
+ await Store.SetAuthorizationAsync(token, identifier, cancellationToken);
+ await UpdateAsync(token, cancellationToken);
+ }
+
+ ///
+ /// Sets the client application associated with a token.
+ ///
+ /// The token.
+ /// The unique identifier associated with the client application.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public virtual async Task SetClientAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) {
+ if (token == null) {
+ throw new ArgumentNullException(nameof(token));
+ }
+
+ await Store.SetClientAsync(token, identifier, cancellationToken);
+ await UpdateAsync(token, cancellationToken);
+ }
+
+ ///
+ /// Updates an existing token.
+ ///
+ /// The token to update.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public virtual Task UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken) {
+ if (token == null) {
+ throw new ArgumentNullException(nameof(token));
+ }
+
+ return Store.UpdateAsync(token, cancellationToken);
+ }
+
+ ///
+ /// Validates the token to ensure it's in a consistent state.
+ ///
+ /// The token.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ protected virtual async Task ValidateAsync([NotNull] TToken token, CancellationToken cancellationToken) {
+ if (token == null) {
+ throw new ArgumentNullException(nameof(token));
+ }
+
+ var type = await Store.GetTokenTypeAsync(token, cancellationToken);
+ if (string.IsNullOrEmpty(type)) {
+ throw new ArgumentException("The token type cannot be null or empty.", nameof(token));
+ }
+
+ if (!string.Equals(type, OpenIdConnectConstants.TokenTypeHints.AuthorizationCode, StringComparison.OrdinalIgnoreCase) &&
+ !string.Equals(type, OpenIdConnectConstants.TokenTypeHints.RefreshToken, StringComparison.OrdinalIgnoreCase)) {
+ throw new ArgumentException("The specified token type is not supported by the default token manager.");
+ }
+
+ if (string.IsNullOrEmpty(await Store.GetSubjectAsync(token, cancellationToken))) {
+ throw new ArgumentException("The subject cannot be null or empty.");
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/OpenIddict.Core/OpenIddictBuilder.cs b/src/OpenIddict.Core/OpenIddictBuilder.cs
index 965402ff..ebb1ec31 100644
--- a/src/OpenIddict.Core/OpenIddictBuilder.cs
+++ b/src/OpenIddict.Core/OpenIddictBuilder.cs
@@ -8,6 +8,7 @@ using System;
using System.ComponentModel;
using JetBrains.Annotations;
using OpenIddict.Core;
+using OpenIddict.Models;
#if NETSTANDARD1_3
using System.Reflection;
@@ -34,25 +35,25 @@ namespace Microsoft.Extensions.DependencyInjection {
/// Gets or sets the type corresponding to the Application entity.
///
[EditorBrowsable(EditorBrowsableState.Never)]
- public Type ApplicationType { get; set; }
+ public Type ApplicationType { get; set; } = typeof(OpenIddictApplication);
///
/// Gets or sets the type corresponding to the Authorization entity.
///
[EditorBrowsable(EditorBrowsableState.Never)]
- public Type AuthorizationType { get; set; }
+ public Type AuthorizationType { get; set; } = typeof(OpenIddictAuthorization);
///
/// Gets or sets the type corresponding to the Scope entity.
///
[EditorBrowsable(EditorBrowsableState.Never)]
- public Type ScopeType { get; set; }
+ public Type ScopeType { get; set; } = typeof(OpenIddictScope);
///
/// Gets or sets the type corresponding to the Token entity.
///
[EditorBrowsable(EditorBrowsableState.Never)]
- public Type TokenType { get; set; }
+ public Type TokenType { get; set; } = typeof(OpenIddictToken);
///
/// Gets the services collection.
diff --git a/src/OpenIddict.Core/OpenIddictConstants.cs b/src/OpenIddict.Core/OpenIddictConstants.cs
index b6f7d0be..f8ca9d9d 100644
--- a/src/OpenIddict.Core/OpenIddictConstants.cs
+++ b/src/OpenIddict.Core/OpenIddictConstants.cs
@@ -24,6 +24,10 @@ namespace OpenIddict.Core {
public const string ExternalProvidersSupported = "external_providers_supported";
}
+ public static class Properties {
+ public const string AuthorizationId = ".authorization_id";
+ }
+
public static class Scopes {
public const string Roles = "roles";
}
diff --git a/src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs b/src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs
index f40cd297..a6954e8f 100644
--- a/src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs
+++ b/src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs
@@ -21,10 +21,9 @@ namespace OpenIddict.Core {
/// The application to create.
/// The that can be used to abort the operation.
///
- /// A that can be used to monitor the asynchronous operation,
- /// whose result returns the unique identifier associated with the application.
+ /// A that can be used to monitor the asynchronous operation, whose result returns the application.
///
- Task CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken);
+ Task CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken);
///
/// Removes an existing application.
@@ -113,6 +112,17 @@ namespace OpenIddict.Core {
///
Task GetHashedSecretAsync([NotNull] TApplication application, CancellationToken cancellationToken);
+ ///
+ /// Retrieves the unique identifier associated with an application.
+ ///
+ /// The application.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the unique identifier associated with the application.
+ ///
+ Task GetIdAsync([NotNull] TApplication application, CancellationToken cancellationToken);
+
///
/// Retrieves the logout callback address associated with an application.
///
diff --git a/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs b/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
index 9ba7a7dd..2bb1b3a8 100644
--- a/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs
@@ -4,10 +4,69 @@
* the license and the contributors participating to this project.
*/
+using System.Threading;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+
namespace OpenIddict.Core {
///
/// Provides methods allowing to manage the authorizations stored in a database.
///
/// The type of the Authorization entity.
- public interface IOpenIddictAuthorizationStore where TAuthorization : class { }
+ public interface IOpenIddictAuthorizationStore where TAuthorization : class {
+ ///
+ /// Creates a new authorization.
+ ///
+ /// The authorization to create.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation, whose result returns the authorization.
+ ///
+ Task CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken);
+
+ ///
+ /// Retrieves an authorization using its unique identifier.
+ ///
+ /// The unique identifier associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the authorization corresponding to the identifier.
+ ///
+ Task FindByIdAsync(string identifier, CancellationToken cancellationToken);
+
+ ///
+ /// Retrieves an authorization using its associated subject/client.
+ ///
+ /// The subject associated with the authorization.
+ /// The client associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the authorization corresponding to the subject/client.
+ ///
+ Task FindAsync(string subject, string client, CancellationToken cancellationToken);
+
+ ///
+ /// Retrieves the unique identifier associated with an authorization.
+ ///
+ /// The authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the unique identifier associated with the authorization.
+ ///
+ Task GetIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken);
+
+ ///
+ /// Retrieves the subject associated with an authorization.
+ ///
+ /// The authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the subject associated with the specified authorization.
+ ///
+ Task GetSubjectAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken);
+ }
}
\ No newline at end of file
diff --git a/src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs b/src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs
index 057eb55b..c2f92973 100644
--- a/src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs
+++ b/src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs
@@ -15,15 +15,25 @@ namespace OpenIddict.Core {
/// The type of the Token entity.
public interface IOpenIddictTokenStore where TToken : class {
///
- /// Creates a new token, which is not associated with a particular user or client.
+ /// Creates a new token.
+ ///
+ /// The token to create.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation, whose result returns the token.
+ ///
+ Task CreateAsync([NotNull] TToken token, CancellationToken cancellationToken);
+
+ ///
+ /// Creates a new token, which is associated with a particular subject.
///
/// The token type.
+ /// The subject associated with the token.
/// The that can be used to abort the operation.
///
- /// A that can be used to monitor the asynchronous operation,
- /// whose result returns the unique identifier associated with the token.
+ /// A that can be used to monitor the asynchronous operation, whose result returns the token.
///
- Task CreateAsync([NotNull] string type, CancellationToken cancellationToken);
+ Task CreateAsync([NotNull] string type, [NotNull] string subject, CancellationToken cancellationToken);
///
/// Retrieves an token using its unique identifier.
@@ -36,6 +46,50 @@ namespace OpenIddict.Core {
///
Task FindByIdAsync(string identifier, CancellationToken cancellationToken);
+ ///
+ /// Retrieves the list of tokens corresponding to the specified subject.
+ ///
+ /// The subject associated with the tokens.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the tokens corresponding to the specified subject.
+ ///
+ Task FindBySubjectAsync(string subject, CancellationToken cancellationToken);
+
+ ///
+ /// Retrieves the unique identifier associated with a token.
+ ///
+ /// The token.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the unique identifier associated with the token.
+ ///
+ Task GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken);
+
+ ///
+ /// Retrieves the token type associated with a token.
+ ///
+ /// The token.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the token type associated with the specified token.
+ ///
+ Task GetTokenTypeAsync([NotNull] TToken token, CancellationToken cancellationToken);
+
+ ///
+ /// Retrieves the subject associated with a token.
+ ///
+ /// The token.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the subject associated with the specified token.
+ ///
+ Task GetSubjectAsync([NotNull] TToken token, CancellationToken cancellationToken);
+
///
/// Revokes a token.
///
@@ -43,5 +97,37 @@ namespace OpenIddict.Core {
/// The that can be used to abort the operation.
/// A that can be used to monitor the asynchronous operation.
Task RevokeAsync([NotNull] TToken token, CancellationToken cancellationToken);
+
+ ///
+ /// Sets the authorization associated with a token.
+ ///
+ /// The token.
+ /// The unique identifier associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ Task SetAuthorizationAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken);
+
+ ///
+ /// Sets the client application associated with a token.
+ ///
+ /// The token.
+ /// The unique identifier associated with the client application.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ Task SetClientAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken);
+
+ ///
+ /// Updates an existing token.
+ ///
+ /// The token to update.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ Task UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken);
}
}
\ No newline at end of file
diff --git a/src/OpenIddict.Core/project.json b/src/OpenIddict.Core/project.json
index 0a482671..2e2ade8a 100644
--- a/src/OpenIddict.Core/project.json
+++ b/src/OpenIddict.Core/project.json
@@ -33,6 +33,7 @@
},
"dependencies": {
+ "AspNet.Security.OpenIdConnect.Primitives": "1.0.0-rc1-*",
"CryptoHelper": "2.0.0",
"JetBrains.Annotations": { "type": "build", "version": "10.1.4" },
"Microsoft.Extensions.DependencyInjection.Abstractions": "1.0.0",
@@ -46,7 +47,8 @@
"netstandard1.3": {
"dependencies": {
- "System.Reflection.TypeExtensions": "4.1.0"
+ "System.Reflection.TypeExtensions": "4.1.0",
+ "System.Security.Claims": "4.0.1"
},
"imports": [
diff --git a/src/OpenIddict.EntityFrameworkCore/OpenIddictCustomizer.cs b/src/OpenIddict.EntityFrameworkCore/OpenIddictCustomizer.cs
index 04dde40c..46f86ec5 100644
--- a/src/OpenIddict.EntityFrameworkCore/OpenIddictCustomizer.cs
+++ b/src/OpenIddict.EntityFrameworkCore/OpenIddictCustomizer.cs
@@ -17,10 +17,10 @@ namespace OpenIddict.EntityFrameworkCore {
/// required by the OpenIddict stack in an Entity Framework context.
///
public class OpenIddictCustomizer : ModelCustomizer
- where TApplication : OpenIddictApplication
- where TAuthorization : OpenIddictAuthorization
- where TScope : OpenIddictScope
- where TToken : OpenIddictToken
+ where TApplication : OpenIddictApplication, new()
+ where TAuthorization : OpenIddictAuthorization, new()
+ where TScope : OpenIddictScope, new()
+ where TToken : OpenIddictToken, new()
where TKey : IEquatable {
public override void Customize([NotNull] ModelBuilder builder, [NotNull] DbContext context) {
if (builder == null) {
diff --git a/src/OpenIddict.EntityFrameworkCore/OpenIddictExtension.cs b/src/OpenIddict.EntityFrameworkCore/OpenIddictExtension.cs
index 669250b5..a698efd0 100644
--- a/src/OpenIddict.EntityFrameworkCore/OpenIddictExtension.cs
+++ b/src/OpenIddict.EntityFrameworkCore/OpenIddictExtension.cs
@@ -5,18 +5,19 @@
*/
using System;
+using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using OpenIddict.Models;
namespace OpenIddict.EntityFrameworkCore {
public class OpenIddictExtension : IDbContextOptionsExtension
- where TApplication : OpenIddictApplication
- where TAuthorization : OpenIddictAuthorization
- where TScope : OpenIddictScope
- where TToken : OpenIddictToken
+ where TApplication : OpenIddictApplication, new()
+ where TAuthorization : OpenIddictAuthorization, new()
+ where TScope : OpenIddictScope, new()
+ where TToken : OpenIddictToken, new()
where TKey : IEquatable {
- public void ApplyServices(IServiceCollection services) {
+ public void ApplyServices([NotNull] IServiceCollection services) {
if (services == null) {
throw new ArgumentNullException(nameof(services));
}
diff --git a/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs b/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs
index d25d8e6e..593f7835 100644
--- a/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs
+++ b/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs
@@ -5,6 +5,7 @@
*/
using System;
+using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using JetBrains.Annotations;
@@ -18,7 +19,7 @@ using OpenIddict.Models;
namespace Microsoft.Extensions.DependencyInjection {
public static class OpenIddictExtensions {
///
- /// Registers the Entity Framework stores. Note: when using the built-in Entity Framework stores,
+ /// Registers the Entity Framework Core stores. Note: when using the Entity Framework Core stores,
/// the entities MUST be derived from the models contained in the OpenIddict.Models package.
///
/// The services builder used by OpenIddict to register new services.
@@ -34,35 +35,42 @@ namespace Microsoft.Extensions.DependencyInjection {
builder.ScopeType != null &&
builder.TokenType != null, "The entity types exposed by OpenIddictBuilder shouldn't be null.");
- var application = FindGenericBaseType(builder.ApplicationType, typeof(OpenIddictApplication<,>));
+ var application = FindGenericBaseType(builder.ApplicationType, typeof(OpenIddictApplication<,,>));
if (application == null) {
- throw new InvalidOperationException("The Entity Framework stores can only be used " +
+ throw new InvalidOperationException("The Entity Framework Core stores can only be used " +
"with the built-in OpenIddictApplication entity.");
}
- var authorization = FindGenericBaseType(builder.AuthorizationType, typeof(OpenIddictAuthorization<,>));
+ var authorization = FindGenericBaseType(builder.AuthorizationType, typeof(OpenIddictAuthorization<,,>));
if (authorization == null) {
- throw new InvalidOperationException("The Entity Framework stores can only be used " +
+ throw new InvalidOperationException("The Entity Framework Core stores can only be used " +
"with the built-in OpenIddictAuthorization entity.");
}
var scope = FindGenericBaseType(builder.ScopeType, typeof(OpenIddictScope<>));
if (scope == null) {
- throw new InvalidOperationException("The Entity Framework stores can only be used " +
+ throw new InvalidOperationException("The Entity Framework Core stores can only be used " +
"with the built-in OpenIddictScope entity.");
}
- var token = FindGenericBaseType(builder.TokenType, typeof(OpenIddictToken<>));
+ var token = FindGenericBaseType(builder.TokenType, typeof(OpenIddictToken<,,>));
if (token == null) {
- throw new InvalidOperationException("The Entity Framework stores can only be used " +
+ throw new InvalidOperationException("The Entity Framework Core stores can only be used " +
"with the built-in OpenIddictToken entity.");
}
+ var converter = TypeDescriptor.GetConverter(application.GenericTypeArguments[0]);
+ if (converter == null || !converter.CanConvertFrom(typeof(string)) ||
+ !converter.CanConvertTo(typeof(string))) {
+ throw new InvalidOperationException("The specified entity key type is not supported.");
+ }
+
// Register the application store in the DI container.
builder.Services.TryAddScoped(
typeof(IOpenIddictApplicationStore<>).MakeGenericType(builder.ApplicationType),
- typeof(OpenIddictApplicationStore<,,,>).MakeGenericType(
+ typeof(OpenIddictApplicationStore<,,,,>).MakeGenericType(
/* TApplication: */ builder.ApplicationType,
+ /* TAuthorization: */ builder.AuthorizationType,
/* TToken: */ builder.TokenType,
/* TContext: */ typeof(TContext),
/* TKey: */ application.GenericTypeArguments[0]));
@@ -70,8 +78,9 @@ namespace Microsoft.Extensions.DependencyInjection {
// Register the authorization store in the DI container.
builder.Services.TryAddScoped(
typeof(IOpenIddictAuthorizationStore<>).MakeGenericType(builder.AuthorizationType),
- typeof(OpenIddictAuthorizationStore<,,,>).MakeGenericType(
+ typeof(OpenIddictAuthorizationStore<,,,,>).MakeGenericType(
/* TAuthorization: */ builder.AuthorizationType,
+ /* TApplication: */ builder.ApplicationType,
/* TToken: */ builder.TokenType,
/* TContext: */ typeof(TContext),
/* TKey: */ authorization.GenericTypeArguments[0]));
@@ -87,8 +96,9 @@ namespace Microsoft.Extensions.DependencyInjection {
// Register the token store in the DI container.
builder.Services.TryAddScoped(
typeof(IOpenIddictTokenStore<>).MakeGenericType(builder.TokenType),
- typeof(OpenIddictTokenStore<,,,>).MakeGenericType(
+ typeof(OpenIddictTokenStore<,,,,>).MakeGenericType(
/* TToken: */ builder.TokenType,
+ /* TApplication: */ builder.ApplicationType,
/* TAuthorization: */ builder.AuthorizationType,
/* TContext: */ typeof(TContext),
/* TKey: */ token.GenericTypeArguments[0]));
@@ -103,7 +113,10 @@ namespace Microsoft.Extensions.DependencyInjection {
/// The builder used to configure the Entity Framework context.
/// The Entity Framework context builder.
public static DbContextOptionsBuilder UseOpenIddict([NotNull] this DbContextOptionsBuilder builder) {
- return builder.UseOpenIddict();
+ return builder.UseOpenIddict();
}
///
@@ -126,10 +139,10 @@ namespace Microsoft.Extensions.DependencyInjection {
/// The builder used to configure the Entity Framework context.
/// The Entity Framework context builder.
public static DbContextOptionsBuilder UseOpenIddict([NotNull] this DbContextOptionsBuilder builder)
- where TApplication : OpenIddictApplication
- where TAuthorization : OpenIddictAuthorization
- where TScope : OpenIddictScope
- where TToken : OpenIddictToken
+ where TApplication : OpenIddictApplication, new()
+ where TAuthorization : OpenIddictAuthorization, new()
+ where TScope : OpenIddictScope, new()
+ where TToken : OpenIddictToken, new()
where TKey : IEquatable {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
@@ -148,7 +161,10 @@ namespace Microsoft.Extensions.DependencyInjection {
/// The builder used to configure the Entity Framework context.
/// The Entity Framework context builder.
public static ModelBuilder UseOpenIddict([NotNull] this ModelBuilder builder) {
- return builder.UseOpenIddict();
+ return builder.UseOpenIddict();
}
///
@@ -171,10 +187,10 @@ namespace Microsoft.Extensions.DependencyInjection {
/// The builder used to configure the Entity Framework context.
/// The Entity Framework context builder.
public static ModelBuilder UseOpenIddict([NotNull] this ModelBuilder builder)
- where TApplication : OpenIddictApplication
- where TAuthorization : OpenIddictAuthorization
- where TScope : OpenIddictScope
- where TToken : OpenIddictToken
+ where TApplication : OpenIddictApplication, new()
+ where TAuthorization : OpenIddictAuthorization, new()
+ where TScope : OpenIddictScope, new()
+ where TToken : OpenIddictToken, new()
where TKey : IEquatable {
if (builder == null) {
throw new ArgumentNullException(nameof(builder));
@@ -188,11 +204,16 @@ namespace Microsoft.Extensions.DependencyInjection {
builder.Entity(entity => {
entity.HasKey(application => application.Id);
- entity.HasIndex("ClientId")
+ entity.HasIndex(application => application.ClientId)
.IsUnique(unique: true);
+ entity.HasMany(application => application.Authorizations)
+ .WithOne(authorization => authorization.Application)
+ .HasForeignKey("ApplicationId")
+ .IsRequired(required: false);
+
entity.HasMany(application => application.Tokens)
- .WithOne()
+ .WithOne(token => token.Application)
.HasForeignKey("ApplicationId")
.IsRequired(required: false);
@@ -204,7 +225,7 @@ namespace Microsoft.Extensions.DependencyInjection {
entity.HasKey(authorization => authorization.Id);
entity.HasMany(application => application.Tokens)
- .WithOne()
+ .WithOne(token => token.Authorization)
.HasForeignKey("AuthorizationId")
.IsRequired(required: false);
@@ -229,6 +250,14 @@ namespace Microsoft.Extensions.DependencyInjection {
}
private static TypeInfo FindGenericBaseType(Type type, Type definition) {
+ if (type == null) {
+ throw new ArgumentNullException(nameof(type));
+ }
+
+ if (definition == null) {
+ throw new ArgumentNullException(nameof(definition));
+ }
+
for (var candidate = type.GetTypeInfo(); candidate != null; candidate = candidate.BaseType?.GetTypeInfo()) {
if (candidate.IsGenericType && candidate.GetGenericTypeDefinition() == definition) {
return candidate;
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs
index a15c57dd..35f7fb56 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs
@@ -16,16 +16,42 @@ using OpenIddict.Core;
using OpenIddict.Models;
namespace OpenIddict.EntityFrameworkCore {
+ ///
+ /// Provides methods allowing to manage the applications stored in a database.
+ ///
+ /// The type of the Entity Framework database context.
+ public class OpenIddictApplicationStore : OpenIddictApplicationStore
+ where TContext : DbContext {
+ public OpenIddictApplicationStore([NotNull] TContext context) : base(context) { }
+ }
+
+ ///
+ /// Provides methods allowing to manage the applications stored in a database.
+ ///
+ /// The type of the Entity Framework database context.
+ /// The type of the entity primary keys.
+ public class OpenIddictApplicationStore : OpenIddictApplicationStore,
+ OpenIddictAuthorization,
+ OpenIddictToken, TContext, TKey>
+ where TContext : DbContext
+ where TKey : IEquatable {
+ public OpenIddictApplicationStore([NotNull] TContext context) : base(context) { }
+ }
+
///
/// Provides methods allowing to manage the applications stored in a database.
///
/// The type of the Application entity.
+ /// The type of the Authorization entity.
/// The type of the Token entity.
/// The type of the Entity Framework database context.
/// The type of the entity primary keys.
- public class OpenIddictApplicationStore : IOpenIddictApplicationStore
- where TApplication : OpenIddictApplication
- where TToken : OpenIddictToken, new()
+ public class OpenIddictApplicationStore : IOpenIddictApplicationStore
+ where TApplication : OpenIddictApplication, new()
+ where TAuthorization : OpenIddictAuthorization, new()
+ where TToken : OpenIddictToken, new()
where TContext : DbContext
where TKey : IEquatable {
public OpenIddictApplicationStore([NotNull] TContext context) {
@@ -51,23 +77,19 @@ namespace OpenIddict.EntityFrameworkCore {
///
/// The application to create.
/// The that can be used to abort the operation.
- /// A that can be used to monitor the asynchronous operation.
- public virtual async Task CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
+ ///
+ /// A that can be used to monitor the asynchronous operation, whose result returns the application.
+ ///
+ public virtual async Task CreateAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
if (application == null) {
throw new ArgumentNullException(nameof(application));
}
- // Ensure that the key type can be serialized.
- var converter = TypeDescriptor.GetConverter(typeof(TKey));
- if (!converter.CanConvertTo(typeof(string))) {
- throw new InvalidOperationException($"The '{typeof(TKey).Name}' key type is not supported.");
- }
-
Context.Add(application);
await Context.SaveChangesAsync(cancellationToken);
- return converter.ConvertToInvariantString(application.Id);
+ return application;
}
///
@@ -102,15 +124,7 @@ namespace OpenIddict.EntityFrameworkCore {
/// whose result returns the client application corresponding to the identifier.
///
public virtual Task FindByIdAsync(string identifier, CancellationToken cancellationToken) {
- var converter = TypeDescriptor.GetConverter(typeof(TKey));
-
- // If the string key cannot be converted to TKey, return null
- // to indicate that the requested application doesn't exist.
- if (!converter.CanConvertFrom(typeof(string))) {
- return Task.FromResult(null);
- }
-
- var key = (TKey) converter.ConvertFromInvariantString(identifier);
+ var key = ConvertIdentifierFromString(identifier);
return Applications.SingleOrDefaultAsync(application => application.Id.Equals(key), cancellationToken);
}
@@ -209,6 +223,23 @@ namespace OpenIddict.EntityFrameworkCore {
return Task.FromResult(application.ClientSecret);
}
+ ///
+ /// Retrieves the unique identifier associated with an application.
+ ///
+ /// The application.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the unique identifier associated with the application.
+ ///
+ public virtual Task GetIdAsync([NotNull] TApplication application, CancellationToken cancellationToken) {
+ if (application == null) {
+ throw new ArgumentNullException(nameof(application));
+ }
+
+ return Task.FromResult(ConvertIdentifierToString(application.Id));
+ }
+
///
/// Retrieves the logout callback address associated with an application.
///
@@ -257,12 +288,6 @@ namespace OpenIddict.EntityFrameworkCore {
throw new ArgumentNullException(nameof(application));
}
- // Ensure that the key type can be serialized.
- var converter = TypeDescriptor.GetConverter(typeof(TKey));
- if (!converter.CanConvertTo(typeof(string))) {
- throw new InvalidOperationException($"The '{typeof(TKey).Name}' key type is not supported.");
- }
-
var query = from entity in Applications
where entity.Id.Equals(application.Id)
from token in entity.Tokens
@@ -271,7 +296,7 @@ namespace OpenIddict.EntityFrameworkCore {
var tokens = new List();
foreach (var identifier in await query.ToArrayAsync()) {
- tokens.Add(converter.ConvertToInvariantString(identifier));
+ tokens.Add(ConvertIdentifierToString(identifier));
}
return tokens;
@@ -345,5 +370,33 @@ namespace OpenIddict.EntityFrameworkCore {
catch (DbUpdateConcurrencyException) { }
}
+
+ ///
+ /// Converts the provided identifier to a strongly typed key object.
+ ///
+ /// The identifier to convert.
+ /// An instance of representing the provided identifier.
+ public virtual TKey ConvertIdentifierFromString([CanBeNull] string identifier) {
+ if (string.IsNullOrEmpty(identifier)) {
+ return default(TKey);
+ }
+
+ return (TKey) TypeDescriptor.GetConverter(typeof(TKey))
+ .ConvertFromInvariantString(identifier);
+ }
+
+ ///
+ /// Converts the provided identifier to its string representation.
+ ///
+ /// The identifier to convert.
+ /// A representation of the provided identifier.
+ public virtual string ConvertIdentifierToString([CanBeNull] TKey identifier) {
+ if (Equals(identifier, default(TKey))) {
+ return null;
+ }
+
+ return TypeDescriptor.GetConverter(typeof(TKey))
+ .ConvertToInvariantString(identifier);
+ }
}
}
\ No newline at end of file
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
index 03767c29..26a36030 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs
@@ -5,22 +5,52 @@
*/
using System;
+using System.ComponentModel;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using OpenIddict.Core;
using OpenIddict.Models;
namespace OpenIddict.EntityFrameworkCore {
+ ///
+ /// Provides methods allowing to manage the authorizations stored in a database.
+ ///
+ /// The type of the Entity Framework database context.
+ public class OpenIddictAuthorizationStore : OpenIddictAuthorizationStore
+ where TContext : DbContext {
+ public OpenIddictAuthorizationStore([NotNull] TContext context) : base(context) { }
+ }
+
+ ///
+ /// Provides methods allowing to manage the authorizations stored in a database.
+ ///
+ /// The type of the Entity Framework database context.
+ /// The type of the entity primary keys.
+ public class OpenIddictAuthorizationStore : OpenIddictAuthorizationStore,
+ OpenIddictApplication,
+ OpenIddictToken, TContext, TKey>
+ where TContext : DbContext
+ where TKey : IEquatable {
+ public OpenIddictAuthorizationStore([NotNull] TContext context) : base(context) { }
+ }
+
///
/// Provides methods allowing to manage the authorizations stored in a database.
///
/// The type of the Authorization entity.
+ /// The type of the Application entity.
/// The type of the Token entity.
/// The type of the Entity Framework database context.
/// The type of the entity primary keys.
- public class OpenIddictAuthorizationStore : IOpenIddictAuthorizationStore
- where TAuthorization : OpenIddictAuthorization
- where TToken : OpenIddictToken
+ public class OpenIddictAuthorizationStore : IOpenIddictAuthorizationStore
+ where TAuthorization : OpenIddictAuthorization, new()
+ where TApplication : OpenIddictApplication, new()
+ where TToken : OpenIddictToken, new()
where TContext : DbContext
where TKey : IEquatable {
public OpenIddictAuthorizationStore([NotNull] TContext context) {
@@ -36,9 +66,131 @@ namespace OpenIddict.EntityFrameworkCore {
///
protected virtual TContext Context { get; }
+ ///
+ /// Gets the database set corresponding to the entity.
+ ///
+ protected DbSet Applications => Context.Set();
+
///
/// Gets the database set corresponding to the entity.
///
protected DbSet Authorizations => Context.Set();
+
+ ///
+ /// Creates a new authorization.
+ ///
+ /// The authorization to create.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation, whose result returns the authorization.
+ ///
+ public virtual async Task CreateAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) {
+ if (authorization == null) {
+ throw new ArgumentNullException(nameof(authorization));
+ }
+
+ Context.Add(authorization);
+
+ await Context.SaveChangesAsync(cancellationToken);
+
+ return authorization;
+ }
+
+ ///
+ /// Retrieves an authorization using its associated subject/client.
+ ///
+ /// The subject associated with the authorization.
+ /// The client associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the authorization corresponding to the subject/client.
+ ///
+ public virtual Task FindAsync(string subject, string client, CancellationToken cancellationToken) {
+ var key = ConvertIdentifierFromString(client);
+
+ return (from application in Applications
+ where application.Id.Equals(key)
+ from authorization in application.Authorizations
+ where authorization.Subject == subject
+ select authorization).FirstOrDefaultAsync();
+ }
+
+ ///
+ /// Retrieves an authorization using its unique identifier.
+ ///
+ /// The unique identifier associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the authorization corresponding to the identifier.
+ ///
+ public virtual Task FindByIdAsync(string identifier, CancellationToken cancellationToken) {
+ var key = ConvertIdentifierFromString(identifier);
+
+ return Authorizations.SingleOrDefaultAsync(authorization => authorization.Id.Equals(key), cancellationToken);
+ }
+
+ ///
+ /// Retrieves the unique identifier associated with an authorization.
+ ///
+ /// The authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the unique identifier associated with the authorization.
+ ///
+ public virtual Task GetIdAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) {
+ if (authorization == null) {
+ throw new ArgumentNullException(nameof(authorization));
+ }
+
+ return Task.FromResult(ConvertIdentifierToString(authorization.Id));
+ }
+
+ ///
+ /// Retrieves the subject associated with an authorization.
+ ///
+ /// The authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the subject associated with the specified authorization.
+ ///
+ public virtual Task GetSubjectAsync([NotNull] TAuthorization authorization, CancellationToken cancellationToken) {
+ if (authorization == null) {
+ throw new ArgumentNullException(nameof(authorization));
+ }
+
+ return Task.FromResult(authorization.Subject);
+ }
+
+ ///
+ /// Converts the provided identifier to a strongly typed key object.
+ ///
+ /// The identifier to convert.
+ /// An instance of representing the provided identifier.
+ public virtual TKey ConvertIdentifierFromString([CanBeNull] string identifier) {
+ if (string.IsNullOrEmpty(identifier)) {
+ return default(TKey);
+ }
+
+ return (TKey) TypeDescriptor.GetConverter(typeof(TKey))
+ .ConvertFromInvariantString(identifier);
+ }
+
+ ///
+ /// Converts the provided identifier to its string representation.
+ ///
+ /// The identifier to convert.
+ /// A representation of the provided identifier.
+ public virtual string ConvertIdentifierToString([CanBeNull] TKey identifier) {
+ if (Equals(identifier, default(TKey))) {
+ return null;
+ }
+
+ return TypeDescriptor.GetConverter(typeof(TKey))
+ .ConvertToInvariantString(identifier);
+ }
}
}
\ No newline at end of file
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs
index b28e49ee..d2ed90b6 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs
@@ -5,12 +5,33 @@
*/
using System;
+using System.ComponentModel;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using OpenIddict.Core;
using OpenIddict.Models;
namespace OpenIddict.EntityFrameworkCore {
+ ///
+ /// Provides methods allowing to manage the scopes stored in a database.
+ ///
+ /// The type of the Entity Framework database context.
+ public class OpenIddictScopeStore : OpenIddictScopeStore
+ where TContext : DbContext {
+ public OpenIddictScopeStore([NotNull] TContext context) : base(context) { }
+ }
+
+ ///
+ /// Provides methods allowing to manage the scopes stored in a database.
+ ///
+ /// The type of the Entity Framework database context.
+ /// The type of the entity primary keys.
+ public class OpenIddictScopeStore : OpenIddictScopeStore, TContext, TKey>
+ where TContext : DbContext
+ where TKey : IEquatable {
+ public OpenIddictScopeStore([NotNull] TContext context) : base(context) { }
+ }
+
///
/// Provides methods allowing to manage the scopes stored in a database.
///
@@ -18,7 +39,7 @@ namespace OpenIddict.EntityFrameworkCore {
/// The type of the Entity Framework database context.
/// The type of the entity primary keys.
public class OpenIddictScopeStore : IOpenIddictScopeStore
- where TScope : OpenIddictScope
+ where TScope : OpenIddictScope, new()
where TContext : DbContext
where TKey : IEquatable {
public OpenIddictScopeStore([NotNull] TContext context) {
@@ -37,6 +58,34 @@ namespace OpenIddict.EntityFrameworkCore {
///
/// Gets the database set corresponding to the entity.
///
- protected DbSet Authorizations => Context.Set();
+ protected DbSet Scopes => Context.Set();
+
+ ///
+ /// Converts the provided identifier to a strongly typed key object.
+ ///
+ /// The identifier to convert.
+ /// An instance of representing the provided identifier.
+ public virtual TKey ConvertIdentifierFromString([CanBeNull] string identifier) {
+ if (string.IsNullOrEmpty(identifier)) {
+ return default(TKey);
+ }
+
+ return (TKey) TypeDescriptor.GetConverter(typeof(TKey))
+ .ConvertFromInvariantString(identifier);
+ }
+
+ ///
+ /// Converts the provided identifier to its string representation.
+ ///
+ /// The identifier to convert.
+ /// A representation of the provided identifier.
+ public virtual string ConvertIdentifierToString([CanBeNull] TKey identifier) {
+ if (Equals(identifier, default(TKey))) {
+ return null;
+ }
+
+ return TypeDescriptor.GetConverter(typeof(TKey))
+ .ConvertToInvariantString(identifier);
+ }
}
}
\ No newline at end of file
diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
index 4145db57..5621c005 100644
--- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
+++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs
@@ -6,6 +6,7 @@
using System;
using System.ComponentModel;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
@@ -14,16 +15,42 @@ using OpenIddict.Core;
using OpenIddict.Models;
namespace OpenIddict.EntityFrameworkCore {
+ ///
+ /// Provides methods allowing to manage the tokens stored in a database.
+ ///
+ /// The type of the Entity Framework database context.
+ public class OpenIddictTokenStore : OpenIddictTokenStore
+ where TContext : DbContext {
+ public OpenIddictTokenStore([NotNull] TContext context) : base(context) { }
+ }
+
+ ///
+ /// Provides methods allowing to manage the tokens stored in a database.
+ ///
+ /// The type of the Entity Framework database context.
+ /// The type of the entity primary keys.
+ public class OpenIddictTokenStore : OpenIddictTokenStore,
+ OpenIddictApplication,
+ OpenIddictAuthorization, TContext, TKey>
+ where TContext : DbContext
+ where TKey : IEquatable {
+ public OpenIddictTokenStore([NotNull] TContext context) : base(context) { }
+ }
+
///
/// Provides methods allowing to manage the tokens stored in a database.
///
/// The type of the Token entity.
+ /// The type of the Application entity.
/// The type of the Authorization entity.
/// The type of the Entity Framework database context.
/// The type of the entity primary keys.
- public class OpenIddictTokenStore : IOpenIddictTokenStore
- where TToken : OpenIddictToken, new()
- where TAuthorization : OpenIddictAuthorization
+ public class OpenIddictTokenStore : IOpenIddictTokenStore
+ where TToken : OpenIddictToken, new()
+ where TApplication : OpenIddictApplication, new()
+ where TAuthorization : OpenIddictAuthorization, new()
where TContext : DbContext
where TKey : IEquatable {
public OpenIddictTokenStore([NotNull] TContext context) {
@@ -39,37 +66,56 @@ namespace OpenIddict.EntityFrameworkCore {
///
protected virtual TContext Context { get; }
+ ///
+ /// Gets the database set corresponding to the entity.
+ ///
+ protected DbSet Applications => Context.Set();
+
+ ///
+ /// Gets the database set corresponding to the entity.
+ ///
+ protected DbSet Authorizations => Context.Set();
+
///
/// Gets the database set corresponding to the entity.
///
protected DbSet Tokens => Context.Set();
///
- /// Creates a new token, which is not associated with a particular user or client.
+ /// Creates a new token.
///
- /// The token type.
+ /// The token to create.
/// The that can be used to abort the operation.
///
- /// A that can be used to monitor the asynchronous operation,
- /// whose result returns the unique identifier associated with the token.
+ /// A that can be used to monitor the asynchronous operation, whose result returns the token.
///
- public virtual async Task CreateAsync([NotNull] string type, CancellationToken cancellationToken) {
- if (string.IsNullOrEmpty(type)) {
- throw new ArgumentException("The token type cannot be null or empty.");
- }
-
- // Ensure that the key type can be serialized.
- var converter = TypeDescriptor.GetConverter(typeof(TKey));
- if (!converter.CanConvertTo(typeof(string))) {
- throw new InvalidOperationException($"The '{typeof(TKey).Name}' key type is not supported.");
+ public virtual async Task CreateAsync([NotNull] TToken token, CancellationToken cancellationToken) {
+ if (token == null) {
+ throw new ArgumentNullException(nameof(token));
}
- var token = new TToken { Type = type };
- Tokens.Add(token);
+ Context.Add(token);
await Context.SaveChangesAsync(cancellationToken);
- return converter.ConvertToInvariantString(token.Id);
+ return token;
+ }
+
+ ///
+ /// Creates a new token, which is associated with a particular subject.
+ ///
+ /// The token type.
+ /// The subject associated with the token.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation, whose result returns the token.
+ ///
+ public virtual Task CreateAsync([NotNull] string type, [NotNull] string subject, CancellationToken cancellationToken) {
+ if (string.IsNullOrEmpty(type)) {
+ throw new ArgumentException("The token type cannot be null or empty.");
+ }
+
+ return CreateAsync(new TToken { Subject = subject, Type = type }, cancellationToken);
}
///
@@ -82,16 +128,73 @@ namespace OpenIddict.EntityFrameworkCore {
/// whose result returns the token corresponding to the unique identifier.
///
public virtual Task FindByIdAsync(string identifier, CancellationToken cancellationToken) {
- // If the string key cannot be converted to TKey, return null
- // to indicate that the requested token doesn't exist.
- var converter = TypeDescriptor.GetConverter(typeof(TKey));
- if (!converter.CanConvertFrom(typeof(string))) {
- return Task.FromResult(null);
+ var key = ConvertIdentifierFromString(identifier);
+
+ return Tokens.SingleOrDefaultAsync(token => token.Id.Equals(key), cancellationToken);
+ }
+
+ ///
+ /// Retrieves the list of tokens corresponding to the specified subject.
+ ///
+ /// The subject associated with the tokens.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the tokens corresponding to the specified subject.
+ ///
+ public virtual Task FindBySubjectAsync(string subject, CancellationToken cancellationToken) {
+ return Tokens.Where(token => token.Subject == subject).ToArrayAsync();
+ }
+
+ ///
+ /// Retrieves the unique identifier associated with a token.
+ ///
+ /// The token.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the unique identifier associated with the token.
+ ///
+ public virtual Task GetIdAsync([NotNull] TToken token, CancellationToken cancellationToken) {
+ if (token == null) {
+ throw new ArgumentNullException(nameof(token));
}
- var key = (TKey) converter.ConvertFromInvariantString(identifier);
+ return Task.FromResult(ConvertIdentifierToString(token.Id));
+ }
- return Tokens.SingleOrDefaultAsync(token => token.Id.Equals(key), cancellationToken);
+ ///
+ /// Retrieves the token type associated with a token.
+ ///
+ /// The token.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the token type associated with the specified token.
+ ///
+ public virtual Task GetTokenTypeAsync([NotNull] TToken token, CancellationToken cancellationToken) {
+ if (token == null) {
+ throw new ArgumentNullException(nameof(token));
+ }
+
+ return Task.FromResult(token.Type);
+ }
+
+ ///
+ /// Retrieves the subject associated with a token.
+ ///
+ /// The token.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation,
+ /// whose result returns the subject associated with the specified token.
+ ///
+ public virtual Task GetSubjectAsync([NotNull] TToken token, CancellationToken cancellationToken) {
+ if (token == null) {
+ throw new ArgumentNullException(nameof(token));
+ }
+
+ return Task.FromResult(token.Subject);
}
///
@@ -113,5 +216,130 @@ namespace OpenIddict.EntityFrameworkCore {
catch (DbUpdateConcurrencyException) { }
}
+
+ ///
+ /// Sets the authorization associated with a token.
+ ///
+ /// The token.
+ /// The unique identifier associated with the authorization.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public virtual async Task SetAuthorizationAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) {
+ if (token == null) {
+ throw new ArgumentNullException(nameof(token));
+ }
+
+ if (!string.IsNullOrEmpty(identifier)) {
+ var key = ConvertIdentifierFromString(identifier);
+
+ var authorization = await Authorizations.SingleOrDefaultAsync(element => element.Id.Equals(key));
+ if (authorization == null) {
+ throw new InvalidOperationException("The authorization associated with the token cannot be found.");
+ }
+
+ authorization.Tokens.Add(token);
+ }
+
+ else {
+ var key = await GetIdAsync(token, cancellationToken);
+
+ // Try to retrieve the authorization associated with the token.
+ // If none can be found, assume that no authorization is attached.
+ var authorization = await Authorizations.SingleOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key)));
+ if (authorization != null) {
+ authorization.Tokens.Remove(token);
+ }
+ }
+ }
+
+ ///
+ /// Sets the client application associated with a token.
+ ///
+ /// The token.
+ /// The unique identifier associated with the client application.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public virtual async Task SetClientAsync([NotNull] TToken token, [CanBeNull] string identifier, CancellationToken cancellationToken) {
+ if (token == null) {
+ throw new ArgumentNullException(nameof(token));
+ }
+
+ if (!string.IsNullOrEmpty(identifier)) {
+ var key = ConvertIdentifierFromString(identifier);
+
+ var application = await Applications.SingleOrDefaultAsync(element => element.Id.Equals(key));
+ if (application == null) {
+ throw new InvalidOperationException("The application associated with the token cannot be found.");
+ }
+
+ application.Tokens.Add(token);
+ }
+
+ else {
+ var key = await GetIdAsync(token, cancellationToken);
+
+ // Try to retrieve the application associated with the token.
+ // If none can be found, assume that no application is attached.
+ var application = await Applications.SingleOrDefaultAsync(element => element.Tokens.Any(t => t.Id.Equals(key)));
+ if (application != null) {
+ application.Tokens.Remove(token);
+ }
+ }
+ }
+
+ ///
+ /// Updates an existing token.
+ ///
+ /// The token to update.
+ /// The that can be used to abort the operation.
+ ///
+ /// A that can be used to monitor the asynchronous operation.
+ ///
+ public virtual async Task UpdateAsync([NotNull] TToken token, CancellationToken cancellationToken) {
+ if (token == null) {
+ throw new ArgumentNullException(nameof(token));
+ }
+
+ Context.Attach(token);
+ Context.Update(token);
+
+ try {
+ await Context.SaveChangesAsync(cancellationToken);
+ }
+
+ catch (DbUpdateConcurrencyException) { }
+ }
+
+ ///
+ /// Converts the provided identifier to a strongly typed key object.
+ ///
+ /// The identifier to convert.
+ /// An instance of representing the provided identifier.
+ public virtual TKey ConvertIdentifierFromString([CanBeNull] string identifier) {
+ if (string.IsNullOrEmpty(identifier)) {
+ return default(TKey);
+ }
+
+ return (TKey) TypeDescriptor.GetConverter(typeof(TKey))
+ .ConvertFromInvariantString(identifier);
+ }
+
+ ///
+ /// Converts the provided identifier to its string representation.
+ ///
+ /// The identifier to convert.
+ /// A representation of the provided identifier.
+ public virtual string ConvertIdentifierToString([CanBeNull] TKey identifier) {
+ if (Equals(identifier, default(TKey))) {
+ return null;
+ }
+
+ return TypeDescriptor.GetConverter(typeof(TKey))
+ .ConvertToInvariantString(identifier);
+ }
}
}
\ No newline at end of file
diff --git a/src/OpenIddict.Models/OpenIddictApplication.cs b/src/OpenIddict.Models/OpenIddictApplication.cs
index ee2b18a2..dc9b98df 100644
--- a/src/OpenIddict.Models/OpenIddictApplication.cs
+++ b/src/OpenIddict.Models/OpenIddictApplication.cs
@@ -11,7 +11,7 @@ namespace OpenIddict.Models {
///
/// Represents an OpenIddict application.
///
- public class OpenIddictApplication : OpenIddictApplication {
+ public class OpenIddictApplication : OpenIddictApplication {
public OpenIddictApplication() {
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
@@ -21,13 +21,18 @@ namespace OpenIddict.Models {
///
/// Represents an OpenIddict application.
///
- public class OpenIddictApplication : OpenIddictApplication>
+ public class OpenIddictApplication : OpenIddictApplication, OpenIddictToken>
where TKey : IEquatable { }
///
/// Represents an OpenIddict application.
///
- public class OpenIddictApplication where TKey : IEquatable {
+ public class OpenIddictApplication where TKey : IEquatable {
+ ///
+ /// Gets the list of the authorizations associated with this application.
+ ///
+ public virtual IList Authorizations { get; } = new List();
+
///
/// Gets or sets the client identifier
/// associated with the current application.
diff --git a/src/OpenIddict.Models/OpenIddictAuthorization.cs b/src/OpenIddict.Models/OpenIddictAuthorization.cs
index 9837b05a..13ad15b5 100644
--- a/src/OpenIddict.Models/OpenIddictAuthorization.cs
+++ b/src/OpenIddict.Models/OpenIddictAuthorization.cs
@@ -11,7 +11,7 @@ namespace OpenIddict.Models {
///
/// Represents an OpenIddict authorization.
///
- public class OpenIddictAuthorization : OpenIddictAuthorization {
+ public class OpenIddictAuthorization : OpenIddictAuthorization {
public OpenIddictAuthorization() {
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
@@ -21,13 +21,18 @@ namespace OpenIddict.Models {
///
/// Represents an OpenIddict authorization.
///
- public class OpenIddictAuthorization : OpenIddictAuthorization>
+ public class OpenIddictAuthorization : OpenIddictAuthorization, OpenIddictToken>
where TKey : IEquatable { }
///
/// Represents an OpenIddict authorization.
///
- public class OpenIddictAuthorization where TKey : IEquatable {
+ public class OpenIddictAuthorization where TKey : IEquatable {
+ ///
+ /// Gets or sets the application associated with the current authorization.
+ ///
+ public virtual TApplication Application { get; set; }
+
///
/// Gets or sets the unique identifier
/// associated with the current authorization.
@@ -40,6 +45,11 @@ namespace OpenIddict.Models {
///
public virtual string Scope { get; set; }
+ ///
+ /// Gets or sets the subject associated with the current authorization.
+ ///
+ public virtual string Subject { get; set; }
+
///
/// Gets or sets the list of tokens
/// associated with the current authorization.
diff --git a/src/OpenIddict.Models/OpenIddictToken.cs b/src/OpenIddict.Models/OpenIddictToken.cs
index e8a1976c..d9574e3d 100644
--- a/src/OpenIddict.Models/OpenIddictToken.cs
+++ b/src/OpenIddict.Models/OpenIddictToken.cs
@@ -10,7 +10,7 @@ namespace OpenIddict.Models {
///
/// Represents an OpenIddict token.
///
- public class OpenIddictToken : OpenIddictToken {
+ public class OpenIddictToken : OpenIddictToken {
public OpenIddictToken() {
// Generate a new string identifier.
Id = Guid.NewGuid().ToString();
@@ -20,13 +20,35 @@ namespace OpenIddict.Models {
///
/// Represents an OpenIddict token.
///
- public class OpenIddictToken where TKey : IEquatable {
+ public class OpenIddictToken : OpenIddictToken, OpenIddictAuthorization>
+ where TKey : IEquatable {
+ }
+
+ ///
+ /// Represents an OpenIddict token.
+ ///
+ public class OpenIddictToken where TKey : IEquatable {
+ ///
+ /// Gets or sets the application associated with the current token.
+ ///
+ public virtual TApplication Application { get; set; }
+
+ ///
+ /// Gets or sets the authorization associated with the current token.
+ ///
+ public virtual TAuthorization Authorization { get; set; }
+
///
/// Gets or sets the unique identifier
/// associated with the current token.
///
public virtual TKey Id { get; set; }
+ ///
+ /// Gets or sets the subject associated with the current token.
+ ///
+ public virtual string Subject { get; set; }
+
///
/// Gets or sets the type of the current token.
///
diff --git a/src/OpenIddict.Mvc/OpenIddictModelBinder.cs b/src/OpenIddict.Mvc/OpenIddictModelBinder.cs
index ed27023a..df34f900 100644
--- a/src/OpenIddict.Mvc/OpenIddictModelBinder.cs
+++ b/src/OpenIddict.Mvc/OpenIddictModelBinder.cs
@@ -1,6 +1,7 @@
using System;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives;
+using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
@@ -17,7 +18,7 @@ namespace OpenIddict.Mvc {
///
/// The model binding context.
/// A representing the asynchronous operation.
- public Task BindModelAsync(ModelBindingContext context) {
+ public Task BindModelAsync([NotNull] ModelBindingContext context) {
if (context == null) {
throw new ArgumentNullException(nameof(context));
}
@@ -65,7 +66,7 @@ namespace OpenIddict.Mvc {
///
/// The model binding context.
/// The current instance or null if the model is not supported.
- public IModelBinder GetBinder(ModelBinderProviderContext context) {
+ public IModelBinder GetBinder([NotNull] ModelBinderProviderContext context) {
if (context == null) {
throw new ArgumentNullException(nameof(context));
}
diff --git a/src/OpenIddict/OpenIddictProvider.Serialization.cs b/src/OpenIddict/OpenIddictProvider.Serialization.cs
index f1964163..022a4b07 100644
--- a/src/OpenIddict/OpenIddictProvider.Serialization.cs
+++ b/src/OpenIddict/OpenIddictProvider.Serialization.cs
@@ -5,6 +5,8 @@
*/
using System;
+using System.Diagnostics;
+using System.Security.Claims;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Primitives;
@@ -18,11 +20,30 @@ namespace OpenIddict {
public partial class OpenIddictProvider : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override async Task SerializeAuthorizationCode([NotNull] SerializeAuthorizationCodeContext context) {
+ var applications = context.HttpContext.RequestServices.GetRequiredService>();
var options = context.HttpContext.RequestServices.GetRequiredService>();
var tokens = context.HttpContext.RequestServices.GetRequiredService>();
+ Debug.Assert(!string.IsNullOrEmpty(context.Request.ClientId), "The client identifier shouldn't be null or empty.");
+
if (!options.Value.DisableTokenRevocation) {
- var identifier = await tokens.CreateAsync(OpenIdConnectConstants.TokenTypeHints.AuthorizationCode, context.HttpContext.RequestAborted);
+ // Resolve the subject from the authentication ticket. If it cannot be found, throw an exception.
+ var subject = context.Ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.Subject) ??
+ context.Ticket.Principal.GetClaim(ClaimTypes.NameIdentifier) ??
+ context.Ticket.Principal.GetClaim(ClaimTypes.Upn);
+
+ if (string.IsNullOrEmpty(subject)) {
+ throw new InvalidOperationException("The subject associated with the authentication ticket cannot be retrieved.");
+ }
+
+ // If a null value was returned by CreateAsync, return immediately.
+ var token = await tokens.CreateAsync(OpenIdConnectConstants.TokenTypeHints.AuthorizationCode, subject, context.HttpContext.RequestAborted);
+ if (token == null) {
+ return;
+ }
+
+ // Throw an exception if the token identifier can't be resolved.
+ var identifier = await tokens.GetIdAsync(token, context.HttpContext.RequestAborted);
if (string.IsNullOrEmpty(identifier)) {
throw new InvalidOperationException("The unique key associated with an authorization code cannot be null or empty.");
}
@@ -31,15 +52,45 @@ namespace OpenIddict {
// to the authorization code to override the default GUID
// generated by the OpenID Connect server middleware.
context.Ticket.SetTicketId(identifier);
+
+ var application = await applications.FindByClientIdAsync(context.Request.ClientId, context.HttpContext.RequestAborted);
+ if (application == null) {
+ throw new InvalidOperationException("The client application cannot be retrieved from the database.");
+ }
+
+ await tokens.SetClientAsync(token, await applications.GetIdAsync(application, context.HttpContext.RequestAborted), context.HttpContext.RequestAborted);
+
+ // If an authorization identifier was specified, bind it to the token.
+ var authorization = context.Ticket.GetProperty(OpenIddictConstants.Properties.AuthorizationId);
+ if (!string.IsNullOrEmpty(authorization)) {
+ await tokens.SetAuthorizationAsync(token, authorization, context.HttpContext.RequestAborted);
+ }
}
}
public override async Task SerializeRefreshToken([NotNull] SerializeRefreshTokenContext context) {
+ var applications = context.HttpContext.RequestServices.GetRequiredService>();
var options = context.HttpContext.RequestServices.GetRequiredService>();
var tokens = context.HttpContext.RequestServices.GetRequiredService>();
if (!options.Value.DisableTokenRevocation) {
- var identifier = await tokens.CreateAsync(OpenIdConnectConstants.TokenTypeHints.RefreshToken, context.HttpContext.RequestAborted);
+ // Resolve the subject from the authentication ticket. If it cannot be found, throw an exception.
+ var subject = context.Ticket.Principal.GetClaim(OpenIdConnectConstants.Claims.Subject) ??
+ context.Ticket.Principal.GetClaim(ClaimTypes.NameIdentifier) ??
+ context.Ticket.Principal.GetClaim(ClaimTypes.Upn);
+
+ if (string.IsNullOrEmpty(subject)) {
+ throw new InvalidOperationException("The subject associated with the authentication ticket cannot be retrieved.");
+ }
+
+ // If a null value was returned by CreateAsync, return immediately.
+ var token = await tokens.CreateAsync(OpenIdConnectConstants.TokenTypeHints.RefreshToken, subject, context.HttpContext.RequestAborted);
+ if (token == null) {
+ return;
+ }
+
+ // Throw an exception if the token identifier can't be resolved.
+ var identifier = await tokens.GetIdAsync(token, context.HttpContext.RequestAborted);
if (string.IsNullOrEmpty(identifier)) {
throw new InvalidOperationException("The unique key associated with a refresh token cannot be null or empty.");
}
@@ -48,6 +99,22 @@ namespace OpenIddict {
// to the refresh token to override the default GUID
// generated by the OpenID Connect server middleware.
context.Ticket.SetTicketId(identifier);
+
+ // If the client application is known, associate it with the token.
+ if (!string.IsNullOrEmpty(context.Request.ClientId)) {
+ var application = await applications.FindByClientIdAsync(context.Request.ClientId, context.HttpContext.RequestAborted);
+ if (application == null) {
+ throw new InvalidOperationException("The client application cannot be retrieved from the database.");
+ }
+
+ await tokens.SetClientAsync(token, await applications.GetIdAsync(application, context.HttpContext.RequestAborted), context.HttpContext.RequestAborted);
+ }
+
+ // If an authorization identifier was specified, bind it to the token.
+ var authorization = context.Ticket.GetProperty(OpenIddictConstants.Properties.AuthorizationId);
+ if (!string.IsNullOrEmpty(authorization)) {
+ await tokens.SetAuthorizationAsync(token, authorization, context.HttpContext.RequestAborted);
+ }
}
}
}
diff --git a/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs
index 4995f34f..2d5f0356 100644
--- a/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs
+++ b/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs
@@ -9,117 +9,129 @@ namespace OpenIddict.EntityFrameworkCore.Tests {
[Fact]
public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidApplicationEntity() {
// Arrange
- var services = new ServiceCollection();
+ var builder = new OpenIddictBuilder(new ServiceCollection());
+ builder.ApplicationType = typeof(object);
// Act and assert
var exception = Assert.Throws(delegate {
- services.AddOpenIddict