From 7bca2c06edd41306ba99fd43eb0b1623e899ed7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Wed, 18 Apr 2018 20:35:48 +0200 Subject: [PATCH] Revamp the OpenIddict services registration process and introduce new builders to separate the server APIs from the core APIs --- OpenIddict.sln | 9 +- .../Controllers/AuthorizationController.cs | 4 +- .../Controllers/UserinfoController.cs | 2 +- samples/Mvc.Server/Startup.cs | 112 +- .../OpenIddict.Abstractions.csproj | 23 + .../OpenIddictBuilder.cs | 38 + .../OpenIddictConstants.cs | 2 +- .../OpenIddictExtensions.cs | 56 + .../IOpenIddictApplicationStoreResolver.cs | 18 + .../IOpenIddictAuthorizationStoreResolver.cs | 18 + .../IOpenIddictScopeStoreResolver.cs | 18 + .../IOpenIddictTokenStoreResolver.cs | 18 + .../Stores/IOpenIddictApplicationStore.cs | 2 +- .../Stores/IOpenIddictAuthorizationStore.cs | 2 +- .../Stores/IOpenIddictScopeStore.cs | 2 +- .../Stores/IOpenIddictTokenStore.cs | 2 +- .../Managers/OpenIddictApplicationManager.cs | 15 +- .../OpenIddictAuthorizationManager.cs | 16 +- .../Managers/OpenIddictScopeManager.cs | 15 +- .../Managers/OpenIddictTokenManager.cs | 16 +- src/OpenIddict.Core/OpenIddict.Core.csproj | 4 + src/OpenIddict.Core/OpenIddictBuilder.cs | 327 ------ src/OpenIddict.Core/OpenIddictCoreBuilder.cs | 628 ++++++++++ .../OpenIddictCoreExtensions.cs | 73 ++ src/OpenIddict.Core/OpenIddictCoreHelpers.cs | 67 ++ src/OpenIddict.Core/OpenIddictCoreOptions.cs | 33 + src/OpenIddict.Core/OpenIddictExtensions.cs | 89 -- .../OpenIddictApplicationStoreResolver.cs | 44 + .../OpenIddictAuthorizationStoreResolver.cs | 44 + .../Resolvers/OpenIddictScopeStoreResolver.cs | 44 + .../Resolvers/OpenIddictTokenStoreResolver.cs | 44 + .../OpenIddict.EntityFramework.csproj | 1 + .../OpenIddictExtensions.cs | 161 +-- .../OpenIddictApplicationStoreResolver.cs | 65 ++ .../OpenIddictAuthorizationStoreResolver.cs | 65 ++ .../Resolvers/OpenIddictScopeStoreResolver.cs | 63 ++ .../Resolvers/OpenIddictTokenStoreResolver.cs | 65 ++ .../Stores/OpenIddictAuthorizationStore.cs | 2 +- .../Stores/OpenIddictTokenStore.cs | 2 +- .../OpenIddict.EntityFrameworkCore.csproj | 1 + .../OpenIddictExtensions.cs | 161 +-- .../OpenIddictApplicationStoreResolver.cs | 65 ++ .../OpenIddictAuthorizationStoreResolver.cs | 65 ++ .../Resolvers/OpenIddictScopeStoreResolver.cs | 63 ++ .../Resolvers/OpenIddictTokenStoreResolver.cs | 65 ++ .../Stores/OpenIddictAuthorizationStore.cs | 2 +- .../Stores/OpenIddictTokenStore.cs | 1 + src/OpenIddict.Mvc/OpenIddictExtensions.cs | 5 +- ...tHandler.cs => OpenIddictServerHandler.cs} | 8 +- ...izer.cs => OpenIddictServerInitializer.cs} | 36 +- ...penIddictServerProvider.Authentication.cs} | 14 +- ... => OpenIddictServerProvider.Discovery.cs} | 8 +- ...s => OpenIddictServerProvider.Exchange.cs} | 11 +- ...cs => OpenIddictServerProvider.Helpers.cs} | 14 +- ...OpenIddictServerProvider.Introspection.cs} | 8 +- ...=> OpenIddictServerProvider.Revocation.cs} | 10 +- ...OpenIddictServerProvider.Serialization.cs} | 16 +- ...cs => OpenIddictServerProvider.Session.cs} | 12 +- ...s => OpenIddictServerProvider.Userinfo.cs} | 4 +- ...rovider.cs => OpenIddictServerProvider.cs} | 13 +- src/OpenIddict.Server/OpenIddictExtensions.cs | 1008 ----------------- .../OpenIddictServerBuilder.cs | 653 +++++++++++ .../OpenIddictServerExtensions.cs | 123 ++ ...tOptions.cs => OpenIddictServerOptions.cs} | 28 +- .../OpenIddict.Stores.csproj | 3 +- .../Stores/OpenIddictApplicationStore.cs | 2 +- .../Stores/OpenIddictAuthorizationStore.cs | 2 +- .../Stores/OpenIddictScopeStore.cs | 2 +- .../Stores/OpenIddictTokenStore.cs | 2 +- src/OpenIddict/OpenIddictExtensions.cs | 84 +- .../OpenIddictBuilderTests.cs | 329 ------ .../OpenIddictCoreBuilderTests.cs | 487 ++++++++ .../OpenIddictExtensionsTests.cs | 48 - .../OpenIddictExtensionsTests.cs | 162 +-- .../OpenIddictExtensionsTests.cs | 162 +-- .../OpenIddict.Mvc.Tests.csproj | 1 + .../OpenIddictExtensionsTests.cs | 2 +- ...cs => OpenIddictServerInitializerTests.cs} | 10 +- ...dictServerProviderTests.Authentication.cs} | 6 +- ...penIddictServerProviderTests.Discovery.cs} | 7 +- ...OpenIddictServerProviderTests.Exchange.cs} | 5 +- ...ddictServerProviderTests.Introspection.cs} | 5 +- ...enIddictServerProviderTests.Revocation.cs} | 5 +- ...ddictServerProviderTests.Serialization.cs} | 5 +- ... OpenIddictServerProviderTests.Session.cs} | 5 +- ...OpenIddictServerProviderTests.Userinfo.cs} | 4 +- ...ts.cs => OpenIddictServerProviderTests.cs} | 113 +- ...sts.cs => OpenIddictServerBuilderTests.cs} | 44 +- .../OpenIddictExtensionsTests.cs | 36 +- 89 files changed, 3432 insertions(+), 2702 deletions(-) create mode 100644 src/OpenIddict.Abstractions/OpenIddict.Abstractions.csproj create mode 100644 src/OpenIddict.Abstractions/OpenIddictBuilder.cs rename src/{OpenIddict.Core => OpenIddict.Abstractions}/OpenIddictConstants.cs (99%) create mode 100644 src/OpenIddict.Abstractions/OpenIddictExtensions.cs create mode 100644 src/OpenIddict.Abstractions/Resolvers/IOpenIddictApplicationStoreResolver.cs create mode 100644 src/OpenIddict.Abstractions/Resolvers/IOpenIddictAuthorizationStoreResolver.cs create mode 100644 src/OpenIddict.Abstractions/Resolvers/IOpenIddictScopeStoreResolver.cs create mode 100644 src/OpenIddict.Abstractions/Resolvers/IOpenIddictTokenStoreResolver.cs rename src/{OpenIddict.Core => OpenIddict.Abstractions}/Stores/IOpenIddictApplicationStore.cs (99%) rename src/{OpenIddict.Core => OpenIddict.Abstractions}/Stores/IOpenIddictAuthorizationStore.cs (99%) rename src/{OpenIddict.Core => OpenIddict.Abstractions}/Stores/IOpenIddictScopeStore.cs (99%) rename src/{OpenIddict.Core => OpenIddict.Abstractions}/Stores/IOpenIddictTokenStore.cs (99%) delete mode 100644 src/OpenIddict.Core/OpenIddictBuilder.cs create mode 100644 src/OpenIddict.Core/OpenIddictCoreBuilder.cs create mode 100644 src/OpenIddict.Core/OpenIddictCoreExtensions.cs create mode 100644 src/OpenIddict.Core/OpenIddictCoreHelpers.cs create mode 100644 src/OpenIddict.Core/OpenIddictCoreOptions.cs delete mode 100644 src/OpenIddict.Core/OpenIddictExtensions.cs create mode 100644 src/OpenIddict.Core/Resolvers/OpenIddictApplicationStoreResolver.cs create mode 100644 src/OpenIddict.Core/Resolvers/OpenIddictAuthorizationStoreResolver.cs create mode 100644 src/OpenIddict.Core/Resolvers/OpenIddictScopeStoreResolver.cs create mode 100644 src/OpenIddict.Core/Resolvers/OpenIddictTokenStoreResolver.cs create mode 100644 src/OpenIddict.EntityFramework/Resolvers/OpenIddictApplicationStoreResolver.cs create mode 100644 src/OpenIddict.EntityFramework/Resolvers/OpenIddictAuthorizationStoreResolver.cs create mode 100644 src/OpenIddict.EntityFramework/Resolvers/OpenIddictScopeStoreResolver.cs create mode 100644 src/OpenIddict.EntityFramework/Resolvers/OpenIddictTokenStoreResolver.cs create mode 100644 src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictApplicationStoreResolver.cs create mode 100644 src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictAuthorizationStoreResolver.cs create mode 100644 src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictScopeStoreResolver.cs create mode 100644 src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictTokenStoreResolver.cs rename src/OpenIddict.Server/Internal/{OpenIddictHandler.cs => OpenIddictServerHandler.cs} (71%) rename src/OpenIddict.Server/Internal/{OpenIddictInitializer.cs => OpenIddictServerInitializer.cs} (89%) rename src/OpenIddict.Server/Internal/{OpenIddictProvider.Authentication.cs => OpenIddictServerProvider.Authentication.cs} (98%) rename src/OpenIddict.Server/Internal/{OpenIddictProvider.Discovery.cs => OpenIddictServerProvider.Discovery.cs} (92%) rename src/OpenIddict.Server/Internal/{OpenIddictProvider.Exchange.cs => OpenIddictServerProvider.Exchange.cs} (98%) rename src/OpenIddict.Server/Internal/{OpenIddictProvider.Helpers.cs => OpenIddictServerProvider.Helpers.cs} (98%) rename src/OpenIddict.Server/Internal/{OpenIddictProvider.Introspection.cs => OpenIddictServerProvider.Introspection.cs} (96%) rename src/OpenIddict.Server/Internal/{OpenIddictProvider.Revocation.cs => OpenIddictServerProvider.Revocation.cs} (96%) rename src/OpenIddict.Server/Internal/{OpenIddictProvider.Serialization.cs => OpenIddictServerProvider.Serialization.cs} (90%) rename src/OpenIddict.Server/Internal/{OpenIddictProvider.Session.cs => OpenIddictServerProvider.Session.cs} (96%) rename src/OpenIddict.Server/Internal/{OpenIddictProvider.Userinfo.cs => OpenIddictServerProvider.Userinfo.cs} (88%) rename src/OpenIddict.Server/Internal/{OpenIddictProvider.cs => OpenIddictServerProvider.cs} (95%) delete mode 100644 src/OpenIddict.Server/OpenIddictExtensions.cs create mode 100644 src/OpenIddict.Server/OpenIddictServerBuilder.cs create mode 100644 src/OpenIddict.Server/OpenIddictServerExtensions.cs rename src/OpenIddict.Server/{OpenIddictOptions.cs => OpenIddictServerOptions.cs} (85%) delete mode 100644 test/OpenIddict.Core.Tests/OpenIddictBuilderTests.cs create mode 100644 test/OpenIddict.Core.Tests/OpenIddictCoreBuilderTests.cs delete mode 100644 test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs rename test/OpenIddict.Server.Tests/Internal/{OpenIddictInitializerTests.cs => OpenIddictServerInitializerTests.cs} (97%) rename test/OpenIddict.Server.Tests/Internal/{OpenIddictProviderTests.Authentication.cs => OpenIddictServerProviderTests.Authentication.cs} (99%) rename test/OpenIddict.Server.Tests/Internal/{OpenIddictProviderTests.Discovery.cs => OpenIddictServerProviderTests.Discovery.cs} (98%) rename test/OpenIddict.Server.Tests/Internal/{OpenIddictProviderTests.Exchange.cs => OpenIddictServerProviderTests.Exchange.cs} (99%) rename test/OpenIddict.Server.Tests/Internal/{OpenIddictProviderTests.Introspection.cs => OpenIddictServerProviderTests.Introspection.cs} (99%) rename test/OpenIddict.Server.Tests/Internal/{OpenIddictProviderTests.Revocation.cs => OpenIddictServerProviderTests.Revocation.cs} (99%) rename test/OpenIddict.Server.Tests/Internal/{OpenIddictProviderTests.Serialization.cs => OpenIddictServerProviderTests.Serialization.cs} (99%) rename test/OpenIddict.Server.Tests/Internal/{OpenIddictProviderTests.Session.cs => OpenIddictServerProviderTests.Session.cs} (98%) rename test/OpenIddict.Server.Tests/Internal/{OpenIddictProviderTests.Userinfo.cs => OpenIddictServerProviderTests.Userinfo.cs} (92%) rename test/OpenIddict.Server.Tests/Internal/{OpenIddictProviderTests.cs => OpenIddictServerProviderTests.cs} (95%) rename test/OpenIddict.Server.Tests/{OpenIddictExtensionsTests.cs => OpenIddictServerBuilderTests.cs} (93%) diff --git a/OpenIddict.sln b/OpenIddict.sln index a3571712..538707b4 100644 --- a/OpenIddict.sln +++ b/OpenIddict.sln @@ -50,7 +50,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Stores", "src\Op EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Server", "src\OpenIddict.Server\OpenIddict.Server.csproj", "{21A7F241-CBE7-4F5C-9787-F2C50D135AEA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIddict.Server.Tests", "test\OpenIddict.Server.Tests\OpenIddict.Server.Tests.csproj", "{07B02B98-8A68-432D-A932-48E6D52B221A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Server.Tests", "test\OpenIddict.Server.Tests\OpenIddict.Server.Tests.csproj", "{07B02B98-8A68-432D-A932-48E6D52B221A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIddict.Abstractions", "src\OpenIddict.Abstractions\OpenIddict.Abstractions.csproj", "{886A16DA-C9CF-4979-9B38-D06DF8A714B6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -122,6 +124,10 @@ Global {07B02B98-8A68-432D-A932-48E6D52B221A}.Debug|Any CPU.Build.0 = Debug|Any CPU {07B02B98-8A68-432D-A932-48E6D52B221A}.Release|Any CPU.ActiveCfg = Release|Any CPU {07B02B98-8A68-432D-A932-48E6D52B221A}.Release|Any CPU.Build.0 = Release|Any CPU + {886A16DA-C9CF-4979-9B38-D06DF8A714B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {886A16DA-C9CF-4979-9B38-D06DF8A714B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {886A16DA-C9CF-4979-9B38-D06DF8A714B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {886A16DA-C9CF-4979-9B38-D06DF8A714B6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -143,6 +149,7 @@ Global {275D888A-B4C8-4E93-AC4B-B1AA25D98159} = {D544447C-D701-46BB-9A5B-C76C612A596B} {21A7F241-CBE7-4F5C-9787-F2C50D135AEA} = {D544447C-D701-46BB-9A5B-C76C612A596B} {07B02B98-8A68-432D-A932-48E6D52B221A} = {5FC71D6A-A994-4F62-977F-88A7D25379D7} + {886A16DA-C9CF-4979-9B38-D06DF8A714B6} = {D544447C-D701-46BB-9A5B-C76C612A596B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A710059F-0466-4D48-9B3A-0EF4F840B616} diff --git a/samples/Mvc.Server/Controllers/AuthorizationController.cs b/samples/Mvc.Server/Controllers/AuthorizationController.cs index 4ec3de0f..002d4e0f 100644 --- a/samples/Mvc.Server/Controllers/AuthorizationController.cs +++ b/samples/Mvc.Server/Controllers/AuthorizationController.cs @@ -5,7 +5,6 @@ */ using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.Security.Claims; using System.Threading.Tasks; @@ -20,6 +19,7 @@ using Mvc.Server.Helpers; using Mvc.Server.Models; using Mvc.Server.ViewModels.Authorization; using Mvc.Server.ViewModels.Shared; +using OpenIddict.Abstractions; using OpenIddict.Core; using OpenIddict.Models; @@ -67,7 +67,7 @@ namespace Mvc.Server // the original authorization request from the cache. return View(new AuthorizeViewModel { - ApplicationName = application.DisplayName, + ApplicationName = await _applicationManager.GetDisplayNameAsync(application), RequestId = request.RequestId, Scope = request.Scope }); diff --git a/samples/Mvc.Server/Controllers/UserinfoController.cs b/samples/Mvc.Server/Controllers/UserinfoController.cs index 643d7d2c..078bb33f 100644 --- a/samples/Mvc.Server/Controllers/UserinfoController.cs +++ b/samples/Mvc.Server/Controllers/UserinfoController.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Mvc.Server.Models; using Newtonsoft.Json.Linq; -using OpenIddict.Core; +using OpenIddict.Abstractions; namespace Mvc.Server.Controllers { diff --git a/samples/Mvc.Server/Startup.cs b/samples/Mvc.Server/Startup.cs index 6ad7131b..f2513442 100644 --- a/samples/Mvc.Server/Startup.cs +++ b/samples/Mvc.Server/Startup.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Mvc.Server.Models; using Mvc.Server.Services; +using OpenIddict.Abstractions; using OpenIddict.Core; using OpenIddict.Models; @@ -67,57 +68,66 @@ namespace Mvc.Server .AddOAuthValidation(); - // Register the OpenIddict services. - services.AddOpenIddict(options => - { - // Register the Entity Framework stores. - options.AddEntityFrameworkCoreStores(); - - // Register the ASP.NET Core MVC binder used by OpenIddict. - // Note: if you don't call this method, you won't be able to - // bind OpenIdConnectRequest or OpenIdConnectResponse parameters. - options.AddMvcBinders(); - - // Enable the authorization, logout, token and userinfo endpoints. - options.EnableAuthorizationEndpoint("/connect/authorize") - .EnableLogoutEndpoint("/connect/logout") - .EnableTokenEndpoint("/connect/token") - .EnableUserinfoEndpoint("/api/userinfo"); - - // Note: the Mvc.Client sample only uses the code flow and the password flow, but you - // can enable the other flows if you need to support implicit or client credentials. - options.AllowAuthorizationCodeFlow() - .AllowPasswordFlow() - .AllowRefreshTokenFlow(); - - // Mark the "email", "profile" and "roles" scopes as supported scopes. - options.RegisterScopes(OpenIdConnectConstants.Scopes.Email, - OpenIdConnectConstants.Scopes.Profile, - OpenIddictConstants.Scopes.Roles); - - // Make the "client_id" parameter mandatory when sending a token request. - options.RequireClientIdentification(); - - // When request caching is enabled, authorization and logout requests - // are stored in the distributed cache by OpenIddict and the user agent - // is redirected to the same page with a single parameter (request_id). - // This allows flowing large OpenID Connect requests even when using - // an external authentication provider like Google, Facebook or Twitter. - options.EnableRequestCaching(); - - // Enable scope validation, so that authorization and token requests - // that specify unregistered scopes are automatically rejected. - options.EnableScopeValidation(); - - // During development, you can disable the HTTPS requirement. - options.DisableHttpsRequirement(); - - // Note: to use JWT access tokens instead of the default - // encrypted format, the following lines are required: - // - // options.UseJsonWebTokens(); - // options.AddEphemeralSigningKey(); - }); + services.AddOpenIddict() + + // Register the OpenIddict core services. + .AddCore(options => + { + // Configure OpenIddict to use the default models. + options.UseDefaultModels(); + + // Register the Entity Framework stores. + options.AddEntityFrameworkCoreStores(); + }) + + // Register the OpenIddict server handler. + .AddServer(options => + { + // Register the ASP.NET Core MVC binder used by OpenIddict. + // Note: if you don't call this method, you won't be able to + // bind OpenIdConnectRequest or OpenIdConnectResponse parameters. + options.AddMvcBinders(); + + // Enable the authorization, logout, token and userinfo endpoints. + options.EnableAuthorizationEndpoint("/connect/authorize") + .EnableLogoutEndpoint("/connect/logout") + .EnableTokenEndpoint("/connect/token") + .EnableUserinfoEndpoint("/api/userinfo"); + + // Note: the Mvc.Client sample only uses the code flow and the password flow, but you + // can enable the other flows if you need to support implicit or client credentials. + options.AllowAuthorizationCodeFlow() + .AllowPasswordFlow() + .AllowRefreshTokenFlow(); + + // Mark the "email", "profile" and "roles" scopes as supported scopes. + options.RegisterScopes(OpenIdConnectConstants.Scopes.Email, + OpenIdConnectConstants.Scopes.Profile, + OpenIddictConstants.Scopes.Roles); + + // Make the "client_id" parameter mandatory when sending a token request. + options.RequireClientIdentification(); + + // When request caching is enabled, authorization and logout requests + // are stored in the distributed cache by OpenIddict and the user agent + // is redirected to the same page with a single parameter (request_id). + // This allows flowing large OpenID Connect requests even when using + // an external authentication provider like Google, Facebook or Twitter. + options.EnableRequestCaching(); + + // Enable scope validation, so that authorization and token requests + // that specify unregistered scopes are automatically rejected. + options.EnableScopeValidation(); + + // During development, you can disable the HTTPS requirement. + options.DisableHttpsRequirement(); + + // Note: to use JWT access tokens instead of the default + // encrypted format, the following lines are required: + // + // options.UseJsonWebTokens(); + // options.AddEphemeralSigningKey(); + }); services.AddTransient(); services.AddTransient(); diff --git a/src/OpenIddict.Abstractions/OpenIddict.Abstractions.csproj b/src/OpenIddict.Abstractions/OpenIddict.Abstractions.csproj new file mode 100644 index 00000000..08be8f9b --- /dev/null +++ b/src/OpenIddict.Abstractions/OpenIddict.Abstractions.csproj @@ -0,0 +1,23 @@ + + + + + + netstandard2.0 + + + + OpenIddict's abstractions. + Kévin Chalet + aspnetcore;authentication;jwt;openidconnect;openiddict;security + + + + + + + + + + + diff --git a/src/OpenIddict.Abstractions/OpenIddictBuilder.cs b/src/OpenIddict.Abstractions/OpenIddictBuilder.cs new file mode 100644 index 00000000..88acb436 --- /dev/null +++ b/src/OpenIddict.Abstractions/OpenIddictBuilder.cs @@ -0,0 +1,38 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using System.ComponentModel; +using JetBrains.Annotations; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Provides a shared entry point allowing to configure the OpenIddict services. + /// + public class OpenIddictBuilder + { + /// + /// Initializes a new instance of . + /// + /// The services collection. + public OpenIddictBuilder([NotNull] IServiceCollection services) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + Services = services; + } + + /// + /// Gets the services collection. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public IServiceCollection Services { get; } + } +} \ No newline at end of file diff --git a/src/OpenIddict.Core/OpenIddictConstants.cs b/src/OpenIddict.Abstractions/OpenIddictConstants.cs similarity index 99% rename from src/OpenIddict.Core/OpenIddictConstants.cs rename to src/OpenIddict.Abstractions/OpenIddictConstants.cs index c4185636..8f47c7fa 100644 --- a/src/OpenIddict.Core/OpenIddictConstants.cs +++ b/src/OpenIddict.Abstractions/OpenIddictConstants.cs @@ -4,7 +4,7 @@ * the license and the contributors participating to this project. */ -namespace OpenIddict.Core +namespace OpenIddict.Abstractions { public static class OpenIddictConstants { diff --git a/src/OpenIddict.Abstractions/OpenIddictExtensions.cs b/src/OpenIddict.Abstractions/OpenIddictExtensions.cs new file mode 100644 index 00000000..b23473fe --- /dev/null +++ b/src/OpenIddict.Abstractions/OpenIddictExtensions.cs @@ -0,0 +1,56 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using JetBrains.Annotations; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class OpenIddictExtensions + { + /// + /// Provides a common entry point for registering the OpenIddict services. + /// + /// The services collection. + /// This extension can be safely called multiple times. + /// The . + public static OpenIddictBuilder AddOpenIddict([NotNull] this IServiceCollection services) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + return new OpenIddictBuilder(services); + } + + /// + /// Provides a common entry point for registering the OpenIddict services. + /// + /// The services collection. + /// The configuration delegate used to register new services. + /// This extension can be safely called multiple times. + /// The . + public static IServiceCollection AddOpenIddict( + [NotNull] this IServiceCollection services, + [NotNull] Action configuration) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + if (configuration == null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + configuration(services.AddOpenIddict()); + + return services; + } + } +} \ No newline at end of file diff --git a/src/OpenIddict.Abstractions/Resolvers/IOpenIddictApplicationStoreResolver.cs b/src/OpenIddict.Abstractions/Resolvers/IOpenIddictApplicationStoreResolver.cs new file mode 100644 index 00000000..1724a255 --- /dev/null +++ b/src/OpenIddict.Abstractions/Resolvers/IOpenIddictApplicationStoreResolver.cs @@ -0,0 +1,18 @@ +using System; + +namespace OpenIddict.Abstractions +{ + /// + /// Exposes a method allowing to resolve an application store. + /// + public interface IOpenIddictApplicationStoreResolver + { + /// + /// Returns an application store compatible with the specified application type or throws an + /// if no store can be built using the specified type. + /// + /// The type of the Application entity. + /// An . + IOpenIddictApplicationStore Get() where TApplication : class; + } +} diff --git a/src/OpenIddict.Abstractions/Resolvers/IOpenIddictAuthorizationStoreResolver.cs b/src/OpenIddict.Abstractions/Resolvers/IOpenIddictAuthorizationStoreResolver.cs new file mode 100644 index 00000000..769ecd24 --- /dev/null +++ b/src/OpenIddict.Abstractions/Resolvers/IOpenIddictAuthorizationStoreResolver.cs @@ -0,0 +1,18 @@ +using System; + +namespace OpenIddict.Abstractions +{ + /// + /// Exposes a method allowing to resolve an authorization store. + /// + public interface IOpenIddictAuthorizationStoreResolver + { + /// + /// Returns an authorization store compatible with the specified authorization type or throws an + /// if no store can be built using the specified type. + /// + /// The type of the Authorization entity. + /// An . + IOpenIddictAuthorizationStore Get() where TAuthorization : class; + } +} diff --git a/src/OpenIddict.Abstractions/Resolvers/IOpenIddictScopeStoreResolver.cs b/src/OpenIddict.Abstractions/Resolvers/IOpenIddictScopeStoreResolver.cs new file mode 100644 index 00000000..9da948de --- /dev/null +++ b/src/OpenIddict.Abstractions/Resolvers/IOpenIddictScopeStoreResolver.cs @@ -0,0 +1,18 @@ +using System; + +namespace OpenIddict.Abstractions +{ + /// + /// Exposes a method allowing to resolve a scope store. + /// + public interface IOpenIddictScopeStoreResolver + { + /// + /// Returns a scope store compatible with the specified scope type or throws an + /// if no store can be built using the specified type. + /// + /// The type of the Scope entity. + /// An . + IOpenIddictScopeStore Get() where TScope : class; + } +} diff --git a/src/OpenIddict.Abstractions/Resolvers/IOpenIddictTokenStoreResolver.cs b/src/OpenIddict.Abstractions/Resolvers/IOpenIddictTokenStoreResolver.cs new file mode 100644 index 00000000..2bd5f8de --- /dev/null +++ b/src/OpenIddict.Abstractions/Resolvers/IOpenIddictTokenStoreResolver.cs @@ -0,0 +1,18 @@ +using System; + +namespace OpenIddict.Abstractions +{ + /// + /// Exposes a method allowing to resolve a token store. + /// + public interface IOpenIddictTokenStoreResolver + { + /// + /// Returns a token store compatible with the specified token type or throws an + /// if no store can be built using the specified type. + /// + /// The type of the Token entity. + /// An . + IOpenIddictTokenStore Get() where TToken : class; + } +} diff --git a/src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs b/src/OpenIddict.Abstractions/Stores/IOpenIddictApplicationStore.cs similarity index 99% rename from src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs rename to src/OpenIddict.Abstractions/Stores/IOpenIddictApplicationStore.cs index c9f023e7..11b5ed90 100644 --- a/src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs +++ b/src/OpenIddict.Abstractions/Stores/IOpenIddictApplicationStore.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using Newtonsoft.Json.Linq; -namespace OpenIddict.Core +namespace OpenIddict.Abstractions { /// /// Provides methods allowing to manage the applications stored in a database. diff --git a/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs b/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs similarity index 99% rename from src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs rename to src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs index 95dd042d..81a65523 100644 --- a/src/OpenIddict.Core/Stores/IOpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.Abstractions/Stores/IOpenIddictAuthorizationStore.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using Newtonsoft.Json.Linq; -namespace OpenIddict.Core +namespace OpenIddict.Abstractions { /// /// Provides methods allowing to manage the authorizations stored in a database. diff --git a/src/OpenIddict.Core/Stores/IOpenIddictScopeStore.cs b/src/OpenIddict.Abstractions/Stores/IOpenIddictScopeStore.cs similarity index 99% rename from src/OpenIddict.Core/Stores/IOpenIddictScopeStore.cs rename to src/OpenIddict.Abstractions/Stores/IOpenIddictScopeStore.cs index 996405db..899669cb 100644 --- a/src/OpenIddict.Core/Stores/IOpenIddictScopeStore.cs +++ b/src/OpenIddict.Abstractions/Stores/IOpenIddictScopeStore.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using Newtonsoft.Json.Linq; -namespace OpenIddict.Core +namespace OpenIddict.Abstractions { /// /// Provides methods allowing to manage the scopes stored in a database. diff --git a/src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs b/src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs similarity index 99% rename from src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs rename to src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs index 59f4cef3..b4d718e4 100644 --- a/src/OpenIddict.Core/Stores/IOpenIddictTokenStore.cs +++ b/src/OpenIddict.Abstractions/Stores/IOpenIddictTokenStore.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using Newtonsoft.Json.Linq; -namespace OpenIddict.Core +namespace OpenIddict.Abstractions { /// /// Provides methods allowing to manage the tokens stored in a database. diff --git a/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs index c52bf4b8..ddb20a09 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs +++ b/src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs @@ -13,6 +13,8 @@ using System.Threading.Tasks; using CryptoHelper; using JetBrains.Annotations; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using OpenIddict.Abstractions; namespace OpenIddict.Core { @@ -23,11 +25,13 @@ namespace OpenIddict.Core public class OpenIddictApplicationManager where TApplication : class { public OpenIddictApplicationManager( - [NotNull] IOpenIddictApplicationStore store, - [NotNull] ILogger> logger) + [NotNull] IOpenIddictApplicationStoreResolver resolver, + [NotNull] ILogger> logger, + [NotNull] IOptionsMonitor options) { - Store = store; + Store = resolver.Get(); Logger = logger; + Options = options; } /// @@ -35,6 +39,11 @@ namespace OpenIddict.Core /// protected ILogger Logger { get; } + /// + /// Gets the options associated with the current manager. + /// + protected IOptionsMonitor Options { get; } + /// /// Gets the store associated with the current manager. /// diff --git a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs index 2c6d9116..989f8242 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs +++ b/src/OpenIddict.Core/Managers/OpenIddictAuthorizationManager.cs @@ -5,7 +5,6 @@ */ using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -14,6 +13,8 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using OpenIddict.Abstractions; namespace OpenIddict.Core { @@ -24,11 +25,13 @@ namespace OpenIddict.Core public class OpenIddictAuthorizationManager where TAuthorization : class { public OpenIddictAuthorizationManager( - [NotNull] IOpenIddictAuthorizationStore store, - [NotNull] ILogger> logger) + [NotNull] IOpenIddictAuthorizationStoreResolver resolver, + [NotNull] ILogger> logger, + [NotNull] IOptionsMonitor options) { + Store = resolver.Get(); Logger = logger; - Store = store; + Options = options; } /// @@ -36,6 +39,11 @@ namespace OpenIddict.Core /// protected ILogger Logger { get; } + /// + /// Gets the options associated with the current manager. + /// + protected IOptionsMonitor Options { get; } + /// /// Gets the store associated with the current manager. /// diff --git a/src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs b/src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs index de70b00c..23ccb595 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs +++ b/src/OpenIddict.Core/Managers/OpenIddictScopeManager.cs @@ -13,6 +13,8 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using OpenIddict.Abstractions; namespace OpenIddict.Core { @@ -23,11 +25,13 @@ namespace OpenIddict.Core public class OpenIddictScopeManager where TScope : class { public OpenIddictScopeManager( - [NotNull] IOpenIddictScopeStore store, - [NotNull] ILogger> logger) + [NotNull] IOpenIddictScopeStoreResolver resolver, + [NotNull] ILogger> logger, + [NotNull] IOptionsMonitor options) { + Store = resolver.Get(); Logger = logger; - Store = store; + Options = options; } /// @@ -35,6 +39,11 @@ namespace OpenIddict.Core /// protected ILogger Logger { get; } + /// + /// Gets the options associated with the current manager. + /// + protected IOptionsMonitor Options { get; } + /// /// Gets the store associated with the current manager. /// diff --git a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs index 0b2a97f3..e97ef7ac 100644 --- a/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs +++ b/src/OpenIddict.Core/Managers/OpenIddictTokenManager.cs @@ -5,7 +5,6 @@ */ using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -15,6 +14,8 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using OpenIddict.Abstractions; namespace OpenIddict.Core { @@ -25,11 +26,13 @@ namespace OpenIddict.Core public class OpenIddictTokenManager where TToken : class { public OpenIddictTokenManager( - [NotNull] IOpenIddictTokenStore store, - [NotNull] ILogger> logger) + [NotNull] IOpenIddictTokenStoreResolver resolver, + [NotNull] ILogger> logger, + [NotNull] IOptionsMonitor options) { + Store = resolver.Get(); Logger = logger; - Store = store; + Options = options; } /// @@ -37,6 +40,11 @@ namespace OpenIddict.Core /// protected ILogger Logger { get; } + /// + /// Gets the options associated with the current manager. + /// + protected IOptionsMonitor Options { get; } + /// /// Gets the store associated with the current manager. /// diff --git a/src/OpenIddict.Core/OpenIddict.Core.csproj b/src/OpenIddict.Core/OpenIddict.Core.csproj index 1573cb23..5df1ad29 100644 --- a/src/OpenIddict.Core/OpenIddict.Core.csproj +++ b/src/OpenIddict.Core/OpenIddict.Core.csproj @@ -12,6 +12,10 @@ aspnetcore;authentication;jwt;openidconnect;openiddict;security + + + + diff --git a/src/OpenIddict.Core/OpenIddictBuilder.cs b/src/OpenIddict.Core/OpenIddictBuilder.cs deleted file mode 100644 index 888ce3d3..00000000 --- a/src/OpenIddict.Core/OpenIddictBuilder.cs +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * See https://github.com/openiddict/openiddict-core for more information concerning - * the license and the contributors participating to this project. - */ - -using System; -using System.ComponentModel; -using JetBrains.Annotations; -using OpenIddict.Core; - -namespace Microsoft.Extensions.DependencyInjection -{ - /// - /// Exposes the necessary methods required to configure OpenIddict. - /// - public class OpenIddictBuilder - { - /// - /// Initializes a new instance of . - /// - /// The services collection. - public OpenIddictBuilder([NotNull] IServiceCollection services) - { - if (services == null) - { - throw new ArgumentNullException(nameof(services)); - } - - Services = services; - } - - /// - /// Gets or sets the type corresponding to the Application entity. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public Type ApplicationType { get; set; } - - /// - /// Gets or sets the type corresponding to the Authorization entity. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public Type AuthorizationType { get; set; } - - /// - /// Gets or sets the type corresponding to the Scope entity. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public Type ScopeType { get; set; } - - /// - /// Gets or sets the type corresponding to the Token entity. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public Type TokenType { get; set; } - - /// - /// Gets the services collection. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public IServiceCollection Services { get; } - - /// - /// Adds a custom application manager derived from - /// . - /// - /// The type of the custom manager. - /// The . - public OpenIddictBuilder AddApplicationManager() where TManager : class - => AddApplicationManager(typeof(TManager)); - - /// - /// Adds a custom application manager derived from - /// . - /// - /// The type of the custom manager. - /// The . - public virtual OpenIddictBuilder AddApplicationManager([NotNull] Type type) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - var contract = typeof(OpenIddictApplicationManager<>).MakeGenericType(ApplicationType); - if (!contract.IsAssignableFrom(type)) - { - throw new InvalidOperationException("The specified type is invalid."); - } - - Services.AddScoped(contract, type); - - return this; - } - - /// - /// Adds a custom application store derived from - /// . - /// - /// The type of the custom store. - /// The . - public OpenIddictBuilder AddApplicationStore() where TStore : class - => AddApplicationStore(typeof(TStore)); - - /// - /// Adds a custom application store derived from - /// . - /// - /// The type of the custom store. - /// The . - public virtual OpenIddictBuilder AddApplicationStore([NotNull] Type type) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - var contract = typeof(IOpenIddictApplicationStore<>).MakeGenericType(ApplicationType); - if (!contract.IsAssignableFrom(type)) - { - throw new InvalidOperationException("The specified type is invalid."); - } - - Services.AddScoped(contract, type); - - return this; - } - - /// - /// Adds a custom authorization manager derived from - /// . - /// - /// The type of the custom manager. - /// The . - public OpenIddictBuilder AddAuthorizationManager() where TManager : class - => AddAuthorizationManager(typeof(TManager)); - - /// - /// Adds a custom authorization manager derived from - /// . - /// - /// The type of the custom manager. - /// The . - public virtual OpenIddictBuilder AddAuthorizationManager([NotNull] Type type) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - var contract = typeof(OpenIddictAuthorizationManager<>).MakeGenericType(AuthorizationType); - if (!contract.IsAssignableFrom(type)) - { - throw new InvalidOperationException("The specified type is invalid."); - } - - Services.AddScoped(contract, type); - - return this; - } - - /// - /// Adds a custom authorization store derived from - /// . - /// - /// The type of the custom store. - /// The . - public OpenIddictBuilder AddAuthorizationStore() where TStore : class - => AddAuthorizationStore(typeof(TStore)); - - /// - /// Adds a custom authorization store derived from - /// . - /// - /// The type of the custom store. - /// The . - public virtual OpenIddictBuilder AddAuthorizationStore([NotNull] Type type) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - var contract = typeof(IOpenIddictAuthorizationStore<>).MakeGenericType(AuthorizationType); - if (!contract.IsAssignableFrom(type)) - { - throw new InvalidOperationException("The specified type is invalid."); - } - - Services.AddScoped(contract, type); - - return this; - } - - /// - /// Adds a custom scope manager derived from - /// . - /// - /// The type of the custom manager. - /// The . - public OpenIddictBuilder AddScopeManager() where TManager : class - => AddScopeManager(typeof(TManager)); - - /// - /// Adds a custom scope manager derived from - /// . - /// - /// The type of the custom manager. - /// The . - public virtual OpenIddictBuilder AddScopeManager([NotNull] Type type) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - var contract = typeof(OpenIddictScopeManager<>).MakeGenericType(ScopeType); - if (!contract.IsAssignableFrom(type)) - { - throw new InvalidOperationException("The specified type is invalid."); - } - - Services.AddScoped(contract, type); - - return this; - } - - /// - /// Adds a custom scope store derived from - /// . - /// - /// The type of the custom store. - /// The . - public OpenIddictBuilder AddScopeStore() where TStore : class - => AddScopeStore(typeof(TStore)); - - /// - /// Adds a custom scope store derived from - /// . - /// - /// The type of the custom store. - /// The . - public virtual OpenIddictBuilder AddScopeStore([NotNull] Type type) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - var contract = typeof(IOpenIddictScopeStore<>).MakeGenericType(ScopeType); - if (!contract.IsAssignableFrom(type)) - { - throw new InvalidOperationException("The specified type is invalid."); - } - - Services.AddScoped(contract, type); - - return this; - } - - /// - /// Adds a custom token manager derived from - /// . - /// - /// The type of the custom manager. - /// The . - public OpenIddictBuilder AddTokenManager() where TManager : class - => AddTokenManager(typeof(TManager)); - - /// - /// Adds a custom token manager derived from - /// . - /// - /// The type of the custom manager. - /// The . - public virtual OpenIddictBuilder AddTokenManager([NotNull] Type type) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - var contract = typeof(OpenIddictTokenManager<>).MakeGenericType(TokenType); - if (!contract.IsAssignableFrom(type)) - { - throw new InvalidOperationException("The specified type is invalid."); - } - - Services.AddScoped(contract, type); - - return this; - } - - /// - /// Adds a custom token store derived from - /// . - /// - /// The type of the custom store. - /// The . - public OpenIddictBuilder AddTokenStore() where TStore : class - => AddTokenStore(typeof(TStore)); - - /// - /// Adds a custom token store derived from - /// . - /// - /// The type of the custom store. - /// The . - public virtual OpenIddictBuilder AddTokenStore([NotNull] Type type) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - var contract = typeof(IOpenIddictTokenStore<>).MakeGenericType(TokenType); - if (!contract.IsAssignableFrom(type)) - { - throw new InvalidOperationException("The specified type is invalid."); - } - - Services.AddScoped(contract, type); - - return this; - } - } -} \ No newline at end of file diff --git a/src/OpenIddict.Core/OpenIddictCoreBuilder.cs b/src/OpenIddict.Core/OpenIddictCoreBuilder.cs new file mode 100644 index 00000000..449063ea --- /dev/null +++ b/src/OpenIddict.Core/OpenIddictCoreBuilder.cs @@ -0,0 +1,628 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using System.ComponentModel; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection.Extensions; +using OpenIddict.Abstractions; +using OpenIddict.Core; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Exposes the necessary methods required to configure the OpenIddict core services. + /// + public class OpenIddictCoreBuilder + { + /// + /// Initializes a new instance of . + /// + /// The services collection. + public OpenIddictCoreBuilder([NotNull] IServiceCollection services) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + Services = services; + } + + /// + /// Gets the services collection. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public IServiceCollection Services { get; } + + /// + /// Amends the default OpenIddict core configuration. + /// + /// The delegate used to configure the OpenIddict options. + /// This extension can be safely called multiple times. + /// The . + public OpenIddictCoreBuilder Configure([NotNull] Action configuration) + { + if (configuration == null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + Services.Configure(configuration); + + return this; + } + + /// + /// Adds a custom application store by a custom implementation derived + /// from . + /// Note: when using this overload, the application store + /// must be either a non-generic or closed generic service. + /// + /// The type of the custom store. + /// The . + public OpenIddictCoreBuilder AddApplicationStore() where TStore : class + => AddApplicationStore(typeof(TStore)); + + /// + /// Adds a custom application store by a custom implementation derived + /// from . + /// Note: when using this overload, the application store can be + /// either a non-generic, a closed or an open generic service. + /// + /// The type of the custom store. + /// The . + public OpenIddictCoreBuilder AddApplicationStore([NotNull] Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(IOpenIddictApplicationStore<>)); + if (root == null) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + // Note: managers can be either open generics (e.g OpenIddictApplicationStore<>) + // or closed generics (e.g OpenIddictApplicationStore). + if (type.IsGenericTypeDefinition) + { + if (type.GetGenericArguments().Length != 1) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + Services.Replace(ServiceDescriptor.Scoped(typeof(IOpenIddictApplicationStore<>), type)); + } + + else + { + Services.Replace(ServiceDescriptor.Scoped(typeof(IOpenIddictApplicationStore<>) + .MakeGenericType(root.GenericTypeArguments[0]), type)); + } + + return this; + } + + /// + /// Adds a custom authorization store by a custom implementation derived + /// from . + /// Note: when using this overload, the authorization store + /// must be either a non-generic or closed generic service. + /// + /// The type of the custom store. + /// The . + public OpenIddictCoreBuilder AddAuthorizationStore() where TStore : class + => AddAuthorizationStore(typeof(TStore)); + + /// + /// Adds a custom authorization store by a custom implementation derived + /// from . + /// Note: when using this overload, the authorization store can be + /// either a non-generic, a closed or an open generic service. + /// + /// The type of the custom store. + /// The . + public OpenIddictCoreBuilder AddAuthorizationStore([NotNull] Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(IOpenIddictAuthorizationStore<>)); + if (root == null) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + // Note: managers can be either open generics (e.g OpenIddictAuthorizationStore<>) + // or closed generics (e.g OpenIddictAuthorizationStore). + if (type.IsGenericTypeDefinition) + { + if (type.GetGenericArguments().Length != 1) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + Services.Replace(ServiceDescriptor.Scoped(typeof(IOpenIddictAuthorizationStore<>), type)); + } + + else + { + Services.Replace(ServiceDescriptor.Scoped(typeof(IOpenIddictAuthorizationStore<>) + .MakeGenericType(root.GenericTypeArguments[0]), type)); + } + + return this; + } + + /// + /// Adds a custom scope store by a custom implementation derived + /// from . + /// Note: when using this overload, the scope store + /// must be either a non-generic or closed generic service. + /// + /// The type of the custom store. + /// The . + public OpenIddictCoreBuilder AddScopeStore() where TStore : class + => AddScopeStore(typeof(TStore)); + + /// + /// Adds a custom scope store by a custom implementation derived + /// from . + /// Note: when using this overload, the scope store can be + /// either a non-generic, a closed or an open generic service. + /// + /// The type of the custom store. + /// The . + public OpenIddictCoreBuilder AddScopeStore([NotNull] Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(IOpenIddictScopeStore<>)); + if (root == null) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + // Note: managers can be either open generics (e.g OpenIddictScopeStore<>) + // or closed generics (e.g OpenIddictScopeStore). + if (type.IsGenericTypeDefinition) + { + if (type.GetGenericArguments().Length != 1) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + Services.Replace(ServiceDescriptor.Scoped(typeof(IOpenIddictScopeStore<>), type)); + } + + else + { + Services.Replace(ServiceDescriptor.Scoped(typeof(IOpenIddictScopeStore<>) + .MakeGenericType(root.GenericTypeArguments[0]), type)); + } + + return this; + } + + /// + /// Adds a custom token store by a custom implementation derived + /// from . + /// Note: when using this overload, the token store + /// must be either a non-generic or closed generic service. + /// + /// The type of the custom store. + /// The . + public OpenIddictCoreBuilder AddTokenStore() where TStore : class + => AddTokenStore(typeof(TStore)); + + /// + /// Adds a custom token store by a custom implementation derived + /// from . + /// Note: when using this overload, the token store can be + /// either a non-generic, a closed or an open generic service. + /// + /// The type of the custom store. + /// The . + public OpenIddictCoreBuilder AddTokenStore([NotNull] Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(IOpenIddictTokenStore<>)); + if (root == null) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + // Note: managers can be either open generics (e.g OpenIddictTokenStore<>) + // or closed generics (e.g OpenIddictTokenStore). + if (type.IsGenericTypeDefinition) + { + if (type.GetGenericArguments().Length != 1) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + Services.Replace(ServiceDescriptor.Scoped(typeof(IOpenIddictTokenStore<>), type)); + } + + else + { + Services.Replace(ServiceDescriptor.Scoped(typeof(IOpenIddictTokenStore<>) + .MakeGenericType(root.GenericTypeArguments[0]), type)); + } + + return this; + } + + /// + /// Replace the default application manager by a custom manager derived + /// from . + /// Note: when using this overload, the application manager can be + /// either a non-generic, a closed or an open generic service. + /// + /// The type of the custom manager. + /// The . + public OpenIddictCoreBuilder ReplaceApplicationManager([NotNull] Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(OpenIddictApplicationManager<>)); + if (root == null) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + // Note: managers can be either open generics (e.g OpenIddictApplicationManager<>) + // or closed generics (e.g OpenIddictApplicationManager). + if (type.IsGenericTypeDefinition) + { + if (type.GetGenericArguments().Length != 1) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + Services.Replace(ServiceDescriptor.Scoped(typeof(OpenIddictApplicationManager<>), type)); + } + + else + { + Services.Replace(ServiceDescriptor.Scoped(typeof(OpenIddictApplicationManager<>) + .MakeGenericType(root.GenericTypeArguments[0]), type)); + } + + return this; + } + + /// + /// Replaces the default application store resolver by a custom implementation. + /// + /// The type of the custom store. + /// The . + public OpenIddictCoreBuilder ReplaceApplicationStoreResolver() + where TResolver : IOpenIddictApplicationStoreResolver + => ReplaceApplicationStoreResolver(typeof(TResolver)); + + /// + /// Replaces the default application store resolver by a custom implementation. + /// + /// The type of the custom store. + /// The . + public OpenIddictCoreBuilder ReplaceApplicationStoreResolver([NotNull] Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (!typeof(IOpenIddictApplicationStoreResolver).IsAssignableFrom(type)) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + Services.Replace(ServiceDescriptor.Scoped(typeof(IOpenIddictApplicationStoreResolver), type)); + + return this; + } + + /// + /// Replace the default application manager by a custom manager derived + /// from . + /// Note: when using this overload, the application manager + /// must be either a non-generic or closed generic service. + /// + /// The type of the custom manager. + /// The . + public OpenIddictCoreBuilder ReplaceApplicationManager() where TManager : class + => ReplaceApplicationManager(typeof(TManager)); + + /// + /// Replace the default authorization manager by a custom manager derived + /// from . + /// Note: when using this overload, the authorization manager + /// must be either a non-generic or closed generic service. + /// + /// The type of the custom manager. + /// The . + public OpenIddictCoreBuilder ReplaceAuthorizationManager() where TManager : class + => ReplaceAuthorizationManager(typeof(TManager)); + + /// + /// Replace the default authorization manager by a custom manager derived + /// from . + /// Note: when using this overload, the authorization manager can be + /// either a non-generic, a closed or an open generic service. + /// + /// The type of the custom manager. + /// The . + public OpenIddictCoreBuilder ReplaceAuthorizationManager([NotNull] Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(OpenIddictAuthorizationManager<>)); + if (root == null) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + // Note: managers can be either open generics (e.g OpenIddictAuthorizationManager<>) + // or closed generics (e.g OpenIddictAuthorizationManager). + if (type.IsGenericTypeDefinition) + { + if (type.GetGenericArguments().Length != 1) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + Services.Replace(ServiceDescriptor.Scoped(typeof(OpenIddictAuthorizationManager<>), type)); + } + + else + { + Services.Replace(ServiceDescriptor.Scoped(typeof(OpenIddictAuthorizationManager<>) + .MakeGenericType(root.GenericTypeArguments[0]), type)); + } + + return this; + } + + /// + /// Replaces the default authorization store resolver by a custom implementation. + /// + /// The type of the custom store. + /// The . + public OpenIddictCoreBuilder ReplaceAuthorizationStoreResolver() + where TResolver : IOpenIddictAuthorizationStoreResolver + => ReplaceAuthorizationStoreResolver(typeof(TResolver)); + + /// + /// Replaces the default authorization store resolver by a custom implementation. + /// + /// The type of the custom store. + /// The . + public OpenIddictCoreBuilder ReplaceAuthorizationStoreResolver([NotNull] Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (!typeof(IOpenIddictAuthorizationStoreResolver).IsAssignableFrom(type)) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + Services.Replace(ServiceDescriptor.Scoped(typeof(IOpenIddictAuthorizationStoreResolver), type)); + + return this; + } + + /// + /// Replace the default scope manager by a custom manager + /// derived from . + /// Note: when using this overload, the scope manager + /// must be either a non-generic or closed generic service. + /// + /// The type of the custom manager. + /// The . + public OpenIddictCoreBuilder ReplaceScopeManager() where TManager : class + => ReplaceScopeManager(typeof(TManager)); + + /// + /// Replace the default scope manager by a custom manager + /// derived from . + /// Note: when using this overload, the scope manager can be + /// either a non-generic, a closed or an open generic service. + /// + /// The type of the custom manager. + /// The . + public OpenIddictCoreBuilder ReplaceScopeManager([NotNull] Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(OpenIddictScopeManager<>)); + if (root == null) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + // Note: managers can be either open generics (e.g OpenIddictScopeManager<>) + // or closed generics (e.g OpenIddictScopeManager). + if (type.IsGenericTypeDefinition) + { + if (type.GetGenericArguments().Length != 1) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + Services.Replace(ServiceDescriptor.Scoped(typeof(OpenIddictScopeManager<>), type)); + } + + else + { + Services.Replace(ServiceDescriptor.Scoped(typeof(OpenIddictScopeManager<>) + .MakeGenericType(root.GenericTypeArguments[0]), type)); + } + + return this; + } + + /// + /// Replaces the default scope store resolver by a custom implementation. + /// + /// The type of the custom store. + /// The . + public OpenIddictCoreBuilder ReplaceScopeStoreResolver() + where TResolver : IOpenIddictScopeStoreResolver + => ReplaceScopeStoreResolver(typeof(TResolver)); + + /// + /// Replaces the default scope store resolver by a custom implementation. + /// + /// The type of the custom store. + /// The . + public OpenIddictCoreBuilder ReplaceScopeStoreResolver([NotNull] Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (!typeof(IOpenIddictScopeStoreResolver).IsAssignableFrom(type)) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + Services.Replace(ServiceDescriptor.Scoped(typeof(IOpenIddictScopeStoreResolver), type)); + + return this; + } + + /// + /// Replace the default token manager by a custom manager + /// derived from . + /// Note: when using this overload, the token manager + /// must be either a non-generic or closed generic service. + /// + /// The type of the custom manager. + /// The . + public OpenIddictCoreBuilder ReplaceTokenManager() where TManager : class + => ReplaceTokenManager(typeof(TManager)); + + /// + /// Replace the default token manager by a custom manager + /// derived from . + /// Note: when using this overload, the token manager can be + /// either a non-generic, a closed or an open generic service. + /// + /// The type of the custom manager. + /// The . + public OpenIddictCoreBuilder ReplaceTokenManager([NotNull] Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + var root = OpenIddictCoreHelpers.FindGenericBaseType(type, typeof(OpenIddictTokenManager<>)); + if (root == null) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + // Note: managers can be either open generics (e.g OpenIddictTokenManager<>) + // or closed generics (e.g OpenIddictTokenManager). + if (type.IsGenericTypeDefinition) + { + if (type.GetGenericArguments().Length != 1) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + Services.Replace(ServiceDescriptor.Scoped(typeof(OpenIddictTokenManager<>), type)); + } + + else + { + Services.Replace(ServiceDescriptor.Scoped(typeof(OpenIddictTokenManager<>) + .MakeGenericType(root.GenericTypeArguments[0]), type)); + } + + return this; + } + + /// + /// Replaces the default token store resolver by a custom implementation. + /// + /// The type of the custom store. + /// The . + public OpenIddictCoreBuilder ReplaceTokenStoreResolver() + where TResolver : IOpenIddictTokenStoreResolver + => ReplaceTokenStoreResolver(typeof(TResolver)); + + /// + /// Replaces the default token store resolver by a custom implementation. + /// + /// The type of the custom store. + /// The . + public OpenIddictCoreBuilder ReplaceTokenStoreResolver([NotNull] Type type) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (!typeof(IOpenIddictTokenStoreResolver).IsAssignableFrom(type)) + { + throw new ArgumentException("The specified type is invalid.", nameof(type)); + } + + Services.Replace(ServiceDescriptor.Scoped(typeof(IOpenIddictTokenStoreResolver), type)); + + return this; + } + + /// + /// Configures OpenIddict to use the specified entities. + /// + /// The type corresponding to the Application entity. + /// The type corresponding to the Authorization entity. + /// The type corresponding to the Scope entity. + /// The type corresponding to the Token entity. + /// The . + public OpenIddictCoreBuilder UseCustomModels() + where TApplication : class + where TAuthorization : class + where TScope : class + where TToken : class + => Configure(options => + { + options.DefaultApplicationType = typeof(TApplication); + options.DefaultAuthorizationType = typeof(TAuthorization); + options.DefaultScopeType = typeof(TScope); + options.DefaultTokenType = typeof(TToken); + }); + } +} \ No newline at end of file diff --git a/src/OpenIddict.Core/OpenIddictCoreExtensions.cs b/src/OpenIddict.Core/OpenIddictCoreExtensions.cs new file mode 100644 index 00000000..774da9c3 --- /dev/null +++ b/src/OpenIddict.Core/OpenIddictCoreExtensions.cs @@ -0,0 +1,73 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection.Extensions; +using OpenIddict.Abstractions; +using OpenIddict.Core; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class OpenIddictCoreExtensions + { + /// + /// Registers the OpenIddict core services in the DI container. + /// + /// The services builder used by OpenIddict to register new services. + /// This extension can be safely called multiple times. + /// The . + public static OpenIddictCoreBuilder AddCore([NotNull] this OpenIddictBuilder builder) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + builder.Services.AddDistributedMemoryCache(); + builder.Services.AddMemoryCache(); + builder.Services.AddOptions(); + + builder.Services.TryAddScoped(typeof(OpenIddictApplicationManager<>)); + builder.Services.TryAddScoped(typeof(OpenIddictAuthorizationManager<>)); + builder.Services.TryAddScoped(typeof(OpenIddictScopeManager<>)); + builder.Services.TryAddScoped(typeof(OpenIddictTokenManager<>)); + + builder.Services.TryAddScoped(); + builder.Services.TryAddScoped(); + builder.Services.TryAddScoped(); + builder.Services.TryAddScoped(); + + return new OpenIddictCoreBuilder(builder.Services); + } + + /// + /// Registers the OpenIddict core services in the DI container. + /// + /// The services builder used by OpenIddict to register new services. + /// The configuration delegate used to configure the core services. + /// This extension can be safely called multiple times. + /// The . + public static OpenIddictBuilder AddCore( + [NotNull] this OpenIddictBuilder builder, + [NotNull] Action configuration) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (configuration == null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + configuration(builder.AddCore()); + + return builder; + } + } +} \ No newline at end of file diff --git a/src/OpenIddict.Core/OpenIddictCoreHelpers.cs b/src/OpenIddict.Core/OpenIddictCoreHelpers.cs new file mode 100644 index 00000000..75d3c541 --- /dev/null +++ b/src/OpenIddict.Core/OpenIddictCoreHelpers.cs @@ -0,0 +1,67 @@ +using System; +using System.ComponentModel; + +namespace OpenIddict.Core +{ + /// + /// Exposes common helpers used by the OpenIddict assemblies. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static class OpenIddictCoreHelpers + { + /// + /// Finds the base type that matches the specified generic type definition. + /// + /// The type to introspect. + /// The generic type definition. + /// A instance if the base type was found, null otherwise. + public static Type FindGenericBaseType(Type type, Type definition) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (definition == null) + { + throw new ArgumentNullException(nameof(definition)); + } + + if (!definition.IsGenericTypeDefinition) + { + throw new ArgumentException("The second parameter must be a generic type definition.", nameof(definition)); + } + + for (var candidate = type; candidate != null; candidate = candidate.BaseType) + { + if (!candidate.IsGenericType && !candidate.IsConstructedGenericType) + { + continue; + } + + if (candidate.GetGenericTypeDefinition() == definition) + { + return candidate; + } + + if (definition.IsInterface) + { + foreach (var contract in candidate.GetInterfaces()) + { + if (!contract.IsGenericType && !contract.IsConstructedGenericType) + { + continue; + } + + if (contract.GetGenericTypeDefinition() == definition) + { + return contract; + } + } + } + } + + return null; + } + } +} diff --git a/src/OpenIddict.Core/OpenIddictCoreOptions.cs b/src/OpenIddict.Core/OpenIddictCoreOptions.cs new file mode 100644 index 00000000..5f728591 --- /dev/null +++ b/src/OpenIddict.Core/OpenIddictCoreOptions.cs @@ -0,0 +1,33 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; + +namespace OpenIddict.Core +{ + public class OpenIddictCoreOptions + { + /// + /// Gets or sets the type corresponding to the Application entity. + /// + public Type DefaultApplicationType { get; set; } + + /// + /// Gets or sets the type corresponding to the Authorization entity. + /// + public Type DefaultAuthorizationType { get; set; } + + /// + /// Gets or sets the type corresponding to the Scope entity. + /// + public Type DefaultScopeType { get; set; } + + /// + /// Gets or sets the type corresponding to the Token entity. + /// + public Type DefaultTokenType { get; set; } + } +} \ No newline at end of file diff --git a/src/OpenIddict.Core/OpenIddictExtensions.cs b/src/OpenIddict.Core/OpenIddictExtensions.cs deleted file mode 100644 index 9be79446..00000000 --- a/src/OpenIddict.Core/OpenIddictExtensions.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * See https://github.com/openiddict/openiddict-core for more information concerning - * the license and the contributors participating to this project. - */ - -using System; -using JetBrains.Annotations; -using Microsoft.Extensions.DependencyInjection.Extensions; -using OpenIddict.Core; - -namespace Microsoft.Extensions.DependencyInjection -{ - public static class OpenIddictExtensions - { - /// - /// Registers the default OpenIddict services in the DI container, using the specified entities. - /// - /// The type of the Application entity. - /// The type of the Authorization entity. - /// The type of the Scope entity. - /// The type of the Token entity. - /// The services collection. - /// The . - public static OpenIddictBuilder AddOpenIddict([NotNull] this IServiceCollection services) - where TApplication : class - where TAuthorization : class - where TScope : class - where TToken : class - { - if (services == null) - { - throw new ArgumentNullException(nameof(services)); - } - - services.AddDistributedMemoryCache(); - services.AddMemoryCache(); - services.AddOptions(); - - // Register the OpenIddict core services in the DI container. - services.TryAddScoped>(); - services.TryAddScoped>(); - services.TryAddScoped>(); - services.TryAddScoped>(); - - return new OpenIddictBuilder(services) - { - ApplicationType = typeof(TApplication), - AuthorizationType = typeof(TAuthorization), - ScopeType = typeof(TScope), - TokenType = typeof(TToken) - }; - } - - /// - /// Registers the default OpenIddict services in the DI container, using the specified entities. - /// - /// The type of the Application entity. - /// The type of the Authorization entity. - /// The type of the Scope entity. - /// The type of the Token entity. - /// The services collection. - /// The configuration delegate used to register new services. - /// The . - public static IServiceCollection AddOpenIddict( - [NotNull] this IServiceCollection services, - [NotNull] Action configuration) - where TApplication : class - where TAuthorization : class - where TScope : class - where TToken : class - { - if (services == null) - { - throw new ArgumentNullException(nameof(services)); - } - - if (configuration == null) - { - throw new ArgumentNullException(nameof(configuration)); - } - - // Register the OpenIddict core services and invoke the configuration delegate. - configuration(services.AddOpenIddict()); - - return services; - } - } -} \ No newline at end of file diff --git a/src/OpenIddict.Core/Resolvers/OpenIddictApplicationStoreResolver.cs b/src/OpenIddict.Core/Resolvers/OpenIddictApplicationStoreResolver.cs new file mode 100644 index 00000000..41353685 --- /dev/null +++ b/src/OpenIddict.Core/Resolvers/OpenIddictApplicationStoreResolver.cs @@ -0,0 +1,44 @@ +using System; +using System.Text; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; +using OpenIddict.Abstractions; + +namespace OpenIddict.Core +{ + /// + /// Exposes a method allowing to resolve an application store. + /// + public class OpenIddictApplicationStoreResolver : IOpenIddictApplicationStoreResolver + { + private readonly IServiceProvider _provider; + + public OpenIddictApplicationStoreResolver([NotNull] IServiceProvider provider) + { + _provider = provider; + } + + /// + /// Returns an application store compatible with the specified application type or throws an + /// if no store can be built using the specified type. + /// + /// The type of the Application entity. + /// An . + public IOpenIddictApplicationStore Get() where TApplication : class + { + var store = _provider.GetService>(); + if (store == null) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("No application store has been registered in the dependency injection container.") + .Append("To register the Entity Framework Core stores, reference the 'OpenIddict.EntityFrameworkCore' ") + .AppendLine("package and call 'services.AddOpenIddict().AddCore().AddEntityFrameworkCoreStores()'.") + .Append("To register a custom store, create an implementation of 'IOpenIddictApplicationStore' and ") + .Append("use 'services.AddOpenIddict().AddCore().AddApplicationStore()' to add it to the DI container.") + .ToString()); + } + + return store; + } + } +} diff --git a/src/OpenIddict.Core/Resolvers/OpenIddictAuthorizationStoreResolver.cs b/src/OpenIddict.Core/Resolvers/OpenIddictAuthorizationStoreResolver.cs new file mode 100644 index 00000000..051ac7d5 --- /dev/null +++ b/src/OpenIddict.Core/Resolvers/OpenIddictAuthorizationStoreResolver.cs @@ -0,0 +1,44 @@ +using System; +using System.Text; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; +using OpenIddict.Abstractions; + +namespace OpenIddict.Core +{ + /// + /// Exposes a method allowing to resolve an authorization store. + /// + public class OpenIddictAuthorizationStoreResolver : IOpenIddictAuthorizationStoreResolver + { + private readonly IServiceProvider _provider; + + public OpenIddictAuthorizationStoreResolver([NotNull] IServiceProvider provider) + { + _provider = provider; + } + + /// + /// Returns an authorization store compatible with the specified authorization type or throws an + /// if no store can be built using the specified type. + /// + /// The type of the Authorization entity. + /// An . + public IOpenIddictAuthorizationStore Get() where TAuthorization : class + { + var store = _provider.GetService>(); + if (store == null) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("No authorization store has been registered in the dependency injection container.") + .Append("To register the Entity Framework Core stores, reference the 'OpenIddict.EntityFrameworkCore' ") + .AppendLine("package and call 'services.AddOpenIddict().AddCore().AddEntityFrameworkCoreStores()'.") + .Append("To register a custom store, create an implementation of 'IOpenIddictAuthorizationStore' and ") + .Append("use 'services.AddOpenIddict().AddCore().AddAuthorizationStore()' to add it to the DI container.") + .ToString()); + } + + return store; + } + } +} diff --git a/src/OpenIddict.Core/Resolvers/OpenIddictScopeStoreResolver.cs b/src/OpenIddict.Core/Resolvers/OpenIddictScopeStoreResolver.cs new file mode 100644 index 00000000..622ba7ff --- /dev/null +++ b/src/OpenIddict.Core/Resolvers/OpenIddictScopeStoreResolver.cs @@ -0,0 +1,44 @@ +using System; +using System.Text; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; +using OpenIddict.Abstractions; + +namespace OpenIddict.Core +{ + /// + /// Exposes a method allowing to resolve a scope store. + /// + public class OpenIddictScopeStoreResolver : IOpenIddictScopeStoreResolver + { + private readonly IServiceProvider _provider; + + public OpenIddictScopeStoreResolver([NotNull] IServiceProvider provider) + { + _provider = provider; + } + + /// + /// Returns a scope store compatible with the specified scope type or throws an + /// if no store can be built using the specified type. + /// + /// The type of the Scope entity. + /// An . + public IOpenIddictScopeStore Get() where TScope : class + { + var store = _provider.GetService>(); + if (store == null) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("No scope store has been registered in the dependency injection container.") + .Append("To register the Entity Framework Core stores, reference the 'OpenIddict.EntityFrameworkCore' ") + .AppendLine("package and call 'services.AddOpenIddict().AddCore().AddEntityFrameworkCoreStores()'.") + .Append("To register a custom store, create an implementation of 'IOpenIddictScopeStore' and ") + .Append("use 'services.AddOpenIddict().AddCore().AddScopeStore()' to add it to the DI container.") + .ToString()); + } + + return store; + } + } +} diff --git a/src/OpenIddict.Core/Resolvers/OpenIddictTokenStoreResolver.cs b/src/OpenIddict.Core/Resolvers/OpenIddictTokenStoreResolver.cs new file mode 100644 index 00000000..c79152cc --- /dev/null +++ b/src/OpenIddict.Core/Resolvers/OpenIddictTokenStoreResolver.cs @@ -0,0 +1,44 @@ +using System; +using System.Text; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; +using OpenIddict.Abstractions; + +namespace OpenIddict.Core +{ + /// + /// Exposes a method allowing to resolve a token store. + /// + public class OpenIddictTokenStoreResolver : IOpenIddictTokenStoreResolver + { + private readonly IServiceProvider _provider; + + public OpenIddictTokenStoreResolver([NotNull] IServiceProvider provider) + { + _provider = provider; + } + + /// + /// Returns a token store compatible with the specified token type or throws an + /// if no store can be built using the specified type. + /// + /// The type of the Token entity. + /// An . + public IOpenIddictTokenStore Get() where TToken : class + { + var store = _provider.GetService>(); + if (store == null) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("No token store factory has been registered in the dependency injection container.") + .Append("To register the Entity Framework Core stores, reference the 'OpenIddict.EntityFrameworkCore' ") + .AppendLine("package and call 'services.AddOpenIddict().AddCore().AddEntityFrameworkCoreStores()'.") + .Append("To register a custom store, create an implementation of 'IOpenIddictTokenStore' and ") + .Append("use 'services.AddOpenIddict().AddCore().AddTokenStore()' to add it to the DI container.") + .ToString()); + } + + return store; + } + } +} diff --git a/src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj b/src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj index ecfddf7b..98e586ed 100644 --- a/src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj +++ b/src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj @@ -13,6 +13,7 @@ + diff --git a/src/OpenIddict.EntityFramework/OpenIddictExtensions.cs b/src/OpenIddict.EntityFramework/OpenIddictExtensions.cs index 5e2cc25d..fbf0dcea 100644 --- a/src/OpenIddict.EntityFramework/OpenIddictExtensions.cs +++ b/src/OpenIddict.EntityFramework/OpenIddictExtensions.cs @@ -5,15 +5,12 @@ */ using System; -using System.ComponentModel; using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity; using System.Data.Entity.Infrastructure.Annotations; -using System.Diagnostics; -using System.Reflection; +using System.Text; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection.Extensions; -using OpenIddict.Core; using OpenIddict.EntityFramework; using OpenIddict.Models; @@ -27,8 +24,9 @@ namespace Microsoft.Extensions.DependencyInjection /// the entities MUST be derived from the models contained in the OpenIddict.Models package. /// /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder AddEntityFrameworkStores([NotNull] this OpenIddictBuilder builder) + /// This extension can be safely called multiple times. + /// The . + public static OpenIddictCoreBuilder AddEntityFrameworkStores([NotNull] this OpenIddictCoreBuilder builder) where TContext : DbContext { if (builder == null) @@ -36,85 +34,15 @@ namespace Microsoft.Extensions.DependencyInjection throw new ArgumentNullException(nameof(builder)); } - Debug.Assert(builder.ApplicationType != null && - builder.AuthorizationType != null && - builder.ScopeType != null && - builder.TokenType != null, "The entity types exposed by OpenIddictBuilder shouldn't be null."); + builder.Services.TryAddScoped(typeof(OpenIddictApplicationStore<,,,,>)); + builder.Services.TryAddScoped(typeof(OpenIddictAuthorizationStore<,,,,>)); + builder.Services.TryAddScoped(typeof(OpenIddictScopeStore<,,>)); + builder.Services.TryAddScoped(typeof(OpenIddictTokenStore<,,,,>)); - var application = FindGenericBaseType(builder.ApplicationType, typeof(OpenIddictApplication<,,>)); - if (application == null) - { - throw new InvalidOperationException("The Entity Framework stores can only be used " + - "with the built-in OpenIddictApplication entity."); - } - - var authorization = FindGenericBaseType(builder.AuthorizationType, typeof(OpenIddictAuthorization<,,>)); - if (authorization == null) - { - throw new InvalidOperationException("The Entity Framework 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 " + - "with the built-in OpenIddictScope entity."); - } - - var token = FindGenericBaseType(builder.TokenType, typeof(OpenIddictToken<,,>)); - if (token == null) - { - throw new InvalidOperationException("The Entity Framework 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( - /* TApplication: */ builder.ApplicationType, - /* TAuthorization: */ builder.AuthorizationType, - /* TToken: */ builder.TokenType, - /* TContext: */ typeof(TContext), - /* TKey: */ application.GenericTypeArguments[0])); - - // Register the authorization store in the DI container. - builder.Services.TryAddScoped( - typeof(IOpenIddictAuthorizationStore<>).MakeGenericType(builder.AuthorizationType), - typeof(OpenIddictAuthorizationStore<,,,,>).MakeGenericType( - /* TAuthorization: */ builder.AuthorizationType, - /* TApplication: */ builder.ApplicationType, - /* TToken: */ builder.TokenType, - /* TContext: */ typeof(TContext), - /* TKey: */ authorization.GenericTypeArguments[0])); - - // Register the scope store in the DI container. - builder.Services.TryAddScoped( - typeof(IOpenIddictScopeStore<>).MakeGenericType(builder.ScopeType), - typeof(OpenIddictScopeStore<,,>).MakeGenericType( - /* TScope: */ builder.ScopeType, - /* TContext: */ typeof(TContext), - /* TKey: */ scope.GenericTypeArguments[0])); - - // Register the token store in the DI container. - builder.Services.TryAddScoped( - typeof(IOpenIddictTokenStore<>).MakeGenericType(builder.TokenType), - typeof(OpenIddictTokenStore<,,,,>).MakeGenericType( - /* TToken: */ builder.TokenType, - /* TApplication: */ builder.ApplicationType, - /* TAuthorization: */ builder.AuthorizationType, - /* TContext: */ typeof(TContext), - /* TKey: */ token.GenericTypeArguments[0])); - - return builder; + return builder.ReplaceApplicationStoreResolver>() + .ReplaceAuthorizationStoreResolver>() + .ReplaceScopeStoreResolver>() + .ReplaceTokenStoreResolver>(); } /// @@ -124,12 +52,10 @@ namespace Microsoft.Extensions.DependencyInjection /// The builder used to configure the Entity Framework context. /// The Entity Framework context builder. public static DbModelBuilder UseOpenIddict([NotNull] this DbModelBuilder builder) - { - return builder.UseOpenIddict(); - } + => builder.UseOpenIddict(); /// /// Registers the OpenIddict entity sets in the Entity Framework context @@ -154,28 +80,36 @@ namespace Microsoft.Extensions.DependencyInjection // Note: unlike Entity Framework Core 1.x/2.x, Entity Framework 6.x // always throws an exception when using generic types as entity types. // To ensure a better exception is thrown, a manual check is made here. - if (typeof(TApplication).GetTypeInfo().IsGenericType) + if (typeof(TApplication).IsGenericType) { - throw new InvalidOperationException("The application entity cannot be a generic type. " + - "Consider creating a non-generic derived class."); + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The application entity cannot be a generic type.") + .Append("Consider creating a non-generic derived class.") + .ToString()); } - if (typeof(TAuthorization).GetTypeInfo().IsGenericType) + if (typeof(TAuthorization).IsGenericType) { - throw new InvalidOperationException("The authorization entity cannot be a generic type. " + - "Consider creating a non-generic derived class."); + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The authorization entity cannot be a generic type.") + .Append("Consider creating a non-generic derived class.") + .ToString()); } - if (typeof(TScope).GetTypeInfo().IsGenericType) + if (typeof(TScope).IsGenericType) { - throw new InvalidOperationException("The scope entity cannot be a generic type. " + - "Consider creating a non-generic derived class."); + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The scope entity cannot be a generic type.") + .Append("Consider creating a non-generic derived class.") + .ToString()); } - if (typeof(TToken).GetTypeInfo().IsGenericType) + if (typeof(TToken).IsGenericType) { - throw new InvalidOperationException("The scope entity cannot be a generic type. " + - "Consider creating a non-generic derived class."); + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The token entity cannot be a generic type.") + .Append("Consider creating a non-generic derived class.") + .ToString()); } // Warning: optional foreign keys MUST NOT be added as CLR properties because @@ -285,28 +219,5 @@ namespace Microsoft.Extensions.DependencyInjection return builder; } - - 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; - } - } - - return null; - } } } \ No newline at end of file diff --git a/src/OpenIddict.EntityFramework/Resolvers/OpenIddictApplicationStoreResolver.cs b/src/OpenIddict.EntityFramework/Resolvers/OpenIddictApplicationStoreResolver.cs new file mode 100644 index 00000000..b235657c --- /dev/null +++ b/src/OpenIddict.EntityFramework/Resolvers/OpenIddictApplicationStoreResolver.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Concurrent; +using System.Data.Entity; +using System.Text; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; +using OpenIddict.Abstractions; +using OpenIddict.Core; +using OpenIddict.Models; + +namespace OpenIddict.EntityFramework +{ + /// + /// Exposes a method allowing to resolve an application store. + /// + public class OpenIddictApplicationStoreResolver : IOpenIddictApplicationStoreResolver + where TContext : DbContext + { + private static readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); + private readonly IServiceProvider _provider; + + public OpenIddictApplicationStoreResolver([NotNull] IServiceProvider provider) + { + _provider = provider; + } + + /// + /// Returns an application store compatible with the specified application type or throws an + /// if no store can be built using the specified type. + /// + /// The type of the Application entity. + /// An . + public IOpenIddictApplicationStore Get() where TApplication : class + { + var store = _provider.GetService>(); + if (store != null) + { + return store; + } + + var type = _cache.GetOrAdd(typeof(TApplication), key => + { + var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictApplication<,,>)); + if (root == null) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The specified application type is not compatible with the Entity Framework 6.x stores.") + .Append("When enabling the Entity Framework 6.x stores, make sure you use the built-in generic ") + .Append("'OpenIddictApplication' entity (from the 'OpenIddict.Models' package) or a custom entity ") + .Append("that inherits from the generic 'OpenIddictApplication' entity.") + .ToString()); + } + + return typeof(OpenIddictApplicationStore<,,,,>).MakeGenericType( + /* TApplication: */ key, + /* TAuthorization: */ root.GenericTypeArguments[1], + /* TToken: */ root.GenericTypeArguments[2], + /* TContext: */ typeof(TContext), + /* TKey: */ root.GenericTypeArguments[0]); + }); + + return (IOpenIddictApplicationStore) _provider.GetRequiredService(type); + } + } +} diff --git a/src/OpenIddict.EntityFramework/Resolvers/OpenIddictAuthorizationStoreResolver.cs b/src/OpenIddict.EntityFramework/Resolvers/OpenIddictAuthorizationStoreResolver.cs new file mode 100644 index 00000000..942dc910 --- /dev/null +++ b/src/OpenIddict.EntityFramework/Resolvers/OpenIddictAuthorizationStoreResolver.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Concurrent; +using System.Data.Entity; +using System.Text; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; +using OpenIddict.Abstractions; +using OpenIddict.Core; +using OpenIddict.Models; + +namespace OpenIddict.EntityFramework +{ + /// + /// Exposes a method allowing to resolve an authorization store. + /// + public class OpenIddictAuthorizationStoreResolver : IOpenIddictAuthorizationStoreResolver + where TContext : DbContext + { + private static readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); + private readonly IServiceProvider _provider; + + public OpenIddictAuthorizationStoreResolver([NotNull] IServiceProvider provider) + { + _provider = provider; + } + + /// + /// Returns an authorization store compatible with the specified authorization type or throws an + /// if no store can be built using the specified type. + /// + /// The type of the Authorization entity. + /// An . + public IOpenIddictAuthorizationStore Get() where TAuthorization : class + { + var store = _provider.GetService>(); + if (store != null) + { + return store; + } + + var type = _cache.GetOrAdd(typeof(TAuthorization), key => + { + var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictAuthorization<,,>)); + if (root == null) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The specified authorization type is not compatible with the Entity Framework 6.x stores.") + .Append("When enabling the Entity Framework 6.x stores, make sure you use the built-in generic ") + .Append("'OpenIddictAuthorization' entity (from the 'OpenIddict.Models' package) or a custom entity ") + .Append("that inherits from the generic 'OpenIddictAuthorization' entity.") + .ToString()); + } + + return typeof(OpenIddictAuthorizationStore<,,,,>).MakeGenericType( + /* TAuthorization: */ key, + /* TApplication: */ root.GenericTypeArguments[1], + /* TToken: */ root.GenericTypeArguments[2], + /* TContext: */ typeof(TContext), + /* TKey: */ root.GenericTypeArguments[0]); + }); + + return (IOpenIddictAuthorizationStore) _provider.GetRequiredService(type); + } + } +} diff --git a/src/OpenIddict.EntityFramework/Resolvers/OpenIddictScopeStoreResolver.cs b/src/OpenIddict.EntityFramework/Resolvers/OpenIddictScopeStoreResolver.cs new file mode 100644 index 00000000..1744e2d0 --- /dev/null +++ b/src/OpenIddict.EntityFramework/Resolvers/OpenIddictScopeStoreResolver.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Concurrent; +using System.Data.Entity; +using System.Text; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; +using OpenIddict.Abstractions; +using OpenIddict.Core; +using OpenIddict.Models; + +namespace OpenIddict.EntityFramework +{ + /// + /// Exposes a method allowing to resolve a scope store. + /// + public class OpenIddictScopeStoreResolver : IOpenIddictScopeStoreResolver + where TContext : DbContext + { + private static readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); + private readonly IServiceProvider _provider; + + public OpenIddictScopeStoreResolver([NotNull] IServiceProvider provider) + { + _provider = provider; + } + + /// + /// Returns a scope store compatible with the specified scope type or throws an + /// if no store can be built using the specified type. + /// + /// The type of the Scope entity. + /// An . + public IOpenIddictScopeStore Get() where TScope : class + { + var store = _provider.GetService>(); + if (store != null) + { + return store; + } + + var type = _cache.GetOrAdd(typeof(TScope), key => + { + var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictScope<>)); + if (root == null) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The specified scope type is not compatible with the Entity Framework 6.x stores.") + .Append("When enabling the Entity Framework 6.x stores, make sure you use the built-in generic ") + .Append("'OpenIdScope' entity (from the 'OpenIddict.Models' package) or a custom entity ") + .Append("that inherits from the generic 'OpenIddictScope' entity.") + .ToString()); + } + + return typeof(OpenIddictScopeStore<,,>).MakeGenericType( + /* TScope: */ key, + /* TContext: */ typeof(TContext), + /* TKey: */ root.GenericTypeArguments[0]); + }); + + return (IOpenIddictScopeStore) _provider.GetRequiredService(type); + } + } +} diff --git a/src/OpenIddict.EntityFramework/Resolvers/OpenIddictTokenStoreResolver.cs b/src/OpenIddict.EntityFramework/Resolvers/OpenIddictTokenStoreResolver.cs new file mode 100644 index 00000000..aefd8ddd --- /dev/null +++ b/src/OpenIddict.EntityFramework/Resolvers/OpenIddictTokenStoreResolver.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Concurrent; +using System.Data.Entity; +using System.Text; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; +using OpenIddict.Abstractions; +using OpenIddict.Core; +using OpenIddict.Models; + +namespace OpenIddict.EntityFramework +{ + /// + /// Exposes a method allowing to resolve a token store. + /// + public class OpenIddictTokenStoreResolver : IOpenIddictTokenStoreResolver + where TContext : DbContext + { + private static readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); + private readonly IServiceProvider _provider; + + public OpenIddictTokenStoreResolver([NotNull] IServiceProvider provider) + { + _provider = provider; + } + + /// + /// Returns a token store compatible with the specified token type or throws an + /// if no store can be built using the specified type. + /// + /// The type of the Token entity. + /// An . + public IOpenIddictTokenStore Get() where TToken : class + { + var store = _provider.GetService>(); + if (store != null) + { + return store; + } + + var type = _cache.GetOrAdd(typeof(TToken), key => + { + var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictToken<,,>)); + if (root == null) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The specified token type is not compatible with the Entity Framework 6.x stores.") + .Append("When enabling the Entity Framework 6.x stores, make sure you use the built-in generic ") + .Append("'OpenIddictToken' entity (from the 'OpenIddict.Models' package) or a custom entity ") + .Append("that inherits from the generic 'OpenIddictToken' entity.") + .ToString()); + } + + return typeof(OpenIddictTokenStore<,,,,>).MakeGenericType( + /* TToken: */ key, + /* TApplication: */ root.GenericTypeArguments[1], + /* TAuthorization: */ root.GenericTypeArguments[2], + /* TContext: */ typeof(TContext), + /* TKey: */ root.GenericTypeArguments[0]); + }); + + return (IOpenIddictTokenStore) _provider.GetRequiredService(type); + } + } +} diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs index 18ca3bb7..0fcc7066 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs @@ -14,7 +14,7 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.Extensions.Caching.Memory; -using OpenIddict.Core; +using OpenIddict.Abstractions; using OpenIddict.Models; using OpenIddict.Stores; diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs index 26239b69..e831691b 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs @@ -14,7 +14,7 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.Extensions.Caching.Memory; -using OpenIddict.Core; +using OpenIddict.Abstractions; using OpenIddict.Models; using OpenIddict.Stores; diff --git a/src/OpenIddict.EntityFrameworkCore/OpenIddict.EntityFrameworkCore.csproj b/src/OpenIddict.EntityFrameworkCore/OpenIddict.EntityFrameworkCore.csproj index ba729a49..46042f4d 100644 --- a/src/OpenIddict.EntityFrameworkCore/OpenIddict.EntityFrameworkCore.csproj +++ b/src/OpenIddict.EntityFrameworkCore/OpenIddict.EntityFrameworkCore.csproj @@ -13,6 +13,7 @@ + diff --git a/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs b/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs index 0cca4c1c..5145552e 100644 --- a/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs +++ b/src/OpenIddict.EntityFrameworkCore/OpenIddictExtensions.cs @@ -5,14 +5,10 @@ */ using System; -using System.ComponentModel; -using System.Diagnostics; -using System.Reflection; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.Extensions.DependencyInjection.Extensions; -using OpenIddict.Core; using OpenIddict.EntityFrameworkCore; using OpenIddict.Models; @@ -25,8 +21,9 @@ namespace Microsoft.Extensions.DependencyInjection /// the entities MUST be derived from the models contained in the OpenIddict.Models package. /// /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder AddEntityFrameworkCoreStores([NotNull] this OpenIddictBuilder builder) + /// This extension can be safely called multiple times. + /// The . + public static OpenIddictCoreBuilder AddEntityFrameworkCoreStores([NotNull] this OpenIddictCoreBuilder builder) where TContext : DbContext { if (builder == null) @@ -34,85 +31,15 @@ namespace Microsoft.Extensions.DependencyInjection throw new ArgumentNullException(nameof(builder)); } - Debug.Assert(builder.ApplicationType != null && - builder.AuthorizationType != null && - builder.ScopeType != null && - builder.TokenType != null, "The entity types exposed by OpenIddictBuilder shouldn't be null."); + builder.Services.TryAddScoped(typeof(OpenIddictApplicationStore<,,,,>)); + builder.Services.TryAddScoped(typeof(OpenIddictAuthorizationStore<,,,,>)); + builder.Services.TryAddScoped(typeof(OpenIddictScopeStore<,,>)); + builder.Services.TryAddScoped(typeof(OpenIddictTokenStore<,,,,>)); - var application = FindGenericBaseType(builder.ApplicationType, typeof(OpenIddictApplication<,,>)); - if (application == null) - { - 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<,,>)); - if (authorization == null) - { - 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 Core stores can only be used " + - "with the built-in OpenIddictScope entity."); - } - - var token = FindGenericBaseType(builder.TokenType, typeof(OpenIddictToken<,,>)); - if (token == null) - { - 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( - /* TApplication: */ builder.ApplicationType, - /* TAuthorization: */ builder.AuthorizationType, - /* TToken: */ builder.TokenType, - /* TContext: */ typeof(TContext), - /* TKey: */ application.GenericTypeArguments[0])); - - // Register the authorization store in the DI container. - builder.Services.TryAddScoped( - typeof(IOpenIddictAuthorizationStore<>).MakeGenericType(builder.AuthorizationType), - typeof(OpenIddictAuthorizationStore<,,,,>).MakeGenericType( - /* TAuthorization: */ builder.AuthorizationType, - /* TApplication: */ builder.ApplicationType, - /* TToken: */ builder.TokenType, - /* TContext: */ typeof(TContext), - /* TKey: */ authorization.GenericTypeArguments[0])); - - // Register the scope store in the DI container. - builder.Services.TryAddScoped( - typeof(IOpenIddictScopeStore<>).MakeGenericType(builder.ScopeType), - typeof(OpenIddictScopeStore<,,>).MakeGenericType( - /* TScope: */ builder.ScopeType, - /* TContext: */ typeof(TContext), - /* TKey: */ scope.GenericTypeArguments[0])); - - // Register the token store in the DI container. - builder.Services.TryAddScoped( - typeof(IOpenIddictTokenStore<>).MakeGenericType(builder.TokenType), - typeof(OpenIddictTokenStore<,,,,>).MakeGenericType( - /* TToken: */ builder.TokenType, - /* TApplication: */ builder.ApplicationType, - /* TAuthorization: */ builder.AuthorizationType, - /* TContext: */ typeof(TContext), - /* TKey: */ token.GenericTypeArguments[0])); - - return builder; + return builder.ReplaceApplicationStoreResolver>() + .ReplaceAuthorizationStoreResolver>() + .ReplaceScopeStoreResolver>() + .ReplaceTokenStoreResolver>(); } /// @@ -122,12 +49,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(); - } + => builder.UseOpenIddict(); /// /// Registers the OpenIddict entity sets in the Entity Framework context @@ -135,13 +60,12 @@ 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 TKey : IEquatable - { - return builder.UseOpenIddict, - OpenIddictAuthorization, - OpenIddictScope, - OpenIddictToken, TKey>(); - } + public static DbContextOptionsBuilder UseOpenIddict([NotNull] this DbContextOptionsBuilder builder) + where TKey : IEquatable + => builder.UseOpenIddict, + OpenIddictAuthorization, + OpenIddictScope, + OpenIddictToken, TKey>(); /// /// Registers the OpenIddict entity sets in the Entity Framework context @@ -171,12 +95,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(); - } + => builder.UseOpenIddict(); /// /// Registers the OpenIddict entity sets in the Entity Framework context @@ -185,12 +107,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 TKey : IEquatable - { - return builder.UseOpenIddict, - OpenIddictAuthorization, - OpenIddictScope, - OpenIddictToken, TKey>(); - } + => builder.UseOpenIddict, + OpenIddictAuthorization, + OpenIddictScope, + OpenIddictToken, TKey>(); /// /// Registers the OpenIddict entity sets in the Entity Framework context @@ -309,28 +229,5 @@ namespace Microsoft.Extensions.DependencyInjection return builder; } - - 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; - } - } - - return null; - } } } \ No newline at end of file diff --git a/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictApplicationStoreResolver.cs b/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictApplicationStoreResolver.cs new file mode 100644 index 00000000..9f5c5e46 --- /dev/null +++ b/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictApplicationStoreResolver.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Concurrent; +using System.Text; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using OpenIddict.Abstractions; +using OpenIddict.Core; +using OpenIddict.Models; + +namespace OpenIddict.EntityFrameworkCore +{ + /// + /// Exposes a method allowing to resolve an application store. + /// + public class OpenIddictApplicationStoreResolver : IOpenIddictApplicationStoreResolver + where TContext : DbContext + { + private static readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); + private readonly IServiceProvider _provider; + + public OpenIddictApplicationStoreResolver([NotNull] IServiceProvider provider) + { + _provider = provider; + } + + /// + /// Returns an application store compatible with the specified application type or throws an + /// if no store can be built using the specified type. + /// + /// The type of the Application entity. + /// An . + public IOpenIddictApplicationStore Get() where TApplication : class + { + var store = _provider.GetService>(); + if (store != null) + { + return store; + } + + var type = _cache.GetOrAdd(typeof(TApplication), key => + { + var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictApplication<,,>)); + if (root == null) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The specified application type is not compatible with the Entity Framework Core stores.") + .Append("When enabling the Entity Framework Core stores, make sure you use the built-in generic ") + .Append("'OpenIddictApplication' entity (from the 'OpenIddict.Models' package) or a custom entity ") + .Append("that inherits from the generic 'OpenIddictApplication' entity.") + .ToString()); + } + + return typeof(OpenIddictApplicationStore<,,,,>).MakeGenericType( + /* TApplication: */ key, + /* TAuthorization: */ root.GenericTypeArguments[1], + /* TToken: */ root.GenericTypeArguments[2], + /* TContext: */ typeof(TContext), + /* TKey: */ root.GenericTypeArguments[0]); + }); + + return (IOpenIddictApplicationStore) _provider.GetRequiredService(type); + } + } +} diff --git a/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictAuthorizationStoreResolver.cs b/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictAuthorizationStoreResolver.cs new file mode 100644 index 00000000..a0fae2c4 --- /dev/null +++ b/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictAuthorizationStoreResolver.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Concurrent; +using System.Text; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using OpenIddict.Abstractions; +using OpenIddict.Core; +using OpenIddict.Models; + +namespace OpenIddict.EntityFrameworkCore +{ + /// + /// Exposes a method allowing to resolve an authorization store. + /// + public class OpenIddictAuthorizationStoreResolver : IOpenIddictAuthorizationStoreResolver + where TContext : DbContext + { + private static readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); + private readonly IServiceProvider _provider; + + public OpenIddictAuthorizationStoreResolver([NotNull] IServiceProvider provider) + { + _provider = provider; + } + + /// + /// Returns an authorization store compatible with the specified authorization type or throws an + /// if no store can be built using the specified type. + /// + /// The type of the Authorization entity. + /// An . + public IOpenIddictAuthorizationStore Get() where TAuthorization : class + { + var store = _provider.GetService>(); + if (store != null) + { + return store; + } + + var type = _cache.GetOrAdd(typeof(TAuthorization), key => + { + var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictAuthorization<,,>)); + if (root == null) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The specified authorization type is not compatible with the Entity Framework Core stores.") + .Append("When enabling the Entity Framework Core stores, make sure you use the built-in generic ") + .Append("'OpenIddictAuthorization' entity (from the 'OpenIddict.Models' package) or a custom entity ") + .Append("that inherits from the generic 'OpenIddictAuthorization' entity.") + .ToString()); + } + + return typeof(OpenIddictAuthorizationStore<,,,,>).MakeGenericType( + /* TAuthorization: */ key, + /* TApplication: */ root.GenericTypeArguments[1], + /* TToken: */ root.GenericTypeArguments[2], + /* TContext: */ typeof(TContext), + /* TKey: */ root.GenericTypeArguments[0]); + }); + + return (IOpenIddictAuthorizationStore) _provider.GetRequiredService(type); + } + } +} diff --git a/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictScopeStoreResolver.cs b/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictScopeStoreResolver.cs new file mode 100644 index 00000000..d7dc2ed1 --- /dev/null +++ b/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictScopeStoreResolver.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Concurrent; +using System.Text; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using OpenIddict.Abstractions; +using OpenIddict.Core; +using OpenIddict.Models; + +namespace OpenIddict.EntityFrameworkCore +{ + /// + /// Exposes a method allowing to resolve a scope store. + /// + public class OpenIddictScopeStoreResolver : IOpenIddictScopeStoreResolver + where TContext : DbContext + { + private static readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); + private readonly IServiceProvider _provider; + + public OpenIddictScopeStoreResolver([NotNull] IServiceProvider provider) + { + _provider = provider; + } + + /// + /// Returns a scope store compatible with the specified scope type or throws an + /// if no store can be built using the specified type. + /// + /// The type of the Scope entity. + /// An . + public IOpenIddictScopeStore Get() where TScope : class + { + var store = _provider.GetService>(); + if (store != null) + { + return store; + } + + var type = _cache.GetOrAdd(typeof(TScope), key => + { + var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictScope<>)); + if (root == null) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The specified scope type is not compatible with the Entity Framework Core stores.") + .Append("When enabling the Entity Framework Core stores, make sure you use the built-in generic ") + .Append("'OpenIdScope' entity (from the 'OpenIddict.Models' package) or a custom entity ") + .Append("that inherits from the generic 'OpenIddictScope' entity.") + .ToString()); + } + + return typeof(OpenIddictScopeStore<,,>).MakeGenericType( + /* TScope: */ key, + /* TContext: */ typeof(TContext), + /* TKey: */ root.GenericTypeArguments[0]); + }); + + return (IOpenIddictScopeStore) _provider.GetRequiredService(type); + } + } +} diff --git a/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictTokenStoreResolver.cs b/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictTokenStoreResolver.cs new file mode 100644 index 00000000..f8f5da53 --- /dev/null +++ b/src/OpenIddict.EntityFrameworkCore/Resolvers/OpenIddictTokenStoreResolver.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Concurrent; +using System.Text; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using OpenIddict.Abstractions; +using OpenIddict.Core; +using OpenIddict.Models; + +namespace OpenIddict.EntityFrameworkCore +{ + /// + /// Exposes a method allowing to resolve a token store. + /// + public class OpenIddictTokenStoreResolver : IOpenIddictTokenStoreResolver + where TContext : DbContext + { + private static readonly ConcurrentDictionary _cache = new ConcurrentDictionary(); + private readonly IServiceProvider _provider; + + public OpenIddictTokenStoreResolver([NotNull] IServiceProvider provider) + { + _provider = provider; + } + + /// + /// Returns a token store compatible with the specified token type or throws an + /// if no store can be built using the specified type. + /// + /// The type of the Token entity. + /// An . + public IOpenIddictTokenStore Get() where TToken : class + { + var store = _provider.GetService>(); + if (store != null) + { + return store; + } + + var type = _cache.GetOrAdd(typeof(TToken), key => + { + var root = OpenIddictCoreHelpers.FindGenericBaseType(key, typeof(OpenIddictToken<,,>)); + if (root == null) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The specified token type is not compatible with the Entity Framework Core stores.") + .Append("When enabling the Entity Framework Core stores, make sure you use the built-in generic ") + .Append("'OpenIddictToken' entity (from the 'OpenIddict.Models' package) or a custom entity ") + .Append("that inherits from the generic 'OpenIddictToken' entity.") + .ToString()); + } + + return typeof(OpenIddictTokenStore<,,,,>).MakeGenericType( + /* TToken: */ key, + /* TApplication: */ root.GenericTypeArguments[1], + /* TAuthorization: */ root.GenericTypeArguments[2], + /* TContext: */ typeof(TContext), + /* TKey: */ root.GenericTypeArguments[0]); + }); + + return (IOpenIddictTokenStore) _provider.GetRequiredService(type); + } + } +} diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs index beb3d846..039dbbe4 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs @@ -16,7 +16,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.Caching.Memory; -using OpenIddict.Core; +using OpenIddict.Abstractions; using OpenIddict.Models; using OpenIddict.Stores; diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs index 85d5b37c..d68bb36f 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs @@ -16,6 +16,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.Caching.Memory; +using OpenIddict.Abstractions; using OpenIddict.Core; using OpenIddict.Models; using OpenIddict.Stores; diff --git a/src/OpenIddict.Mvc/OpenIddictExtensions.cs b/src/OpenIddict.Mvc/OpenIddictExtensions.cs index ca0a44fc..abe2f8d4 100644 --- a/src/OpenIddict.Mvc/OpenIddictExtensions.cs +++ b/src/OpenIddict.Mvc/OpenIddictExtensions.cs @@ -17,8 +17,9 @@ namespace Microsoft.Extensions.DependencyInjection /// Registers the ASP.NET Core MVC model binders used by OpenIddict. /// /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder AddMvcBinders([NotNull] this OpenIddictBuilder builder) + /// This extension can be safely called multiple times. + /// The . + public static OpenIddictServerBuilder AddMvcBinders([NotNull] this OpenIddictServerBuilder builder) { if (builder == null) { diff --git a/src/OpenIddict.Server/Internal/OpenIddictHandler.cs b/src/OpenIddict.Server/Internal/OpenIddictServerHandler.cs similarity index 71% rename from src/OpenIddict.Server/Internal/OpenIddictHandler.cs rename to src/OpenIddict.Server/Internal/OpenIddictServerHandler.cs index 69157b74..79405eec 100644 --- a/src/OpenIddict.Server/Internal/OpenIddictHandler.cs +++ b/src/OpenIddict.Server/Internal/OpenIddictServerHandler.cs @@ -6,13 +6,13 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace OpenIddict +namespace OpenIddict.Server { [EditorBrowsable(EditorBrowsableState.Never)] - public class OpenIddictHandler : OpenIdConnectServerHandler + public class OpenIddictServerHandler : OpenIdConnectServerHandler { - public OpenIddictHandler( - [NotNull] IOptionsMonitor options, + public OpenIddictServerHandler( + [NotNull] IOptionsMonitor options, [NotNull] ILoggerFactory logger, [NotNull] UrlEncoder encoder, [NotNull] ISystemClock clock) diff --git a/src/OpenIddict.Server/Internal/OpenIddictInitializer.cs b/src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs similarity index 89% rename from src/OpenIddict.Server/Internal/OpenIddictInitializer.cs rename to src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs index e672da53..b1a1e1ee 100644 --- a/src/OpenIddict.Server/Internal/OpenIddictInitializer.cs +++ b/src/OpenIddict.Server/Internal/OpenIddictServerInitializer.cs @@ -15,28 +15,32 @@ using Microsoft.AspNetCore.DataProtection; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; +using OpenIddict.Core; -namespace OpenIddict +namespace OpenIddict.Server { /// /// Contains the methods required to ensure that the configuration /// used by OpenIddict is in a consistent and valid state. /// [EditorBrowsable(EditorBrowsableState.Never)] - public class OpenIddictInitializer : IPostConfigureOptions + public class OpenIddictServerInitializer : IPostConfigureOptions { private readonly IDistributedCache _cache; private readonly IDataProtectionProvider _dataProtectionProvider; + private readonly IOptionsMonitor _options; /// - /// Creates a new instance of the class. + /// Creates a new instance of the class. /// - public OpenIddictInitializer( + public OpenIddictServerInitializer( [NotNull] IDistributedCache cache, - [NotNull] IDataProtectionProvider dataProtectionProvider) + [NotNull] IDataProtectionProvider dataProtectionProvider, + [NotNull] IOptionsMonitor options) { _cache = cache; _dataProtectionProvider = dataProtectionProvider; + _options = options; } /// @@ -45,7 +49,7 @@ namespace OpenIddict /// /// The authentication scheme associated with the handler instance. /// The options instance to initialize. - public void PostConfigure([NotNull] string name, [NotNull] OpenIddictOptions options) + public void PostConfigure([NotNull] string name, [NotNull] OpenIddictServerOptions options) { if (options == null) { @@ -189,6 +193,26 @@ namespace OpenIddict { options.Scopes.Add(OpenIdConnectConstants.Scopes.OfflineAccess); } + + if (options.ApplicationType == null) + { + options.ApplicationType = _options.CurrentValue.DefaultApplicationType; + } + + if (options.AuthorizationType == null) + { + options.AuthorizationType = _options.CurrentValue.DefaultAuthorizationType; + } + + if (options.ScopeType == null) + { + options.ScopeType = _options.CurrentValue.DefaultScopeType; + } + + if (options.TokenType == null) + { + options.TokenType = _options.CurrentValue.DefaultTokenType; + } } } } diff --git a/src/OpenIddict.Server/Internal/OpenIddictProvider.Authentication.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Authentication.cs similarity index 98% rename from src/OpenIddict.Server/Internal/OpenIddictProvider.Authentication.cs rename to src/OpenIddict.Server/Internal/OpenIddictServerProvider.Authentication.cs index 7405b974..905cd802 100644 --- a/src/OpenIddict.Server/Internal/OpenIddictProvider.Authentication.cs +++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Authentication.cs @@ -19,16 +19,16 @@ using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json; using Newtonsoft.Json.Bson; using Newtonsoft.Json.Linq; -using OpenIddict.Core; +using OpenIddict.Abstractions; -namespace OpenIddict +namespace OpenIddict.Server { - public partial class OpenIddictProvider : OpenIdConnectServerProvider + public partial class OpenIddictServerProvider : OpenIdConnectServerProvider where TApplication : class where TAuthorization : class where TScope : class where TToken : class { public override async Task ExtractAuthorizationRequest([NotNull] ExtractAuthorizationRequestContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; // Reject requests using the unsupported request parameter. if (!string.IsNullOrEmpty(context.Request.Request)) @@ -109,7 +109,7 @@ namespace OpenIddict public override async Task ValidateAuthorizationRequest([NotNull] ValidateAuthorizationRequestContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; // Note: the OpenID Connect server middleware supports authorization code, implicit, hybrid, // none and custom flows but OpenIddict uses a stricter policy rejecting none and custum flows. @@ -414,7 +414,7 @@ namespace OpenIddict public override async Task HandleAuthorizationRequest([NotNull] HandleAuthorizationRequestContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; // If no request_id parameter can be found in the current request, assume the OpenID Connect request // was not serialized yet and store the entire payload in the distributed cache to make it easier @@ -466,7 +466,7 @@ namespace OpenIddict public override async Task ApplyAuthorizationResponse([NotNull] ApplyAuthorizationResponseContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; // Remove the authorization request from the distributed cache. if (options.EnableRequestCaching && !string.IsNullOrEmpty(context.Request.RequestId)) diff --git a/src/OpenIddict.Server/Internal/OpenIddictProvider.Discovery.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Discovery.cs similarity index 92% rename from src/OpenIddict.Server/Internal/OpenIddictProvider.Discovery.cs rename to src/OpenIddict.Server/Internal/OpenIddictServerProvider.Discovery.cs index 83897077..1b2b6ea7 100644 --- a/src/OpenIddict.Server/Internal/OpenIddictProvider.Discovery.cs +++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Discovery.cs @@ -12,16 +12,16 @@ using JetBrains.Annotations; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json.Linq; -using OpenIddict.Core; +using OpenIddict.Abstractions; -namespace OpenIddict +namespace OpenIddict.Server { - public partial class OpenIddictProvider : OpenIdConnectServerProvider + public partial class OpenIddictServerProvider : OpenIdConnectServerProvider where TApplication : class where TAuthorization : class where TScope : class where TToken : class { public override async Task HandleConfigurationRequest([NotNull] HandleConfigurationRequestContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; // Note: though it's natively supported by the OpenID Connect server middleware, // OpenIddict disallows the use of the unsecure code_challenge_method=plain method, diff --git a/src/OpenIddict.Server/Internal/OpenIddictProvider.Exchange.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Exchange.cs similarity index 98% rename from src/OpenIddict.Server/Internal/OpenIddictProvider.Exchange.cs rename to src/OpenIddict.Server/Internal/OpenIddictServerProvider.Exchange.cs index 040874c2..4f95bd5e 100644 --- a/src/OpenIddict.Server/Internal/OpenIddictProvider.Exchange.cs +++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Exchange.cs @@ -5,7 +5,6 @@ */ using System; -using System.Collections.Immutable; using System.Diagnostics; using System.Threading.Tasks; using AspNet.Security.OpenIdConnect.Extensions; @@ -13,16 +12,16 @@ using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Server; using JetBrains.Annotations; using Microsoft.Extensions.Logging; -using OpenIddict.Core; +using OpenIddict.Abstractions; -namespace OpenIddict +namespace OpenIddict.Server { - public partial class OpenIddictProvider : OpenIdConnectServerProvider + public partial class OpenIddictServerProvider : OpenIdConnectServerProvider where TApplication : class where TAuthorization : class where TScope : class where TToken : class { public override async Task ValidateTokenRequest([NotNull] ValidateTokenRequestContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; // Reject token requests that don't specify a supported grant type. if (!options.GrantTypes.Contains(context.Request.GrantType)) @@ -274,7 +273,7 @@ namespace OpenIddict public override async Task HandleTokenRequest([NotNull] HandleTokenRequestContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; if (context.Ticket != null) { diff --git a/src/OpenIddict.Server/Internal/OpenIddictProvider.Helpers.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Helpers.cs similarity index 98% rename from src/OpenIddict.Server/Internal/OpenIddictProvider.Helpers.cs rename to src/OpenIddict.Server/Internal/OpenIddictServerProvider.Helpers.cs index f4cefc32..a24bba26 100644 --- a/src/OpenIddict.Server/Internal/OpenIddictProvider.Helpers.cs +++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Helpers.cs @@ -14,19 +14,19 @@ using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Server; using JetBrains.Annotations; using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json.Linq; +using OpenIddict.Abstractions; using OpenIddict.Core; -namespace OpenIddict +namespace OpenIddict.Server { - public partial class OpenIddictProvider : OpenIdConnectServerProvider + public partial class OpenIddictServerProvider : OpenIdConnectServerProvider where TApplication : class where TAuthorization : class where TScope : class where TToken : class { private async Task CreateAuthorizationAsync([NotNull] AuthenticationTicket ticket, - [NotNull] OpenIddictOptions options, [NotNull] OpenIdConnectRequest request) + [NotNull] OpenIddictServerOptions options, [NotNull] OpenIdConnectRequest request) { var descriptor = new OpenIddictAuthorizationDescriptor { @@ -81,7 +81,7 @@ namespace OpenIddict private async Task CreateTokenAsync( [NotNull] string type, [NotNull] AuthenticationTicket ticket, - [NotNull] OpenIddictOptions options, + [NotNull] OpenIddictServerOptions options, [NotNull] OpenIdConnectRequest request, [NotNull] ISecureDataFormat format) { @@ -214,7 +214,7 @@ namespace OpenIddict private async Task ReceiveTokenAsync( [NotNull] string type, [NotNull] string value, - [NotNull] OpenIddictOptions options, + [NotNull] OpenIddictServerOptions options, [NotNull] OpenIdConnectRequest request, [NotNull] ISecureDataFormat format) { @@ -478,7 +478,7 @@ namespace OpenIddict } private async Task TryExtendTokenAsync( - [NotNull] TToken token, [NotNull] AuthenticationTicket ticket, [NotNull] OpenIddictOptions options) + [NotNull] TToken token, [NotNull] AuthenticationTicket ticket, [NotNull] OpenIddictServerOptions options) { var identifier = ticket.GetTokenId(); Debug.Assert(!string.IsNullOrEmpty(identifier), "The token identifier shouldn't be null or empty."); diff --git a/src/OpenIddict.Server/Internal/OpenIddictProvider.Introspection.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Introspection.cs similarity index 96% rename from src/OpenIddict.Server/Internal/OpenIddictProvider.Introspection.cs rename to src/OpenIddict.Server/Internal/OpenIddictServerProvider.Introspection.cs index d1f3c76e..683619a8 100644 --- a/src/OpenIddict.Server/Internal/OpenIddictProvider.Introspection.cs +++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Introspection.cs @@ -12,11 +12,11 @@ using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Server; using JetBrains.Annotations; using Microsoft.Extensions.Logging; -using OpenIddict.Core; +using OpenIddict.Abstractions; -namespace OpenIddict +namespace OpenIddict.Server { - public partial class OpenIddictProvider : OpenIdConnectServerProvider + public partial class OpenIddictServerProvider : OpenIdConnectServerProvider where TApplication : class where TAuthorization : class where TScope : class where TToken : class { public override Task ExtractIntrospectionRequest([NotNull] ExtractIntrospectionRequestContext context) @@ -112,7 +112,7 @@ namespace OpenIddict public override async Task HandleIntrospectionRequest([NotNull] HandleIntrospectionRequestContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; Debug.Assert(context.Ticket != null, "The authentication ticket shouldn't be null."); Debug.Assert(!string.IsNullOrEmpty(context.Request.ClientId), "The client_id parameter shouldn't be null."); diff --git a/src/OpenIddict.Server/Internal/OpenIddictProvider.Revocation.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Revocation.cs similarity index 96% rename from src/OpenIddict.Server/Internal/OpenIddictProvider.Revocation.cs rename to src/OpenIddict.Server/Internal/OpenIddictServerProvider.Revocation.cs index 09a610d3..935ffbf4 100644 --- a/src/OpenIddict.Server/Internal/OpenIddictProvider.Revocation.cs +++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Revocation.cs @@ -12,16 +12,16 @@ using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Server; using JetBrains.Annotations; using Microsoft.Extensions.Logging; -using OpenIddict.Core; +using OpenIddict.Abstractions; -namespace OpenIddict +namespace OpenIddict.Server { - public partial class OpenIddictProvider : OpenIdConnectServerProvider + public partial class OpenIddictServerProvider : OpenIdConnectServerProvider where TApplication : class where TAuthorization : class where TScope : class where TToken : class { public override async Task ValidateRevocationRequest([NotNull] ValidateRevocationRequestContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; Debug.Assert(!options.DisableTokenRevocation, "Token revocation support shouldn't be disabled at this stage."); @@ -163,7 +163,7 @@ namespace OpenIddict public override async Task HandleRevocationRequest([NotNull] HandleRevocationRequestContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; Debug.Assert(context.Ticket != null, "The authentication ticket shouldn't be null."); diff --git a/src/OpenIddict.Server/Internal/OpenIddictProvider.Serialization.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Serialization.cs similarity index 90% rename from src/OpenIddict.Server/Internal/OpenIddictProvider.Serialization.cs rename to src/OpenIddict.Server/Internal/OpenIddictServerProvider.Serialization.cs index e5257eeb..dc62d97c 100644 --- a/src/OpenIddict.Server/Internal/OpenIddictProvider.Serialization.cs +++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Serialization.cs @@ -10,14 +10,14 @@ using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Server; using JetBrains.Annotations; -namespace OpenIddict +namespace OpenIddict.Server { - public partial class OpenIddictProvider : OpenIdConnectServerProvider + public partial class OpenIddictServerProvider : OpenIdConnectServerProvider where TApplication : class where TAuthorization : class where TScope : class where TToken : class { public override async Task DeserializeAccessToken([NotNull] DeserializeAccessTokenContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; if (options.DisableTokenRevocation) { return; @@ -38,7 +38,7 @@ namespace OpenIddict public override async Task DeserializeAuthorizationCode([NotNull] DeserializeAuthorizationCodeContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; if (options.DisableTokenRevocation) { return; @@ -55,7 +55,7 @@ namespace OpenIddict public override async Task DeserializeRefreshToken([NotNull] DeserializeRefreshTokenContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; if (options.DisableTokenRevocation) { return; @@ -72,7 +72,7 @@ namespace OpenIddict public override async Task SerializeAccessToken([NotNull] SerializeAccessTokenContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; if (options.DisableTokenRevocation) { return; @@ -96,7 +96,7 @@ namespace OpenIddict public override async Task SerializeAuthorizationCode([NotNull] SerializeAuthorizationCodeContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; if (options.DisableTokenRevocation) { return; @@ -122,7 +122,7 @@ namespace OpenIddict public override async Task SerializeRefreshToken([NotNull] SerializeRefreshTokenContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; if (options.DisableTokenRevocation) { return; diff --git a/src/OpenIddict.Server/Internal/OpenIddictProvider.Session.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Session.cs similarity index 96% rename from src/OpenIddict.Server/Internal/OpenIddictProvider.Session.cs rename to src/OpenIddict.Server/Internal/OpenIddictServerProvider.Session.cs index 3787b2ef..68148475 100644 --- a/src/OpenIddict.Server/Internal/OpenIddictProvider.Session.cs +++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Session.cs @@ -18,16 +18,16 @@ using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json; using Newtonsoft.Json.Bson; using Newtonsoft.Json.Linq; -using OpenIddict.Core; +using OpenIddict.Abstractions; -namespace OpenIddict +namespace OpenIddict.Server { - public partial class OpenIddictProvider : OpenIdConnectServerProvider + public partial class OpenIddictServerProvider : OpenIdConnectServerProvider where TApplication : class where TAuthorization : class where TScope : class where TToken : class { public override async Task ExtractLogoutRequest([NotNull] ExtractLogoutRequestContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; // If a request_id parameter can be found in the logout request, // restore the complete logout request from the distributed cache. @@ -127,7 +127,7 @@ namespace OpenIddict public override async Task HandleLogoutRequest([NotNull] HandleLogoutRequestContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; // If no request_id parameter can be found in the current request, assume the OpenID Connect // request was not serialized yet and store the entire payload in the distributed cache @@ -177,7 +177,7 @@ namespace OpenIddict public override async Task ApplyLogoutResponse([NotNull] ApplyLogoutResponseContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; // Remove the logout request from the distributed cache. if (options.EnableRequestCaching && !string.IsNullOrEmpty(context.Request.RequestId)) diff --git a/src/OpenIddict.Server/Internal/OpenIddictProvider.Userinfo.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Userinfo.cs similarity index 88% rename from src/OpenIddict.Server/Internal/OpenIddictProvider.Userinfo.cs rename to src/OpenIddict.Server/Internal/OpenIddictServerProvider.Userinfo.cs index 39145f07..779c2d84 100644 --- a/src/OpenIddict.Server/Internal/OpenIddictProvider.Userinfo.cs +++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.Userinfo.cs @@ -8,9 +8,9 @@ using System.Threading.Tasks; using AspNet.Security.OpenIdConnect.Server; using JetBrains.Annotations; -namespace OpenIddict +namespace OpenIddict.Server { - public partial class OpenIddictProvider : OpenIdConnectServerProvider + public partial class OpenIddictServerProvider : OpenIdConnectServerProvider where TApplication : class where TAuthorization : class where TScope : class where TToken : class { public override Task ExtractUserinfoRequest([NotNull] ExtractUserinfoRequestContext context) diff --git a/src/OpenIddict.Server/Internal/OpenIddictProvider.cs b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.cs similarity index 95% rename from src/OpenIddict.Server/Internal/OpenIddictProvider.cs rename to src/OpenIddict.Server/Internal/OpenIddictServerProvider.cs index 7efb0d70..731b3b56 100644 --- a/src/OpenIddict.Server/Internal/OpenIddictProvider.cs +++ b/src/OpenIddict.Server/Internal/OpenIddictServerProvider.cs @@ -14,22 +14,23 @@ using AspNet.Security.OpenIdConnect.Server; using JetBrains.Annotations; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; +using OpenIddict.Abstractions; using OpenIddict.Core; -namespace OpenIddict +namespace OpenIddict.Server { /// /// Provides the logic necessary to extract, validate and handle OpenID Connect requests. /// [EditorBrowsable(EditorBrowsableState.Never)] - public partial class OpenIddictProvider : OpenIdConnectServerProvider + public partial class OpenIddictServerProvider : OpenIdConnectServerProvider where TApplication : class where TAuthorization : class where TScope : class where TToken : class { /// - /// Creates a new instance of the class. + /// Creates a new instance of the class. /// - public OpenIddictProvider( - [NotNull] ILogger> logger, + public OpenIddictServerProvider( + [NotNull] ILogger> logger, [NotNull] OpenIddictApplicationManager applications, [NotNull] OpenIddictAuthorizationManager authorizations, [NotNull] OpenIddictScopeManager scopes, @@ -86,7 +87,7 @@ namespace OpenIddict public override async Task ProcessSigninResponse([NotNull] ProcessSigninResponseContext context) { - var options = (OpenIddictOptions) context.Options; + var options = (OpenIddictServerOptions) context.Options; Debug.Assert(context.Request.IsAuthorizationRequest() || context.Request.IsTokenRequest(), diff --git a/src/OpenIddict.Server/OpenIddictExtensions.cs b/src/OpenIddict.Server/OpenIddictExtensions.cs deleted file mode 100644 index 33443e64..00000000 --- a/src/OpenIddict.Server/OpenIddictExtensions.cs +++ /dev/null @@ -1,1008 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * See https://github.com/openiddict/openiddict-core for more information concerning - * the license and the contributors participating to this project. - */ - -using System; -using System.Collections.Generic; -using System.IdentityModel.Tokens.Jwt; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using AspNet.Security.OpenIdConnect.Primitives; -using AspNet.Security.OpenIdConnect.Server; -using JetBrains.Annotations; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.DataProtection; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; -using Microsoft.IdentityModel.Tokens; -using OpenIddict; -using OpenIddict.Core; - -namespace Microsoft.Extensions.DependencyInjection -{ - public static class OpenIddictExtensions - { - /// - /// Amends the default OpenIddict configuration. - /// - /// The services builder used by OpenIddict to register new services. - /// The delegate used to configure the OpenIddict options. - /// This extension can be safely called multiple times. - /// The . - public static OpenIddictBuilder Configure( - [NotNull] this OpenIddictBuilder builder, - [NotNull] Action configuration) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (configuration == null) - { - throw new ArgumentNullException(nameof(configuration)); - } - - // Register the OpenIddict handler/provider. - builder.Services.TryAddScoped(); - builder.Services.TryAddScoped( - typeof(OpenIdConnectServerProvider), - typeof(OpenIddictProvider<,,,>).MakeGenericType( - /* TApplication: */ builder.ApplicationType, - /* TAuthorization: */ builder.AuthorizationType, - /* TScope: */ builder.ScopeType, - /* TToken: */ builder.TokenType)); - - // Register the options initializers used by the OpenID Connect server handler and OpenIddict. - // Note: TryAddEnumerable() is used here to ensure the initializers are only registered once. - builder.Services.TryAddEnumerable( - ServiceDescriptor.Singleton, - OpenIddictInitializer>()); - - builder.Services.TryAddEnumerable( - ServiceDescriptor.Singleton, - OpenIdConnectServerInitializer>()); - - // Register the OpenID Connect server handler in the authentication options, - // so it can be discovered by the default authentication handler provider. - builder.Services.Configure(options => - { - // Note: similarly to Identity, OpenIddict should be registered only once. - // To prevent multiple schemes from being registered, a check is made here. - if (options.SchemeMap.ContainsKey(OpenIdConnectServerDefaults.AuthenticationScheme)) - { - return; - } - - options.AddScheme(OpenIdConnectServerDefaults.AuthenticationScheme, scheme => - { - scheme.HandlerType = typeof(OpenIddictHandler); - }); - }); - - builder.Services.Configure(OpenIdConnectServerDefaults.AuthenticationScheme, configuration); - - return builder; - } - - /// - /// Registers (and generates if necessary) a user-specific development - /// certificate used to sign the JWT tokens issued by OpenIddict. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder AddDevelopmentSigningCertificate( - [NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.SigningCredentials.AddDevelopmentCertificate()); - } - - /// - /// Registers (and generates if necessary) a user-specific development - /// certificate used to sign the JWT tokens issued by OpenIddict. - /// - /// The services builder used by OpenIddict to register new services. - /// The subject name associated with the certificate. - /// The . - public static OpenIddictBuilder AddDevelopmentSigningCertificate( - [NotNull] this OpenIddictBuilder builder, [NotNull] X500DistinguishedName subject) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (subject == null) - { - throw new ArgumentNullException(nameof(subject)); - } - - return builder.Configure(options => options.SigningCredentials.AddDevelopmentCertificate(subject)); - } - - /// - /// Registers a new ephemeral key used to sign the JWT tokens issued by OpenIddict: the key - /// is discarded when the application shuts down and tokens signed using this key are - /// automatically invalidated. This method should only be used during development. - /// On production, using a X.509 certificate stored in the machine store is recommended. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder AddEphemeralSigningKey([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.SigningCredentials.AddEphemeralKey()); - } - - /// - /// Registers a new ephemeral key used to sign the JWT tokens issued by OpenIddict: the key - /// is discarded when the application shuts down and tokens signed using this key are - /// automatically invalidated. This method should only be used during development. - /// On production, using a X.509 certificate stored in the machine store is recommended. - /// - /// The services builder used by OpenIddict to register new services. - /// The algorithm associated with the signing key. - /// The . - public static OpenIddictBuilder AddEphemeralSigningKey( - [NotNull] this OpenIddictBuilder builder, [NotNull] string algorithm) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (string.IsNullOrEmpty(algorithm)) - { - throw new ArgumentException("The algorithm cannot be null or empty.", nameof(algorithm)); - } - - return builder.Configure(options => options.SigningCredentials.AddEphemeralKey(algorithm)); - } - - /// - /// Registers a used to encrypt the JWT access tokens issued by OpenIddict. - /// - /// The services builder used by OpenIddict to register new services. - /// The security key. - /// The . - public static OpenIddictBuilder AddEncryptingKey( - [NotNull] this OpenIddictBuilder builder, [NotNull] SecurityKey key) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - return builder.Configure(options => options.EncryptingCredentials.AddKey(key)); - } - - /// - /// Registers a that is used to sign the JWT tokens issued by OpenIddict. - /// - /// The services builder used by OpenIddict to register new services. - /// The certificate used to sign the security tokens issued by the server. - /// The . - public static OpenIddictBuilder AddSigningCertificate( - [NotNull] this OpenIddictBuilder builder, - [NotNull] X509Certificate2 certificate) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (certificate == null) - { - throw new ArgumentNullException(nameof(certificate)); - } - - if (!certificate.HasPrivateKey) - { - throw new InvalidOperationException("The certificate doesn't contain the required private key."); - } - - return builder.Configure(options => options.SigningCredentials.AddCertificate(certificate)); - } - - /// - /// Registers a retrieved from an - /// embedded resource and used to sign the JWT tokens issued by OpenIddict. - /// - /// The services builder used by OpenIddict to register new services. - /// The assembly containing the certificate. - /// The name of the embedded resource. - /// The password used to open the certificate. - /// The . - public static OpenIddictBuilder AddSigningCertificate( - [NotNull] this OpenIddictBuilder builder, [NotNull] Assembly assembly, - [NotNull] string resource, [NotNull] string password) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (assembly == null) - { - throw new ArgumentNullException(nameof(assembly)); - } - - if (string.IsNullOrEmpty(resource)) - { - throw new ArgumentNullException(nameof(resource)); - } - - if (string.IsNullOrEmpty(password)) - { - throw new ArgumentNullException(nameof(password)); - } - - return builder.Configure(options => options.SigningCredentials.AddCertificate(assembly, resource, password)); - } - - /// - /// Registers a extracted from a - /// stream and used to sign the JWT tokens issued by OpenIddict. - /// - /// The services builder used by OpenIddict to register new services. - /// The stream containing the certificate. - /// The password used to open the certificate. - /// The . - public static OpenIddictBuilder AddSigningCertificate( - [NotNull] this OpenIddictBuilder builder, - [NotNull] Stream stream, [NotNull] string password) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (string.IsNullOrEmpty(password)) - { - throw new ArgumentNullException(nameof(password)); - } - - return builder.Configure(options => options.SigningCredentials.AddCertificate(stream, password)); - } - - /// - /// Registers a extracted from a - /// stream and used to sign the JWT tokens issued by OpenIddict. - /// - /// The services builder used by OpenIddict to register new services. - /// The stream containing the certificate. - /// The password used to open the certificate. - /// - /// An enumeration of flags indicating how and where - /// to store the private key of the certificate. - /// - /// The . - public static OpenIddictBuilder AddSigningCertificate( - [NotNull] this OpenIddictBuilder builder, [NotNull] Stream stream, - [NotNull] string password, X509KeyStorageFlags flags) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (stream == null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (string.IsNullOrEmpty(password)) - { - throw new ArgumentNullException(nameof(password)); - } - - return builder.Configure(options => options.SigningCredentials.AddCertificate(stream, password, flags)); - } - - /// - /// Registers a retrieved from the X.509 - /// machine store and used to sign the JWT tokens issued by OpenIddict. - /// - /// The services builder used by OpenIddict to register new services. - /// The thumbprint of the certificate used to identify it in the X.509 store. - /// The . - public static OpenIddictBuilder AddSigningCertificate( - [NotNull] this OpenIddictBuilder builder, [NotNull] string thumbprint) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (string.IsNullOrEmpty(thumbprint)) - { - throw new ArgumentNullException(nameof(thumbprint)); - } - - return builder.Configure(options => options.SigningCredentials.AddCertificate(thumbprint)); - } - - /// - /// Registers a retrieved from the given - /// X.509 store and used to sign the JWT tokens issued by OpenIddict. - /// - /// The services builder used by OpenIddict to register new services. - /// The thumbprint of the certificate used to identify it in the X.509 store. - /// The name of the X.509 store. - /// The location of the X.509 store. - /// The . - public static OpenIddictBuilder AddSigningCertificate( - [NotNull] this OpenIddictBuilder builder, - [NotNull] string thumbprint, StoreName name, StoreLocation location) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (string.IsNullOrEmpty(thumbprint)) - { - throw new ArgumentNullException(nameof(thumbprint)); - } - - return builder.Configure(options => options.SigningCredentials.AddCertificate(thumbprint, name, location)); - } - - /// - /// Registers a used to sign the JWT tokens issued by OpenIddict. - /// Note: using asymmetric keys is recommended on production. - /// - /// The services builder used by OpenIddict to register new services. - /// The security key. - /// The . - public static OpenIddictBuilder AddSigningKey( - [NotNull] this OpenIddictBuilder builder, [NotNull] SecurityKey key) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (key == null) - { - throw new ArgumentNullException(nameof(key)); - } - - return builder.Configure(options => options.SigningCredentials.AddKey(key)); - } - - /// - /// Enables authorization code flow support. For more information - /// about this specific OAuth2/OpenID Connect flow, visit - /// https://tools.ietf.org/html/rfc6749#section-4.1 and - /// http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder AllowAuthorizationCodeFlow([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.AuthorizationCode)); - } - - /// - /// Enables client credentials flow support. For more information about this - /// specific OAuth2 flow, visit https://tools.ietf.org/html/rfc6749#section-4.4. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder AllowClientCredentialsFlow([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.ClientCredentials)); - } - - /// - /// Enables custom grant type support. - /// - /// The services builder used by OpenIddict to register new services. - /// The grant type associated with the flow. - /// The . - public static OpenIddictBuilder AllowCustomFlow( - [NotNull] this OpenIddictBuilder builder, [NotNull] string type) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (string.IsNullOrEmpty(type)) - { - throw new ArgumentException("The grant type cannot be null or empty.", nameof(type)); - } - - return builder.Configure(options => options.GrantTypes.Add(type)); - } - - /// - /// Enables implicit flow support. For more information - /// about this specific OAuth2/OpenID Connect flow, visit - /// https://tools.ietf.org/html/rfc6749#section-4.2 and - /// http://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder AllowImplicitFlow([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.Implicit)); - } - - /// - /// Enables password flow support. For more information about this specific - /// OAuth2 flow, visit https://tools.ietf.org/html/rfc6749#section-4.3. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder AllowPasswordFlow([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.Password)); - } - - /// - /// Enables refresh token flow support. For more information about this - /// specific OAuth2 flow, visit https://tools.ietf.org/html/rfc6749#section-6. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder AllowRefreshTokenFlow([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.RefreshToken)); - } - - /// - /// Disables the configuration endpoint. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder DisableConfigurationEndpoint([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.ConfigurationEndpointPath = PathString.Empty); - } - - /// - /// Disables the cryptography endpoint. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder DisableCryptographyEndpoint([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.CryptographyEndpointPath = PathString.Empty); - } - - /// - /// Disables the HTTPS requirement during development. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder DisableHttpsRequirement([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.AllowInsecureHttp = true); - } - - /// - /// Disables sliding expiration. When using this option, refresh tokens - /// are issued with a fixed expiration date: when it expires, a complete - /// authorization flow must be started to retrieve a new refresh token. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder DisableSlidingExpiration([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.UseSlidingExpiration = false); - } - - /// - /// Disables token revocation, so that authorization code and - /// refresh tokens are never stored and cannot be revoked. - /// Using this option is generally not recommended. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder DisableTokenRevocation([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.DisableTokenRevocation = true); - } - - /// - /// Enables the authorization endpoint. - /// - /// The services builder used by OpenIddict to register new services. - /// The relative path of the authorization endpoint. - /// The . - public static OpenIddictBuilder EnableAuthorizationEndpoint( - [NotNull] this OpenIddictBuilder builder, PathString path) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (!path.HasValue) - { - throw new ArgumentException("The path cannot be empty.", nameof(path)); - } - - return builder.Configure(options => options.AuthorizationEndpointPath = path); - } - - /// - /// Enables the introspection endpoint. - /// - /// The services builder used by OpenIddict to register new services. - /// The relative path of the logout endpoint. - /// The . - public static OpenIddictBuilder EnableIntrospectionEndpoint( - [NotNull] this OpenIddictBuilder builder, PathString path) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (!path.HasValue) - { - throw new ArgumentException("The path cannot be empty.", nameof(path)); - } - - return builder.Configure(options => options.IntrospectionEndpointPath = path); - } - - /// - /// Enables the logout endpoint. - /// - /// The services builder used by OpenIddict to register new services. - /// The relative path of the logout endpoint. - /// The . - public static OpenIddictBuilder EnableLogoutEndpoint( - [NotNull] this OpenIddictBuilder builder, PathString path) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (!path.HasValue) - { - throw new ArgumentException("The path cannot be empty.", nameof(path)); - } - - return builder.Configure(options => options.LogoutEndpointPath = path); - } - - /// - /// Enables request caching, so that both authorization and logout requests - /// are automatically stored in the distributed cache, which allows flowing - /// large payloads across requests. Enabling this option is recommended - /// when using external authentication providers or when large GET or POST - /// OpenID Connect authorization requests support is required. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder EnableRequestCaching([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.EnableRequestCaching = true); - } - - /// - /// Enables the revocation endpoint. - /// - /// The services builder used by OpenIddict to register new services. - /// The relative path of the revocation endpoint. - /// The . - public static OpenIddictBuilder EnableRevocationEndpoint( - [NotNull] this OpenIddictBuilder builder, PathString path) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (!path.HasValue) - { - throw new ArgumentException("The path cannot be empty.", nameof(path)); - } - - return builder.Configure(options => options.RevocationEndpointPath = path); - } - - /// - /// Rejects authorization and token requests that specify scopes that have not been - /// registered using or - /// . - /// - /// The services builder used by OpenIddict to register new services. - public static OpenIddictBuilder EnableScopeValidation([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.EnableScopeValidation = true); - } - - /// - /// Enables the token endpoint. - /// - /// The services builder used by OpenIddict to register new services. - /// The relative path of the token endpoint. - /// The . - public static OpenIddictBuilder EnableTokenEndpoint( - [NotNull] this OpenIddictBuilder builder, PathString path) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (!path.HasValue) - { - throw new ArgumentException("The path cannot be empty.", nameof(path)); - } - - return builder.Configure(options => options.TokenEndpointPath = path); - } - - /// - /// Enables the userinfo endpoint. - /// - /// The services builder used by OpenIddict to register new services. - /// The relative path of the userinfo endpoint. - /// The . - public static OpenIddictBuilder EnableUserinfoEndpoint( - [NotNull] this OpenIddictBuilder builder, PathString path) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (!path.HasValue) - { - throw new ArgumentException("The path cannot be empty.", nameof(path)); - } - - return builder.Configure(options => options.UserinfoEndpointPath = path); - } - - /// - /// Makes client identification mandatory so that token and revocation - /// requests that don't specify a client_id are automatically rejected. - /// Note: enabling this option doesn't prevent public clients from using - /// the token and revocation endpoints, but specifying a client_id is required. - /// - /// The services builder used by OpenIddict to register new services. - public static OpenIddictBuilder RequireClientIdentification([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.RequireClientIdentification = true); - } - - /// - /// Sets the access token lifetime, after which client applications must retrieve - /// a new access token by making a grant_type=refresh_token token request - /// or a prompt=none authorization request, depending on the selected flow. - /// Using long-lived access tokens or tokens that never expire is not recommended. - /// - /// The services builder used by OpenIddict to register new services. - /// The access token lifetime. - /// The . - public static OpenIddictBuilder SetAccessTokenLifetime( - [NotNull] this OpenIddictBuilder builder, TimeSpan lifetime) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.AccessTokenLifetime = lifetime); - } - - /// - /// Sets the authorization code lifetime, after which client applications - /// are unable to send a grant_type=authorization_code token request. - /// Using short-lived authorization codes is strongly recommended. - /// - /// The services builder used by OpenIddict to register new services. - /// The authorization code lifetime. - /// The . - public static OpenIddictBuilder SetAuthorizationCodeLifetime( - [NotNull] this OpenIddictBuilder builder, TimeSpan lifetime) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.AuthorizationCodeLifetime = lifetime); - } - - /// - /// Sets the identity token lifetime, after which client - /// applications should refuse processing identity tokens. - /// - /// The services builder used by OpenIddict to register new services. - /// The identity token lifetime. - /// The . - public static OpenIddictBuilder SetIdentityTokenLifetime( - [NotNull] this OpenIddictBuilder builder, TimeSpan lifetime) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.IdentityTokenLifetime = lifetime); - } - - /// - /// Sets the refresh token lifetime, after which client applications must get - /// a new authorization from the user. When sliding expiration is enabled, - /// a new refresh token is always issued to the client application, - /// which prolongs the validity period of the refresh token. - /// - /// The services builder used by OpenIddict to register new services. - /// The refresh token lifetime. - /// The . - public static OpenIddictBuilder SetRefreshTokenLifetime( - [NotNull] this OpenIddictBuilder builder, TimeSpan lifetime) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.RefreshTokenLifetime = lifetime); - } - - /// - /// Sets the issuer address, which is used as the base address - /// for the endpoint URIs returned from the discovery endpoint. - /// - /// The services builder used by OpenIddict to register new services. - /// The issuer address. - /// The . - public static OpenIddictBuilder SetIssuer( - [NotNull] this OpenIddictBuilder builder, [NotNull] Uri address) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (address == null) - { - throw new ArgumentNullException(nameof(address)); - } - - return builder.Configure(options => options.Issuer = address); - } - - /// - /// Registers the specified claims as supported claims so - /// they can be returned as part of the discovery document. - /// - /// The services builder used by OpenIddict to register new services. - /// The supported claims. - /// The . - public static OpenIddictBuilder RegisterClaims( - [NotNull] this OpenIddictBuilder builder, [NotNull] params string[] claims) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (claims == null) - { - throw new ArgumentNullException(nameof(claims)); - } - - if (claims.Any(claim => string.IsNullOrEmpty(claim))) - { - throw new ArgumentException("Claims cannot be null or empty.", nameof(claims)); - } - - return builder.Configure(options => options.Claims.UnionWith(claims)); - } - - /// - /// Registers the specified scopes as supported scopes so - /// they can be returned as part of the discovery document. - /// - /// The services builder used by OpenIddict to register new services. - /// The supported scopes. - /// The . - public static OpenIddictBuilder RegisterScopes( - [NotNull] this OpenIddictBuilder builder, [NotNull] params string[] scopes) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (scopes == null) - { - throw new ArgumentNullException(nameof(scopes)); - } - - if (scopes.Any(scope => string.IsNullOrEmpty(scope))) - { - throw new ArgumentException("Scopes cannot be null or empty.", nameof(scopes)); - } - - return builder.Configure(options => options.Scopes.UnionWith(scopes)); - } - - /// - /// Configures OpenIddict to use a specific data protection provider - /// instead of relying on the default instance provided by the DI container. - /// - /// The services builder used by OpenIddict to register new services. - /// The data protection provider used to create token protectors. - /// The . - public static OpenIddictBuilder UseDataProtectionProvider( - [NotNull] this OpenIddictBuilder builder, [NotNull] IDataProtectionProvider provider) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (provider == null) - { - throw new ArgumentNullException(nameof(provider)); - } - - return builder.Configure(options => options.DataProtectionProvider = provider); - } - - /// - /// Sets JWT as the default token format for access tokens. - /// Note: this option cannot be used when using reference tokens. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder UseJsonWebTokens([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => - { - options.AccessTokenHandler = new JwtSecurityTokenHandler - { - InboundClaimTypeMap = new Dictionary(), - OutboundClaimTypeMap = new Dictionary() - }; - }); - } - - /// - /// Configures OpenIddict to use reference tokens, so that authorization codes, - /// access tokens and refresh tokens are stored as ciphertext in the database - /// (only an identifier is returned to the client application). Enabling this option - /// is useful to keep track of all the issued tokens, when storing a very large - /// number of claims in the authorization codes, access tokens and refresh tokens - /// or when immediate revocation of reference access tokens is desired. - /// Note: this option cannot be used when configuring JWT as the access token format. - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder UseReferenceTokens([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.UseReferenceTokens = true); - } - - /// - /// Configures OpenIddict to use rolling refresh tokens. When this option is enabled, - /// a new refresh token is always issued for each refresh token request (and the previous - /// one is automatically revoked unless token revocation was explicitly disabled). - /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder UseRollingTokens([NotNull] this OpenIddictBuilder builder) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.UseRollingTokens = true); - } - } -} \ No newline at end of file diff --git a/src/OpenIddict.Server/OpenIddictServerBuilder.cs b/src/OpenIddict.Server/OpenIddictServerBuilder.cs new file mode 100644 index 00000000..f300732f --- /dev/null +++ b/src/OpenIddict.Server/OpenIddictServerBuilder.cs @@ -0,0 +1,653 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IdentityModel.Tokens.Jwt; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using AspNet.Security.OpenIdConnect.Primitives; +using AspNet.Security.OpenIdConnect.Server; +using JetBrains.Annotations; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.Http; +using Microsoft.IdentityModel.Tokens; +using OpenIddict.Core; +using OpenIddict.Server; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Exposes the necessary methods required to configure the OpenIddict server services. + /// + public class OpenIddictServerBuilder + { + /// + /// Initializes a new instance of . + /// + /// The services collection. + public OpenIddictServerBuilder([NotNull] IServiceCollection services) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + Services = services; + } + + /// + /// Gets the services collection. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public IServiceCollection Services { get; } + + /// + /// Amends the default OpenIddict server configuration. + /// + /// The delegate used to configure the OpenIddict options. + /// This extension can be safely called multiple times. + /// The . + public OpenIddictServerBuilder Configure([NotNull] Action configuration) + { + if (configuration == null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + Services.Configure(OpenIdConnectServerDefaults.AuthenticationScheme, configuration); + + return this; + } + + /// + /// Registers (and generates if necessary) a user-specific development + /// certificate used to sign the JWT tokens issued by OpenIddict. + /// + /// The . + public OpenIddictServerBuilder AddDevelopmentSigningCertificate() + => Configure(options => options.SigningCredentials.AddDevelopmentCertificate()); + + /// + /// Registers (and generates if necessary) a user-specific development + /// certificate used to sign the JWT tokens issued by OpenIddict. + /// + /// The subject name associated with the certificate. + /// The . + public OpenIddictServerBuilder AddDevelopmentSigningCertificate([NotNull] X500DistinguishedName subject) + { + if (subject == null) + { + throw new ArgumentNullException(nameof(subject)); + } + + return Configure(options => options.SigningCredentials.AddDevelopmentCertificate(subject)); + } + + /// + /// Registers a new ephemeral key used to sign the JWT tokens issued by OpenIddict: the key + /// is discarded when the application shuts down and tokens signed using this key are + /// automatically invalidated. This method should only be used during development. + /// On production, using a X.509 certificate stored in the machine store is recommended. + /// + /// The . + public OpenIddictServerBuilder AddEphemeralSigningKey() + => Configure(options => options.SigningCredentials.AddEphemeralKey()); + + /// + /// Registers a new ephemeral key used to sign the JWT tokens issued by OpenIddict: the key + /// is discarded when the application shuts down and tokens signed using this key are + /// automatically invalidated. This method should only be used during development. + /// On production, using a X.509 certificate stored in the machine store is recommended. + /// + /// The algorithm associated with the signing key. + /// The . + public OpenIddictServerBuilder AddEphemeralSigningKey([NotNull] string algorithm) + { + if (string.IsNullOrEmpty(algorithm)) + { + throw new ArgumentException("The algorithm cannot be null or empty.", nameof(algorithm)); + } + + return Configure(options => options.SigningCredentials.AddEphemeralKey(algorithm)); + } + + /// + /// Registers a used to encrypt the JWT access tokens issued by OpenIddict. + /// + /// The security key. + /// The . + public OpenIddictServerBuilder AddEncryptingKey([NotNull] SecurityKey key) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + return Configure(options => options.EncryptingCredentials.AddKey(key)); + } + + /// + /// Registers a that is used to sign the JWT tokens issued by OpenIddict. + /// + /// The certificate used to sign the security tokens issued by the server. + /// The . + public OpenIddictServerBuilder AddSigningCertificate([NotNull] X509Certificate2 certificate) + { + if (certificate == null) + { + throw new ArgumentNullException(nameof(certificate)); + } + + if (!certificate.HasPrivateKey) + { + throw new InvalidOperationException("The certificate doesn't contain the required private key."); + } + + return Configure(options => options.SigningCredentials.AddCertificate(certificate)); + } + + /// + /// Registers a retrieved from an + /// embedded resource and used to sign the JWT tokens issued by OpenIddict. + /// + /// The assembly containing the certificate. + /// The name of the embedded resource. + /// The password used to open the certificate. + /// The . + public OpenIddictServerBuilder AddSigningCertificate( + [NotNull] Assembly assembly, [NotNull] string resource, [NotNull] string password) + { + if (assembly == null) + { + throw new ArgumentNullException(nameof(assembly)); + } + + if (string.IsNullOrEmpty(resource)) + { + throw new ArgumentNullException(nameof(resource)); + } + + if (string.IsNullOrEmpty(password)) + { + throw new ArgumentNullException(nameof(password)); + } + + return Configure(options => options.SigningCredentials.AddCertificate(assembly, resource, password)); + } + + /// + /// Registers a extracted from a + /// stream and used to sign the JWT tokens issued by OpenIddict. + /// + /// The stream containing the certificate. + /// The password used to open the certificate. + /// The . + public OpenIddictServerBuilder AddSigningCertificate([NotNull] Stream stream, [NotNull] string password) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (string.IsNullOrEmpty(password)) + { + throw new ArgumentNullException(nameof(password)); + } + + return Configure(options => options.SigningCredentials.AddCertificate(stream, password)); + } + + /// + /// Registers a extracted from a + /// stream and used to sign the JWT tokens issued by OpenIddict. + /// + /// The stream containing the certificate. + /// The password used to open the certificate. + /// + /// An enumeration of flags indicating how and where + /// to store the private key of the certificate. + /// + /// The . + public OpenIddictServerBuilder AddSigningCertificate( + [NotNull] Stream stream, [NotNull] string password, X509KeyStorageFlags flags) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (string.IsNullOrEmpty(password)) + { + throw new ArgumentNullException(nameof(password)); + } + + return Configure(options => options.SigningCredentials.AddCertificate(stream, password, flags)); + } + + /// + /// Registers a retrieved from the X.509 + /// machine store and used to sign the JWT tokens issued by OpenIddict. + /// + /// The thumbprint of the certificate used to identify it in the X.509 store. + /// The . + public OpenIddictServerBuilder AddSigningCertificate([NotNull] string thumbprint) + { + if (string.IsNullOrEmpty(thumbprint)) + { + throw new ArgumentNullException(nameof(thumbprint)); + } + + return Configure(options => options.SigningCredentials.AddCertificate(thumbprint)); + } + + /// + /// Registers a retrieved from the given + /// X.509 store and used to sign the JWT tokens issued by OpenIddict. + /// + /// The thumbprint of the certificate used to identify it in the X.509 store. + /// The name of the X.509 store. + /// The location of the X.509 store. + /// The . + public OpenIddictServerBuilder AddSigningCertificate( + [NotNull] string thumbprint, StoreName name, StoreLocation location) + { + if (string.IsNullOrEmpty(thumbprint)) + { + throw new ArgumentNullException(nameof(thumbprint)); + } + + return Configure(options => options.SigningCredentials.AddCertificate(thumbprint, name, location)); + } + + /// + /// Registers a used to sign the JWT tokens issued by OpenIddict. + /// Note: using asymmetric keys is recommended on production. + /// + /// The security key. + /// The . + public OpenIddictServerBuilder AddSigningKey([NotNull] SecurityKey key) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + return Configure(options => options.SigningCredentials.AddKey(key)); + } + + /// + /// Enables authorization code flow support. For more information + /// about this specific OAuth2/OpenID Connect flow, visit + /// https://tools.ietf.org/html/rfc6749#section-4.1 and + /// http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth. + /// + /// The . + public OpenIddictServerBuilder AllowAuthorizationCodeFlow() + => Configure(options => options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.AuthorizationCode)); + + /// + /// Enables client credentials flow support. For more information about this + /// specific OAuth2 flow, visit https://tools.ietf.org/html/rfc6749#section-4.4. + /// + /// The . + public OpenIddictServerBuilder AllowClientCredentialsFlow() + => Configure(options => options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.ClientCredentials)); + + /// + /// Enables custom grant type support. + /// + /// The grant type associated with the flow. + /// The . + public OpenIddictServerBuilder AllowCustomFlow([NotNull] string type) + { + if (string.IsNullOrEmpty(type)) + { + throw new ArgumentException("The grant type cannot be null or empty.", nameof(type)); + } + + return Configure(options => options.GrantTypes.Add(type)); + } + + /// + /// Enables implicit flow support. For more information + /// about this specific OAuth2/OpenID Connect flow, visit + /// https://tools.ietf.org/html/rfc6749#section-4.2 and + /// http://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth. + /// + /// The . + public OpenIddictServerBuilder AllowImplicitFlow() + => Configure(options => options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.Implicit)); + + /// + /// Enables password flow support. For more information about this specific + /// OAuth2 flow, visit https://tools.ietf.org/html/rfc6749#section-4.3. + /// + /// The . + public OpenIddictServerBuilder AllowPasswordFlow() + => Configure(options => options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.Password)); + + /// + /// Enables refresh token flow support. For more information about this + /// specific OAuth2 flow, visit https://tools.ietf.org/html/rfc6749#section-6. + /// + /// The . + public OpenIddictServerBuilder AllowRefreshTokenFlow() + => Configure(options => options.GrantTypes.Add(OpenIdConnectConstants.GrantTypes.RefreshToken)); + + /// + /// Disables the configuration endpoint. + /// + /// The . + public OpenIddictServerBuilder DisableConfigurationEndpoint() + => Configure(options => options.ConfigurationEndpointPath = PathString.Empty); + + /// + /// Disables the cryptography endpoint. + /// + /// The . + public OpenIddictServerBuilder DisableCryptographyEndpoint() + => Configure(options => options.CryptographyEndpointPath = PathString.Empty); + + /// + /// Disables the HTTPS requirement during development. + /// + /// The . + public OpenIddictServerBuilder DisableHttpsRequirement() + => Configure(options => options.AllowInsecureHttp = true); + + /// + /// Disables sliding expiration. When using this option, refresh tokens + /// are issued with a fixed expiration date: when it expires, a complete + /// authorization flow must be started to retrieve a new refresh token. + /// + /// The . + public OpenIddictServerBuilder DisableSlidingExpiration() + => Configure(options => options.UseSlidingExpiration = false); + + /// + /// Disables token revocation, so that authorization code and + /// refresh tokens are never stored and cannot be revoked. + /// Using this option is generally not recommended. + /// + /// The . + public OpenIddictServerBuilder DisableTokenRevocation() + => Configure(options => options.DisableTokenRevocation = true); + + /// + /// Enables the authorization endpoint. + /// + /// The relative path of the authorization endpoint. + /// The . + public OpenIddictServerBuilder EnableAuthorizationEndpoint(PathString path) + { + if (!path.HasValue) + { + throw new ArgumentException("The path cannot be empty.", nameof(path)); + } + + return Configure(options => options.AuthorizationEndpointPath = path); + } + + /// + /// Enables the introspection endpoint. + /// + /// The relative path of the logout endpoint. + /// The . + public OpenIddictServerBuilder EnableIntrospectionEndpoint(PathString path) + { + if (!path.HasValue) + { + throw new ArgumentException("The path cannot be empty.", nameof(path)); + } + + return Configure(options => options.IntrospectionEndpointPath = path); + } + + /// + /// Enables the logout endpoint. + /// + /// The relative path of the logout endpoint. + /// The . + public OpenIddictServerBuilder EnableLogoutEndpoint(PathString path) + { + if (!path.HasValue) + { + throw new ArgumentException("The path cannot be empty.", nameof(path)); + } + + return Configure(options => options.LogoutEndpointPath = path); + } + + /// + /// Enables request caching, so that both authorization and logout requests + /// are automatically stored in the distributed cache, which allows flowing + /// large payloads across requests. Enabling this option is recommended + /// when using external authentication providers or when large GET or POST + /// OpenID Connect authorization requests support is required. + /// + /// The . + public OpenIddictServerBuilder EnableRequestCaching() + => Configure(options => options.EnableRequestCaching = true); + + /// + /// Enables the revocation endpoint. + /// + /// The relative path of the revocation endpoint. + /// The . + public OpenIddictServerBuilder EnableRevocationEndpoint( PathString path) + { + if (!path.HasValue) + { + throw new ArgumentException("The path cannot be empty.", nameof(path)); + } + + return Configure(options => options.RevocationEndpointPath = path); + } + + /// + /// Rejects authorization and token requests that specify scopes that have not been + /// registered using or + /// . + /// + /// The . + public OpenIddictServerBuilder EnableScopeValidation() + => Configure(options => options.EnableScopeValidation = true); + + /// + /// Enables the token endpoint. + /// + /// The relative path of the token endpoint. + /// The . + public OpenIddictServerBuilder EnableTokenEndpoint(PathString path) + { + if (!path.HasValue) + { + throw new ArgumentException("The path cannot be empty.", nameof(path)); + } + + return Configure(options => options.TokenEndpointPath = path); + } + + /// + /// Enables the userinfo endpoint. + /// + /// The relative path of the userinfo endpoint. + /// The . + public OpenIddictServerBuilder EnableUserinfoEndpoint(PathString path) + { + if (!path.HasValue) + { + throw new ArgumentException("The path cannot be empty.", nameof(path)); + } + + return Configure(options => options.UserinfoEndpointPath = path); + } + + /// + /// Makes client identification mandatory so that token and revocation + /// requests that don't specify a client_id are automatically rejected. + /// Note: enabling this option doesn't prevent public clients from using + /// the token and revocation endpoints, but specifying a client_id is required. + /// + /// The . + public OpenIddictServerBuilder RequireClientIdentification() + => Configure(options => options.RequireClientIdentification = true); + + /// + /// Sets the access token lifetime, after which client applications must retrieve + /// a new access token by making a grant_type=refresh_token token request + /// or a prompt=none authorization request, depending on the selected flow. + /// Using long-lived access tokens or tokens that never expire is not recommended. + /// + /// The access token lifetime. + /// The . + public OpenIddictServerBuilder SetAccessTokenLifetime(TimeSpan lifetime) + => Configure(options => options.AccessTokenLifetime = lifetime); + + /// + /// Sets the authorization code lifetime, after which client applications + /// are unable to send a grant_type=authorization_code token request. + /// Using short-lived authorization codes is strongly recommended. + /// + /// The authorization code lifetime. + /// The . + public OpenIddictServerBuilder SetAuthorizationCodeLifetime(TimeSpan lifetime) + => Configure(options => options.AuthorizationCodeLifetime = lifetime); + + /// + /// Sets the identity token lifetime, after which client + /// applications should refuse processing identity tokens. + /// + /// The identity token lifetime. + /// The . + public OpenIddictServerBuilder SetIdentityTokenLifetime(TimeSpan lifetime) + => Configure(options => options.IdentityTokenLifetime = lifetime); + + /// + /// Sets the refresh token lifetime, after which client applications must get + /// a new authorization from the user. When sliding expiration is enabled, + /// a new refresh token is always issued to the client application, + /// which prolongs the validity period of the refresh token. + /// + /// The refresh token lifetime. + /// The . + public OpenIddictServerBuilder SetRefreshTokenLifetime(TimeSpan lifetime) + => Configure(options => options.RefreshTokenLifetime = lifetime); + + /// + /// Sets the issuer address, which is used as the base address + /// for the endpoint URIs returned from the discovery endpoint. + /// + /// The issuer address. + /// The . + public OpenIddictServerBuilder SetIssuer([NotNull] Uri address) + { + if (address == null) + { + throw new ArgumentNullException(nameof(address)); + } + + return Configure(options => options.Issuer = address); + } + + /// + /// Registers the specified claims as supported claims so + /// they can be returned as part of the discovery document. + /// + /// The supported claims. + /// The . + public OpenIddictServerBuilder RegisterClaims([NotNull] params string[] claims) + { + if (claims == null) + { + throw new ArgumentNullException(nameof(claims)); + } + + if (claims.Any(claim => string.IsNullOrEmpty(claim))) + { + throw new ArgumentException("Claims cannot be null or empty.", nameof(claims)); + } + + return Configure(options => options.Claims.UnionWith(claims)); + } + + /// + /// Registers the specified scopes as supported scopes so + /// they can be returned as part of the discovery document. + /// + /// The supported scopes. + /// The . + public OpenIddictServerBuilder RegisterScopes([NotNull] params string[] scopes) + { + if (scopes == null) + { + throw new ArgumentNullException(nameof(scopes)); + } + + if (scopes.Any(scope => string.IsNullOrEmpty(scope))) + { + throw new ArgumentException("Scopes cannot be null or empty.", nameof(scopes)); + } + + return Configure(options => options.Scopes.UnionWith(scopes)); + } + + /// + /// Configures OpenIddict to use a specific data protection provider + /// instead of relying on the default instance provided by the DI container. + /// + /// The data protection provider used to create token protectors. + /// The . + public OpenIddictServerBuilder UseDataProtectionProvider([NotNull] IDataProtectionProvider provider) + { + if (provider == null) + { + throw new ArgumentNullException(nameof(provider)); + } + + return Configure(options => options.DataProtectionProvider = provider); + } + + /// + /// Sets JWT as the default token format for access tokens. + /// Note: this option cannot be used when using reference tokens. + /// + /// The . + public OpenIddictServerBuilder UseJsonWebTokens() + => Configure(options => options.AccessTokenHandler = new JwtSecurityTokenHandler + { + InboundClaimTypeMap = new Dictionary(), + OutboundClaimTypeMap = new Dictionary() + }); + + /// + /// Configures OpenIddict to use reference tokens, so that authorization codes, + /// access tokens and refresh tokens are stored as ciphertext in the database + /// (only an identifier is returned to the client application). Enabling this option + /// is useful to keep track of all the issued tokens, when storing a very large + /// number of claims in the authorization codes, access tokens and refresh tokens + /// or when immediate revocation of reference access tokens is desired. + /// Note: this option cannot be used when configuring JWT as the access token format. + /// + /// The . + public OpenIddictServerBuilder UseReferenceTokens() + => Configure(options => options.UseReferenceTokens = true); + + /// + /// Configures OpenIddict to use rolling refresh tokens. When this option is enabled, + /// a new refresh token is always issued for each refresh token request (and the previous + /// one is automatically revoked unless token revocation was explicitly disabled). + /// + /// The . + public OpenIddictServerBuilder UseRollingTokens() + => Configure(options => options.UseRollingTokens = true); + } +} \ No newline at end of file diff --git a/src/OpenIddict.Server/OpenIddictServerExtensions.cs b/src/OpenIddict.Server/OpenIddictServerExtensions.cs new file mode 100644 index 00000000..75781bc4 --- /dev/null +++ b/src/OpenIddict.Server/OpenIddictServerExtensions.cs @@ -0,0 +1,123 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using System.Text; +using AspNet.Security.OpenIdConnect.Server; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; +using OpenIddict.Server; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class OpenIddictServerExtensions + { + /// + /// Registers the OpenIddict token server services in the DI container. + /// + /// The services builder used by OpenIddict to register new services. + /// This extension can be safely called multiple times. + /// The . + public static OpenIddictServerBuilder AddServer([NotNull] this OpenIddictBuilder builder) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + builder.Services.AddAuthentication(); + + // Register the options initializers used by the OpenID Connect server handler and OpenIddict. + // Note: TryAddEnumerable() is used here to ensure the initializers are only registered once. + builder.Services.TryAddEnumerable(new[] + { + ServiceDescriptor.Singleton, + OpenIddictServerInitializer>(), + ServiceDescriptor.Singleton, + OpenIdConnectServerInitializer>() + }); + + // Register the OpenIddict handler/provider. + builder.Services.TryAddScoped(typeof(OpenIddictServerProvider<,,,>)); + builder.Services.TryAddScoped(); + builder.Services.TryAddScoped(provider => + { + var options = provider.GetRequiredService>() + .Get(OpenIdConnectServerDefaults.AuthenticationScheme); + + if (options == null) + { + throw new InvalidOperationException("The OpenIddict validation options cannot be resolved."); + } + + if (options.ApplicationType == null || options.AuthorizationType == null || + options.ScopeType == null || options.TokenType == null) + { + throw new InvalidOperationException(new StringBuilder() + .AppendLine("The entity types must be configured for the token server services to work correctly.") + .Append("To configure the entities, use either 'services.AddOpenIddict().AddCore().UseDefaultModels()' ") + .Append("or 'services.AddOpenIddict().AddCore().UseCustomModels()'.") + .ToString()); + } + + var type = typeof(OpenIddictServerProvider<,,,>).MakeGenericType( + /* TApplication: */ options.ApplicationType, + /* TAuthorization: */ options.AuthorizationType, + /* TScope: */ options.ScopeType, + /* TToken: */ options.TokenType); + + return (OpenIdConnectServerProvider) provider.GetRequiredService(type); + }); + + // Register the OpenID Connect server handler in the authentication options, + // so it can be discovered by the default authentication handler provider. + builder.Services.Configure(options => + { + // Note: this method is guaranteed to be idempotent. To prevent multiple schemes from being + // registered (which would result in an exception being thrown), a manual check is made here. + if (options.SchemeMap.ContainsKey(OpenIdConnectServerDefaults.AuthenticationScheme)) + { + return; + } + + options.AddScheme(OpenIdConnectServerDefaults.AuthenticationScheme, scheme => + { + scheme.HandlerType = typeof(OpenIddictServerHandler); + }); + }); + + return new OpenIddictServerBuilder(builder.Services); + } + + /// + /// Registers the OpenIddict token server services in the DI container. + /// + /// The services builder used by OpenIddict to register new services. + /// The configuration delegate used to configure the server services. + /// This extension can be safely called multiple times. + /// The . + public static OpenIddictBuilder AddServer( + [NotNull] this OpenIddictBuilder builder, + [NotNull] Action configuration) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (configuration == null) + { + throw new ArgumentNullException(nameof(configuration)); + } + + configuration(builder.AddServer()); + + return builder; + } + } +} diff --git a/src/OpenIddict.Server/OpenIddictOptions.cs b/src/OpenIddict.Server/OpenIddictServerOptions.cs similarity index 85% rename from src/OpenIddict.Server/OpenIddictOptions.cs rename to src/OpenIddict.Server/OpenIddictServerOptions.cs index 9492197b..9fdda579 100644 --- a/src/OpenIddict.Server/OpenIddictOptions.cs +++ b/src/OpenIddict.Server/OpenIddictServerOptions.cs @@ -11,17 +11,17 @@ using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Server; using Microsoft.Extensions.Caching.Distributed; -namespace OpenIddict +namespace OpenIddict.Server { /// /// Provides various settings needed to configure OpenIddict. /// - public class OpenIddictOptions : OpenIdConnectServerOptions + public class OpenIddictServerOptions : OpenIdConnectServerOptions { /// - /// Creates a new instance of the class. + /// Creates a new instance of the class. /// - public OpenIddictOptions() + public OpenIddictServerOptions() { // Note: OpenIdConnectServerProvider is automatically mapped // to the generic OpenIddictProvider class by the DI container. @@ -29,6 +29,16 @@ namespace OpenIddict ProviderType = typeof(OpenIdConnectServerProvider); } + /// + /// Gets or sets the type corresponding to the Application entity. + /// + public Type ApplicationType { get; set; } + + /// + /// Gets or sets the type corresponding to the Authorization entity. + /// + public Type AuthorizationType { get; set; } + /// /// Gets or sets the distributed cache used by OpenIddict. If no cache is explicitly /// provided, the cache registered in the dependency injection container is used. @@ -93,6 +103,16 @@ namespace OpenIddict OpenIdConnectConstants.Scopes.OpenId }; + /// + /// Gets or sets the type corresponding to the Scope entity. + /// + public Type ScopeType { get; set; } + + /// + /// Gets or sets the type corresponding to the Token entity. + /// + public Type TokenType { get; set; } + /// /// Gets or sets a boolean indicating whether reference tokens should be used. /// When set to true, authorization codes, access tokens and refresh tokens diff --git a/src/OpenIddict.Stores/OpenIddict.Stores.csproj b/src/OpenIddict.Stores/OpenIddict.Stores.csproj index 1a91dee5..524ba5ab 100644 --- a/src/OpenIddict.Stores/OpenIddict.Stores.csproj +++ b/src/OpenIddict.Stores/OpenIddict.Stores.csproj @@ -13,12 +13,13 @@ - + + diff --git a/src/OpenIddict.Stores/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.Stores/Stores/OpenIddictApplicationStore.cs index d422282a..bb66cbe6 100644 --- a/src/OpenIddict.Stores/Stores/OpenIddictApplicationStore.cs +++ b/src/OpenIddict.Stores/Stores/OpenIddictApplicationStore.cs @@ -14,7 +14,7 @@ using JetBrains.Annotations; using Microsoft.Extensions.Caching.Memory; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using OpenIddict.Core; +using OpenIddict.Abstractions; using OpenIddict.Models; namespace OpenIddict.Stores diff --git a/src/OpenIddict.Stores/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.Stores/Stores/OpenIddictAuthorizationStore.cs index 118ccc1e..aaaa0e21 100644 --- a/src/OpenIddict.Stores/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.Stores/Stores/OpenIddictAuthorizationStore.cs @@ -14,7 +14,7 @@ using JetBrains.Annotations; using Microsoft.Extensions.Caching.Memory; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using OpenIddict.Core; +using OpenIddict.Abstractions; using OpenIddict.Models; namespace OpenIddict.Stores diff --git a/src/OpenIddict.Stores/Stores/OpenIddictScopeStore.cs b/src/OpenIddict.Stores/Stores/OpenIddictScopeStore.cs index 2686bd75..c8a44ecb 100644 --- a/src/OpenIddict.Stores/Stores/OpenIddictScopeStore.cs +++ b/src/OpenIddict.Stores/Stores/OpenIddictScopeStore.cs @@ -14,7 +14,7 @@ using JetBrains.Annotations; using Microsoft.Extensions.Caching.Memory; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using OpenIddict.Core; +using OpenIddict.Abstractions; using OpenIddict.Models; namespace OpenIddict.Stores diff --git a/src/OpenIddict.Stores/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.Stores/Stores/OpenIddictTokenStore.cs index a5938aae..f396118c 100644 --- a/src/OpenIddict.Stores/Stores/OpenIddictTokenStore.cs +++ b/src/OpenIddict.Stores/Stores/OpenIddictTokenStore.cs @@ -14,7 +14,7 @@ using JetBrains.Annotations; using Microsoft.Extensions.Caching.Memory; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using OpenIddict.Core; +using OpenIddict.Abstractions; using OpenIddict.Models; namespace OpenIddict.Stores diff --git a/src/OpenIddict/OpenIddictExtensions.cs b/src/OpenIddict/OpenIddictExtensions.cs index 1338e89e..d7ea853a 100644 --- a/src/OpenIddict/OpenIddictExtensions.cs +++ b/src/OpenIddict/OpenIddictExtensions.cs @@ -13,69 +13,45 @@ namespace Microsoft.Extensions.DependencyInjection public static class OpenIddictExtensions { /// - /// Registers the default OpenIddict services in the DI container, - /// using the default entities and the default entity key type. + /// Configures OpenIddict to use the default entities, with the default entity key type (string). + /// The default entities are , , + /// and . /// - /// The services collection. - /// The . - public static OpenIddictBuilder AddOpenIddict([NotNull] this IServiceCollection services) + /// The services builder used by OpenIddict to register new services + /// The . + public static OpenIddictCoreBuilder UseDefaultModels([NotNull] this OpenIddictCoreBuilder builder) { - return services.AddOpenIddict(); - } - - /// - /// Registers the default OpenIddict services in the DI container, - /// using the default entities and the specified entity key type. - /// - /// The type of the entity primary keys. - /// The services collection. - /// The . - public static OpenIddictBuilder AddOpenIddict([NotNull] this IServiceCollection services) - where TKey : IEquatable - { - return services.AddOpenIddict, - OpenIddictAuthorization, - OpenIddictScope, - OpenIddictToken>(); - } + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } - /// - /// Registers the default OpenIddict services in the DI container, - /// using the default entities and the default entity key type. - /// - /// The services collection. - /// The configuration delegate used to register new services. - /// The . - public static IServiceCollection AddOpenIddict( - [NotNull] this IServiceCollection services, - [NotNull] Action configuration) - { - return services.AddOpenIddict(configuration); + return builder.UseCustomModels(); } /// - /// Registers the default OpenIddict services in the DI container, - /// using the default entities and the specified entity key type. + /// Configures OpenIddict to use the default entities, with the specified entity key type. + /// The default entities are , , + /// and . /// /// The type of the entity primary keys. - /// The services collection. - /// The configuration delegate used to register new services. - /// The . - public static IServiceCollection AddOpenIddict( - [NotNull] this IServiceCollection services, - [NotNull] Action configuration) + /// The services builder used by OpenIddict to register new services + /// The . + public static OpenIddictCoreBuilder UseDefaultModels([NotNull] this OpenIddictCoreBuilder builder) where TKey : IEquatable { - return services.AddOpenIddict, - OpenIddictAuthorization, - OpenIddictScope, - OpenIddictToken>(configuration); + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + return builder.UseCustomModels, + OpenIddictAuthorization, + OpenIddictScope, + OpenIddictToken>(); } } -} \ No newline at end of file +} diff --git a/test/OpenIddict.Core.Tests/OpenIddictBuilderTests.cs b/test/OpenIddict.Core.Tests/OpenIddictBuilderTests.cs deleted file mode 100644 index c547ad8e..00000000 --- a/test/OpenIddict.Core.Tests/OpenIddictBuilderTests.cs +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * See https://github.com/openiddict/openiddict-core for more information concerning - * the license and the contributors participating to this project. - */ - -using System; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Moq; -using Xunit; - -namespace OpenIddict.Core.Tests -{ - public class OpenIddictBuilderTests - { - [Fact] - public void AddApplicationManager_ThrowsAnExceptionForInvalidManager() - { - // Arrange - var services = new ServiceCollection(); - services.AddOptions(); - - var builder = new OpenIddictBuilder(services); - builder.ApplicationType = typeof(object); - - // Act and assert - var exception = Assert.Throws(() => builder.AddApplicationManager(typeof(object))); - - Assert.Equal("The specified type is invalid.", exception.Message); - } - - [Fact] - public void AddApplicationManager_OverridesDefaultManager() - { - // Arrange - var services = new ServiceCollection(); - services.AddOptions(); - - var builder = new OpenIddictBuilder(services); - builder.ApplicationType = typeof(object); - - var type = new Mock>( - Mock.Of>(), - Mock.Of>>()).Object.GetType(); - - // Act - builder.AddApplicationManager(type); - - var provider = services.BuildServiceProvider(); - var manager = provider.GetRequiredService>(); - - // Assert - Assert.IsType(type, manager); - } - - [Fact] - public void AddApplicationStore_ThrowsAnExceptionForInvalidStore() - { - // Arrange - var services = new ServiceCollection(); - services.AddOptions(); - - var builder = new OpenIddictBuilder(services); - builder.ApplicationType = typeof(object); - - // Act and assert - var exception = Assert.Throws(() => builder.AddApplicationStore(typeof(object))); - - Assert.Equal("The specified type is invalid.", exception.Message); - } - - [Fact] - public void AddApplicationStore_OverridesDefaultManager() - { - // Arrange - var services = new ServiceCollection(); - services.AddOptions(); - - var builder = new OpenIddictBuilder(services); - builder.ApplicationType = typeof(object); - - var type = Mock.Of>().GetType(); - - // Act - builder.AddApplicationStore(type); - - var provider = services.BuildServiceProvider(); - var store = provider.GetRequiredService>(); - - // Assert - Assert.IsType(type, store); - } - - [Fact] - public void AddAuthorizationManager_ThrowsAnExceptionForInvalidManager() - { - // Arrange - var services = new ServiceCollection(); - services.AddOptions(); - - var builder = new OpenIddictBuilder(services); - builder.AuthorizationType = typeof(object); - - // Act and assert - var exception = Assert.Throws(() => builder.AddAuthorizationManager(typeof(object))); - - Assert.Equal("The specified type is invalid.", exception.Message); - } - - [Fact] - public void AddAuthorizationManager_OverridesDefaultManager() - { - // Arrange - var services = new ServiceCollection(); - services.AddOptions(); - - var builder = new OpenIddictBuilder(services); - builder.AuthorizationType = typeof(object); - - var type = new Mock>( - Mock.Of>(), - Mock.Of>>()).Object.GetType(); - - // Act - builder.AddAuthorizationManager(type); - - var provider = services.BuildServiceProvider(); - var manager = provider.GetRequiredService>(); - - // Assert - Assert.IsType(type, manager); - } - - [Fact] - public void AddAuthorizationStore_ThrowsAnExceptionForInvalidStore() - { - // Arrange - var services = new ServiceCollection(); - services.AddOptions(); - - var builder = new OpenIddictBuilder(services); - builder.AuthorizationType = typeof(object); - - // Act and assert - var exception = Assert.Throws(() => builder.AddAuthorizationStore(typeof(object))); - - Assert.Equal("The specified type is invalid.", exception.Message); - } - - [Fact] - public void AddAuthorizationStore_OverridesDefaultManager() - { - // Arrange - var services = new ServiceCollection(); - services.AddOptions(); - - var builder = new OpenIddictBuilder(services); - builder.AuthorizationType = typeof(object); - - var type = Mock.Of>().GetType(); - - // Act - builder.AddAuthorizationStore(type); - - var provider = services.BuildServiceProvider(); - var store = provider.GetRequiredService>(); - - // Assert - Assert.IsType(type, store); - } - - [Fact] - public void AddScopeManager_ThrowsAnExceptionForInvalidManager() - { - // Arrange - var services = new ServiceCollection(); - services.AddOptions(); - - var builder = new OpenIddictBuilder(services); - builder.ScopeType = typeof(object); - - // Act and assert - var exception = Assert.Throws(() => builder.AddScopeManager(typeof(object))); - - Assert.Equal("The specified type is invalid.", exception.Message); - } - - [Fact] - public void AddScopeManager_OverridesDefaultManager() - { - // Arrange - var services = new ServiceCollection(); - services.AddOptions(); - - var builder = new OpenIddictBuilder(services); - builder.ScopeType = typeof(object); - - var type = new Mock>( - Mock.Of>(), - Mock.Of>>()).Object.GetType(); - - // Act - builder.AddScopeManager(type); - - var provider = services.BuildServiceProvider(); - var manager = provider.GetRequiredService>(); - - // Assert - Assert.IsType(type, manager); - } - - [Fact] - public void AddScopeStore_ThrowsAnExceptionForInvalidStore() - { - // Arrange - var services = new ServiceCollection(); - services.AddOptions(); - - var builder = new OpenIddictBuilder(services); - builder.ScopeType = typeof(object); - - // Act and assert - var exception = Assert.Throws(() => builder.AddScopeStore(typeof(object))); - - Assert.Equal("The specified type is invalid.", exception.Message); - } - - [Fact] - public void AddScopeStore_OverridesDefaultManager() - { - // Arrange - var services = new ServiceCollection(); - services.AddOptions(); - - var builder = new OpenIddictBuilder(services); - builder.ScopeType = typeof(object); - - var type = Mock.Of>().GetType(); - - // Act - builder.AddScopeStore(type); - - var provider = services.BuildServiceProvider(); - var store = provider.GetRequiredService>(); - - // Assert - Assert.IsType(type, store); - } - - [Fact] - public void AddTokenManager_ThrowsAnExceptionForInvalidManager() - { - // Arrange - var services = new ServiceCollection(); - services.AddOptions(); - - var builder = new OpenIddictBuilder(services); - builder.TokenType = typeof(object); - - // Act and assert - var exception = Assert.Throws(() => builder.AddTokenManager(typeof(object))); - - Assert.Equal("The specified type is invalid.", exception.Message); - } - - [Fact] - public void AddTokenManager_OverridesDefaultManager() - { - // Arrange - var services = new ServiceCollection(); - services.AddOptions(); - - var builder = new OpenIddictBuilder(services); - builder.TokenType = typeof(object); - - var type = new Mock>( - Mock.Of>(), - Mock.Of>>()).Object.GetType(); - - // Act - builder.AddTokenManager(type); - - var provider = services.BuildServiceProvider(); - var manager = provider.GetRequiredService>(); - - // Assert - Assert.IsType(type, manager); - } - - [Fact] - public void AddTokenStore_ThrowsAnExceptionForInvalidStore() - { - // Arrange - var services = new ServiceCollection(); - services.AddOptions(); - - var builder = new OpenIddictBuilder(services); - builder.TokenType = typeof(object); - - // Act and assert - var exception = Assert.Throws(() => builder.AddTokenStore(typeof(object))); - - Assert.Equal("The specified type is invalid.", exception.Message); - } - - [Fact] - public void AddTokenStore_OverridesDefaultManager() - { - // Arrange - var services = new ServiceCollection(); - services.AddOptions(); - - var builder = new OpenIddictBuilder(services); - builder.TokenType = typeof(object); - - var type = Mock.Of>().GetType(); - - // Act - builder.AddTokenStore(type); - - var provider = services.BuildServiceProvider(); - var store = provider.GetRequiredService>(); - - // Assert - Assert.IsType(type, store); - } - } -} diff --git a/test/OpenIddict.Core.Tests/OpenIddictCoreBuilderTests.cs b/test/OpenIddict.Core.Tests/OpenIddictCoreBuilderTests.cs new file mode 100644 index 00000000..d72f4560 --- /dev/null +++ b/test/OpenIddict.Core.Tests/OpenIddictCoreBuilderTests.cs @@ -0,0 +1,487 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/openiddict-core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Moq; +using OpenIddict.Abstractions; +using Xunit; + +namespace OpenIddict.Core.Tests +{ + public class OpenIddictCoreBuilderTests + { + [Fact] + public void ReplaceApplicationManager_ThrowsAnExceptionForInvalidManager() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act and assert + var exception = Assert.Throws(() => builder.ReplaceApplicationManager(typeof(object))); + + Assert.Equal("type", exception.ParamName); + Assert.StartsWith("The specified type is invalid.", exception.Message); + } + + [Fact] + public void ReplaceApplicationManager_OverridesDefaultOpenGenericManager() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.ReplaceApplicationManager(typeof(OpenGenericApplicationManager<>)); + + // Assert + Assert.Contains(services, service => + service.ServiceType == typeof(OpenIddictApplicationManager<>) && + service.ImplementationType == typeof(OpenGenericApplicationManager<>)); + Assert.DoesNotContain(services, service => + service.ServiceType == typeof(OpenIddictApplicationManager<>) && + service.ImplementationType == typeof(OpenIddictApplicationManager<>)); + } + + [Fact] + public void ReplaceApplicationManager_AddsClosedGenericManager() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.ReplaceApplicationManager(typeof(ClosedGenericApplicationManager)); + + // Assert + Assert.Contains(services, service => + service.ServiceType == typeof(OpenIddictApplicationManager) && + service.ImplementationType == typeof(ClosedGenericApplicationManager)); + Assert.Contains(services, service => + service.ServiceType == typeof(OpenIddictApplicationManager<>) && + service.ImplementationType == typeof(OpenIddictApplicationManager<>)); + } + + [Fact] + public void ReplaceApplicationStoreResolver_ThrowsAnExceptionForInvalidStoreResolver() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act and assert + var exception = Assert.Throws(() => builder.ReplaceApplicationStoreResolver(typeof(object))); + + Assert.Equal("type", exception.ParamName); + Assert.StartsWith("The specified type is invalid.", exception.Message); + } + + [Fact] + public void ReplaceApplicationStoreResolver_OverridesDefaultManager() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + var type = Mock.Of().GetType(); + + // Act + builder.ReplaceApplicationStoreResolver(type); + + var provider = services.BuildServiceProvider(); + var store = provider.GetRequiredService(); + + // Assert + Assert.IsType(type, store); + } + + [Fact] + public void ReplaceAuthorizationManager_ThrowsAnExceptionForInvalidManager() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act and assert + var exception = Assert.Throws(() => builder.ReplaceAuthorizationManager(typeof(object))); + + Assert.Equal("type", exception.ParamName); + Assert.StartsWith("The specified type is invalid.", exception.Message); + } + + [Fact] + public void ReplaceAuthorizationManager_OverridesDefaultOpenGenericManager() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.ReplaceAuthorizationManager(typeof(OpenGenericAuthorizationManager<>)); + + // Assert + Assert.Contains(services, service => + service.ServiceType == typeof(OpenIddictAuthorizationManager<>) && + service.ImplementationType == typeof(OpenGenericAuthorizationManager<>)); + Assert.DoesNotContain(services, service => + service.ServiceType == typeof(OpenIddictAuthorizationManager<>) && + service.ImplementationType == typeof(OpenIddictAuthorizationManager<>)); + } + + [Fact] + public void ReplaceAuthorizationManager_AddsClosedGenericManager() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.ReplaceAuthorizationManager(typeof(ClosedGenericAuthorizationManager)); + + // Assert + Assert.Contains(services, service => + service.ServiceType == typeof(OpenIddictAuthorizationManager) && + service.ImplementationType == typeof(ClosedGenericAuthorizationManager)); + Assert.Contains(services, service => + service.ServiceType == typeof(OpenIddictAuthorizationManager<>) && + service.ImplementationType == typeof(OpenIddictAuthorizationManager<>)); + } + [Fact] + public void ReplaceAuthorizationStoreResolver_ThrowsAnExceptionForInvalidStoreResolver() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act and assert + var exception = Assert.Throws(() => builder.ReplaceAuthorizationStoreResolver(typeof(object))); + + Assert.Equal("type", exception.ParamName); + Assert.StartsWith("The specified type is invalid.", exception.Message); + } + + [Fact] + public void ReplaceAuthorizationStoreResolver_OverridesDefaultManager() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + var type = Mock.Of().GetType(); + + // Act + builder.ReplaceAuthorizationStoreResolver(type); + + var provider = services.BuildServiceProvider(); + var store = provider.GetRequiredService(); + + // Assert + Assert.IsType(type, store); + } + + [Fact] + public void ReplaceScopeManager_ThrowsAnExceptionForInvalidManager() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act and assert + var exception = Assert.Throws(() => builder.ReplaceScopeManager(typeof(object))); + + Assert.Equal("type", exception.ParamName); + Assert.StartsWith("The specified type is invalid.", exception.Message); + } + + [Fact] + public void ReplaceScopeManager_OverridesDefaultOpenGenericManager() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.ReplaceScopeManager(typeof(OpenGenericScopeManager<>)); + + // Assert + Assert.Contains(services, service => + service.ServiceType == typeof(OpenIddictScopeManager<>) && + service.ImplementationType == typeof(OpenGenericScopeManager<>)); + Assert.DoesNotContain(services, service => + service.ServiceType == typeof(OpenIddictScopeManager<>) && + service.ImplementationType == typeof(OpenIddictScopeManager<>)); + } + + [Fact] + public void ReplaceScopeManager_AddsClosedGenericManager() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.ReplaceScopeManager(typeof(ClosedGenericScopeManager)); + + // Assert + Assert.Contains(services, service => + service.ServiceType == typeof(OpenIddictScopeManager) && + service.ImplementationType == typeof(ClosedGenericScopeManager)); + Assert.Contains(services, service => + service.ServiceType == typeof(OpenIddictScopeManager<>) && + service.ImplementationType == typeof(OpenIddictScopeManager<>)); + } + + [Fact] + public void ReplaceScopeStoreResolver_ThrowsAnExceptionForInvalidStoreResolver() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act and assert + var exception = Assert.Throws(() => builder.ReplaceScopeStoreResolver(typeof(object))); + + Assert.Equal("type", exception.ParamName); + Assert.StartsWith("The specified type is invalid.", exception.Message); + } + + [Fact] + public void ReplaceScopeStoreResolver_OverridesDefaultManager() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + var type = Mock.Of().GetType(); + + // Act + builder.ReplaceScopeStoreResolver(type); + + var provider = services.BuildServiceProvider(); + var store = provider.GetRequiredService(); + + // Assert + Assert.IsType(type, store); + } + + [Fact] + public void ReplaceTokenManager_ThrowsAnExceptionForInvalidManager() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act and assert + var exception = Assert.Throws(() => builder.ReplaceTokenManager(typeof(object))); + + Assert.Equal("type", exception.ParamName); + Assert.StartsWith("The specified type is invalid.", exception.Message); + } + + [Fact] + public void ReplaceTokenManager_OverridesDefaultOpenGenericManager() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.ReplaceTokenManager(typeof(OpenGenericTokenManager<>)); + + // Assert + Assert.Contains(services, service => + service.ServiceType == typeof(OpenIddictTokenManager<>) && + service.ImplementationType == typeof(OpenGenericTokenManager<>)); + Assert.DoesNotContain(services, service => + service.ServiceType == typeof(OpenIddictTokenManager<>) && + service.ImplementationType == typeof(OpenIddictTokenManager<>)); + } + + [Fact] + public void ReplaceTokenManager_AddsClosedGenericManager() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.ReplaceTokenManager(typeof(ClosedGenericTokenManager)); + + // Assert + Assert.Contains(services, service => + service.ServiceType == typeof(OpenIddictTokenManager) && + service.ImplementationType == typeof(ClosedGenericTokenManager)); + Assert.Contains(services, service => + service.ServiceType == typeof(OpenIddictTokenManager<>) && + service.ImplementationType == typeof(OpenIddictTokenManager<>)); + } + + [Fact] + public void ReplaceTokenStoreResolver_ThrowsAnExceptionForInvalidStoreResolver() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act and assert + var exception = Assert.Throws(() => builder.ReplaceTokenStoreResolver(typeof(object))); + + Assert.Equal("type", exception.ParamName); + Assert.StartsWith("The specified type is invalid.", exception.Message); + } + + [Fact] + public void ReplaceTokenStoreResolver_OverridesDefaultManager() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + var type = Mock.Of().GetType(); + + // Act + builder.ReplaceTokenStoreResolver(type); + + var provider = services.BuildServiceProvider(); + var store = provider.GetRequiredService(); + + // Assert + Assert.IsType(type, store); + } + + [Fact] + public void UseCustomModels_CustomEntitiesAreCorrectlySet() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + services.AddOpenIddict().AddCore() + .UseCustomModels(); + + // Assert + var provider = services.BuildServiceProvider(); + var options = provider.GetRequiredService>().CurrentValue; + + Assert.Equal(typeof(CustomApplication), options.DefaultApplicationType); + Assert.Equal(typeof(CustomAuthorization), options.DefaultAuthorizationType); + Assert.Equal(typeof(CustomScope), options.DefaultScopeType); + Assert.Equal(typeof(CustomToken), options.DefaultTokenType); + } + + private static OpenIddictCoreBuilder CreateBuilder(IServiceCollection services) + => services.AddOpenIddict().AddCore(); + + private static IServiceCollection CreateServices() + { + var services = new ServiceCollection(); + services.AddOptions(); + + return services; + } + + public class CustomApplication { } + public class CustomAuthorization { } + public class CustomScope { } + public class CustomToken { } + + private class ClosedGenericApplicationManager : OpenIddictApplicationManager + { + public ClosedGenericApplicationManager( + IOpenIddictApplicationStoreResolver resolver, + ILogger> logger, + IOptionsMonitor options) + : base(resolver, logger, options) + { + } + } + + private class OpenGenericApplicationManager : OpenIddictApplicationManager + where TApplication : class + { + public OpenGenericApplicationManager( + IOpenIddictApplicationStoreResolver resolver, + ILogger> logger, + IOptionsMonitor options) + : base(resolver, logger, options) + { + } + } + + private class ClosedGenericAuthorizationManager : OpenIddictAuthorizationManager + { + public ClosedGenericAuthorizationManager( + IOpenIddictAuthorizationStoreResolver resolver, + ILogger> logger, + IOptionsMonitor options) + : base(resolver, logger, options) + { + } + } + + private class OpenGenericAuthorizationManager : OpenIddictAuthorizationManager + where TAuthorization : class + { + public OpenGenericAuthorizationManager( + IOpenIddictAuthorizationStoreResolver resolver, + ILogger> logger, + IOptionsMonitor options) + : base(resolver, logger, options) + { + } + } + + private class ClosedGenericScopeManager : OpenIddictScopeManager + { + public ClosedGenericScopeManager( + IOpenIddictScopeStoreResolver resolver, + ILogger> logger, + IOptionsMonitor options) + : base(resolver, logger, options) + { + } + } + + private class OpenGenericScopeManager : OpenIddictScopeManager + where TScope : class + { + public OpenGenericScopeManager( + IOpenIddictScopeStoreResolver resolver, + ILogger> logger, + IOptionsMonitor options) + : base(resolver, logger, options) + { + } + } + + private class ClosedGenericTokenManager : OpenIddictTokenManager + { + public ClosedGenericTokenManager( + IOpenIddictTokenStoreResolver resolver, + ILogger> logger, + IOptionsMonitor options) + : base(resolver, logger, options) + { + } + } + + private class OpenGenericTokenManager : OpenIddictTokenManager + where TToken : class + { + public OpenGenericTokenManager( + IOpenIddictTokenStoreResolver resolver, + ILogger> logger, + IOptionsMonitor options) + : base(resolver, logger, options) + { + } + } + } +} diff --git a/test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs deleted file mode 100644 index b3467ae8..00000000 --- a/test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * See https://github.com/openiddict/openiddict-core for more information concerning - * the license and the contributors participating to this project. - */ - -using System; -using Microsoft.Extensions.DependencyInjection; -using Xunit; - -namespace OpenIddict.Core.Tests -{ - public class OpenIddictExtensionsTests - { - [Fact] - public void AddOpenIddict_CustomEntitiesAreCorrectlySet() - { - // Arrange - var services = new ServiceCollection(); - - // Act - var builder = services.AddOpenIddict(); - - // Assert - Assert.Equal(typeof(object), builder.ApplicationType); - Assert.Equal(typeof(object), builder.AuthorizationType); - Assert.Equal(typeof(object), builder.ScopeType); - Assert.Equal(typeof(object), builder.TokenType); - } - - [Theory] - [InlineData(typeof(OpenIddictApplicationManager))] - [InlineData(typeof(OpenIddictAuthorizationManager))] - [InlineData(typeof(OpenIddictScopeManager))] - [InlineData(typeof(OpenIddictTokenManager))] - public void AddOpenIddict_ManagersForCustomEntitiesAreCorrectlyRegistered(Type type) - { - // Arrange - var services = new ServiceCollection(); - - // Act - services.AddOpenIddict(); - - // Assert - Assert.Contains(services, service => service.ServiceType == type); - } - } -} diff --git a/test/OpenIddict.EntityFramework.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.EntityFramework.Tests/OpenIddictExtensionsTests.cs index 874bd0b3..a0521f3e 100644 --- a/test/OpenIddict.EntityFramework.Tests/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.EntityFramework.Tests/OpenIddictExtensionsTests.cs @@ -8,117 +8,22 @@ using System; using System.Data.Entity; using Microsoft.Extensions.DependencyInjection; using OpenIddict.EntityFramework; -using OpenIddict.Models; using Xunit; namespace OpenIddict.EntityFrameworkCore.Tests { public class OpenIddictExtensionsTests { - [Fact] - public void AddEntityFrameworkStores_ThrowsAnExceptionForInvalidApplicationEntity() - { - // Arrange - var builder = new OpenIddictBuilder(new ServiceCollection()) - { - ApplicationType = typeof(object), - AuthorizationType = typeof(OpenIddictAuthorization), - ScopeType = typeof(OpenIddictScope), - TokenType = typeof(OpenIddictToken) - }; - - // Act and assert - var exception = Assert.Throws(delegate - { - builder.AddEntityFrameworkStores(); - }); - - Assert.Equal("The Entity Framework stores can only be used " + - "with the built-in OpenIddictApplication entity.", exception.Message); - } - - [Fact] - public void AddEntityFrameworkStores_ThrowsAnExceptionForInvalidAuthorizationEntity() - { - // Arrange - var builder = new OpenIddictBuilder(new ServiceCollection()) - { - ApplicationType = typeof(OpenIddictApplication), - AuthorizationType = typeof(object), - ScopeType = typeof(OpenIddictScope), - TokenType = typeof(OpenIddictToken) - }; - - // Act and assert - var exception = Assert.Throws(delegate - { - builder.AddEntityFrameworkStores(); - }); - - Assert.Equal("The Entity Framework stores can only be used " + - "with the built-in OpenIddictAuthorization entity.", exception.Message); - } - - [Fact] - public void AddEntityFrameworkStores_ThrowsAnExceptionForInvalidScopeEntity() - { - // Arrange - var builder = new OpenIddictBuilder(new ServiceCollection()) - { - ApplicationType = typeof(OpenIddictApplication), - AuthorizationType = typeof(OpenIddictAuthorization), - ScopeType = typeof(object), - TokenType = typeof(OpenIddictToken) - }; - - // Act and assert - var exception = Assert.Throws(delegate - { - builder.AddEntityFrameworkStores(); - }); - - Assert.Equal("The Entity Framework stores can only be used " + - "with the built-in OpenIddictScope entity.", exception.Message); - } - - [Fact] - public void AddEntityFrameworkStores_ThrowsAnExceptionForInvalidTokenEntity() - { - // Arrange - var builder = new OpenIddictBuilder(new ServiceCollection()) - { - ApplicationType = typeof(OpenIddictApplication), - AuthorizationType = typeof(OpenIddictAuthorization), - ScopeType = typeof(OpenIddictScope), - TokenType = typeof(object) - }; - - // Act and assert - var exception = Assert.Throws(delegate - { - builder.AddEntityFrameworkStores(); - }); - - Assert.Equal("The Entity Framework stores can only be used " + - "with the built-in OpenIddictToken entity.", exception.Message); - } - [Theory] - [InlineData(typeof(OpenIddictApplicationStore))] - [InlineData(typeof(OpenIddictAuthorizationStore))] - [InlineData(typeof(OpenIddictScopeStore))] - [InlineData(typeof(OpenIddictTokenStore))] - public void AddEntityFrameworkStores_RegistersEntityFrameworkStores(Type type) + [InlineData(typeof(OpenIddictApplicationStoreResolver))] + [InlineData(typeof(OpenIddictAuthorizationStoreResolver))] + [InlineData(typeof(OpenIddictScopeStoreResolver))] + [InlineData(typeof(OpenIddictTokenStoreResolver))] + public void AddEntityFrameworkStores_RegistersEntityFrameworkStoreFactories(Type type) { // Arrange var services = new ServiceCollection(); - var builder = new OpenIddictBuilder(services) - { - ApplicationType = typeof(OpenIddictApplication), - AuthorizationType = typeof(OpenIddictAuthorization), - ScopeType = typeof(OpenIddictScope), - TokenType = typeof(OpenIddictToken) - }; + var builder = services.AddOpenIddict().AddCore(); // Act builder.AddEntityFrameworkStores(); @@ -126,60 +31,5 @@ namespace OpenIddict.EntityFrameworkCore.Tests // Assert Assert.Contains(services, service => service.ImplementationType == type); } - - [Theory] - [InlineData(typeof(OpenIddictApplicationStore, OpenIddictAuthorization, OpenIddictToken, DbContext, Guid>))] - [InlineData(typeof(OpenIddictAuthorizationStore, OpenIddictApplication, OpenIddictToken, DbContext, Guid>))] - [InlineData(typeof(OpenIddictScopeStore, DbContext, Guid>))] - [InlineData(typeof(OpenIddictTokenStore, OpenIddictApplication, OpenIddictAuthorization, DbContext, Guid>))] - public void AddEntityFrameworkStores_KeyTypeIsInferredFromEntities(Type type) - { - // Arrange - var services = new ServiceCollection(); - - var builder = new OpenIddictBuilder(services) - { - ApplicationType = typeof(OpenIddictApplication), - AuthorizationType = typeof(OpenIddictAuthorization), - ScopeType = typeof(OpenIddictScope), - TokenType = typeof(OpenIddictToken) - }; - - // Act - builder.AddEntityFrameworkStores(); - - // Assert - Assert.Contains(services, service => service.ImplementationType == type); - } - - [Theory] - [InlineData(typeof(OpenIddictApplicationStore))] - [InlineData(typeof(OpenIddictAuthorizationStore))] - [InlineData(typeof(OpenIddictScopeStore))] - [InlineData(typeof(OpenIddictTokenStore))] - public void AddEntityFrameworkStores_DefaultEntitiesCanBeReplaced(Type type) - { - // Arrange - var services = new ServiceCollection(); - - var builder = new OpenIddictBuilder(services) - { - ApplicationType = typeof(CustomApplication), - AuthorizationType = typeof(CustomAuthorization), - ScopeType = typeof(CustomScope), - TokenType = typeof(CustomToken) - }; - - // Act - builder.AddEntityFrameworkStores(); - - // Assert - Assert.Contains(services, service => service.ImplementationType == type); - } - - public class CustomApplication : OpenIddictApplication { } - public class CustomAuthorization : OpenIddictAuthorization { } - public class CustomScope : OpenIddictScope { } - public class CustomToken : OpenIddictToken { } } } diff --git a/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs index 42752609..76da44ba 100644 --- a/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs @@ -7,117 +7,22 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -using OpenIddict.Models; using Xunit; namespace OpenIddict.EntityFrameworkCore.Tests { public class OpenIddictExtensionsTests { - [Fact] - public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidApplicationEntity() - { - // Arrange - var builder = new OpenIddictBuilder(new ServiceCollection()) - { - ApplicationType = typeof(object), - AuthorizationType = typeof(OpenIddictAuthorization), - ScopeType = typeof(OpenIddictScope), - TokenType = typeof(OpenIddictToken) - }; - - // Act and assert - var exception = Assert.Throws(delegate - { - builder.AddEntityFrameworkCoreStores(); - }); - - Assert.Equal("The Entity Framework Core stores can only be used " + - "with the built-in OpenIddictApplication entity.", exception.Message); - } - - [Fact] - public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidAuthorizationEntity() - { - // Arrange - var builder = new OpenIddictBuilder(new ServiceCollection()) - { - ApplicationType = typeof(OpenIddictApplication), - AuthorizationType = typeof(object), - ScopeType = typeof(OpenIddictScope), - TokenType = typeof(OpenIddictToken) - }; - - // Act and assert - var exception = Assert.Throws(delegate - { - builder.AddEntityFrameworkCoreStores(); - }); - - Assert.Equal("The Entity Framework Core stores can only be used " + - "with the built-in OpenIddictAuthorization entity.", exception.Message); - } - - [Fact] - public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidScopeEntity() - { - // Arrange - var builder = new OpenIddictBuilder(new ServiceCollection()) - { - ApplicationType = typeof(OpenIddictApplication), - AuthorizationType = typeof(OpenIddictAuthorization), - ScopeType = typeof(object), - TokenType = typeof(OpenIddictToken) - }; - - // Act and assert - var exception = Assert.Throws(delegate - { - builder.AddEntityFrameworkCoreStores(); - }); - - Assert.Equal("The Entity Framework Core stores can only be used " + - "with the built-in OpenIddictScope entity.", exception.Message); - } - - [Fact] - public void AddEntityFrameworkCoreStores_ThrowsAnExceptionForInvalidTokenEntity() - { - // Arrange - var builder = new OpenIddictBuilder(new ServiceCollection()) - { - ApplicationType = typeof(OpenIddictApplication), - AuthorizationType = typeof(OpenIddictAuthorization), - ScopeType = typeof(OpenIddictScope), - TokenType = typeof(object) - }; - - // Act and assert - var exception = Assert.Throws(delegate - { - builder.AddEntityFrameworkCoreStores(); - }); - - Assert.Equal("The Entity Framework Core stores can only be used " + - "with the built-in OpenIddictToken entity.", exception.Message); - } - [Theory] - [InlineData(typeof(OpenIddictApplicationStore))] - [InlineData(typeof(OpenIddictAuthorizationStore))] - [InlineData(typeof(OpenIddictScopeStore))] - [InlineData(typeof(OpenIddictTokenStore))] - public void AddEntityFrameworkCoreStores_RegistersEntityFrameworkStores(Type type) + [InlineData(typeof(OpenIddictApplicationStoreResolver))] + [InlineData(typeof(OpenIddictAuthorizationStoreResolver))] + [InlineData(typeof(OpenIddictScopeStoreResolver))] + [InlineData(typeof(OpenIddictTokenStoreResolver))] + public void AddEntityFrameworkCoreStores_RegistersEntityFrameworkCoreStoreFactories(Type type) { // Arrange var services = new ServiceCollection(); - var builder = new OpenIddictBuilder(services) - { - ApplicationType = typeof(OpenIddictApplication), - AuthorizationType = typeof(OpenIddictAuthorization), - ScopeType = typeof(OpenIddictScope), - TokenType = typeof(OpenIddictToken) - }; + var builder = services.AddOpenIddict().AddCore(); // Act builder.AddEntityFrameworkCoreStores(); @@ -125,60 +30,5 @@ namespace OpenIddict.EntityFrameworkCore.Tests // Assert Assert.Contains(services, service => service.ImplementationType == type); } - - [Theory] - [InlineData(typeof(OpenIddictApplicationStore, OpenIddictAuthorization, OpenIddictToken, DbContext, Guid>))] - [InlineData(typeof(OpenIddictAuthorizationStore, OpenIddictApplication, OpenIddictToken, DbContext, Guid>))] - [InlineData(typeof(OpenIddictScopeStore, DbContext, Guid>))] - [InlineData(typeof(OpenIddictTokenStore, OpenIddictApplication, OpenIddictAuthorization, DbContext, Guid>))] - public void AddEntityFrameworkCoreStores_KeyTypeIsInferredFromEntities(Type type) - { - // Arrange - var services = new ServiceCollection(); - - var builder = new OpenIddictBuilder(services) - { - ApplicationType = typeof(OpenIddictApplication), - AuthorizationType = typeof(OpenIddictAuthorization), - ScopeType = typeof(OpenIddictScope), - TokenType = typeof(OpenIddictToken) - }; - - // Act - builder.AddEntityFrameworkCoreStores(); - - // Assert - Assert.Contains(services, service => service.ImplementationType == type); - } - - [Theory] - [InlineData(typeof(OpenIddictApplicationStore))] - [InlineData(typeof(OpenIddictAuthorizationStore))] - [InlineData(typeof(OpenIddictScopeStore))] - [InlineData(typeof(OpenIddictTokenStore))] - public void AddEntityFrameworkCoreStores_DefaultEntitiesCanBeReplaced(Type type) - { - // Arrange - var services = new ServiceCollection(); - - var builder = new OpenIddictBuilder(services) - { - ApplicationType = typeof(CustomApplication), - AuthorizationType = typeof(CustomAuthorization), - ScopeType = typeof(CustomScope), - TokenType = typeof(CustomToken) - }; - - // Act - builder.AddEntityFrameworkCoreStores(); - - // Assert - Assert.Contains(services, service => service.ImplementationType == type); - } - - public class CustomApplication : OpenIddictApplication { } - public class CustomAuthorization : OpenIddictAuthorization { } - public class CustomScope : OpenIddictScope { } - public class CustomToken : OpenIddictToken { } } } diff --git a/test/OpenIddict.Mvc.Tests/OpenIddict.Mvc.Tests.csproj b/test/OpenIddict.Mvc.Tests/OpenIddict.Mvc.Tests.csproj index 20d98d8c..d4012151 100644 --- a/test/OpenIddict.Mvc.Tests/OpenIddict.Mvc.Tests.csproj +++ b/test/OpenIddict.Mvc.Tests/OpenIddict.Mvc.Tests.csproj @@ -8,6 +8,7 @@ + diff --git a/test/OpenIddict.Mvc.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.Mvc.Tests/OpenIddictExtensionsTests.cs index e6674752..5aa9c286 100644 --- a/test/OpenIddict.Mvc.Tests/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.Mvc.Tests/OpenIddictExtensionsTests.cs @@ -20,7 +20,7 @@ namespace OpenIddict.Mvc.Tests var services = new ServiceCollection(); services.AddOptions(); - var builder = new OpenIddictBuilder(services); + var builder = services.AddOpenIddict().AddServer(); // Act builder.AddMvcBinders(); diff --git a/test/OpenIddict.Server.Tests/Internal/OpenIddictInitializerTests.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs similarity index 97% rename from test/OpenIddict.Server.Tests/Internal/OpenIddictInitializerTests.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs index e5c65b60..4fd3a9fc 100644 --- a/test/OpenIddict.Server.Tests/Internal/OpenIddictInitializerTests.cs +++ b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerInitializerTests.cs @@ -18,9 +18,9 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Xunit; -namespace OpenIddict.Tests +namespace OpenIddict.Server.Tests { - public class OpenIddictInitializerTests + public class OpenIddictServerInitializerTests { [Fact] public async Task PostConfigure_ThrowsAnExceptionWhenRandomNumberGeneratorIsNull() @@ -259,7 +259,7 @@ namespace OpenIddict.Tests "'services.AddOpenIddict().AddEphemeralSigningKey()' to use an ephemeral key.", exception.Message); } - private static TestServer CreateAuthorizationServer(Action configuration = null) + private static TestServer CreateAuthorizationServer(Action configuration = null) { var builder = new WebHostBuilder(); @@ -273,7 +273,9 @@ namespace OpenIddict.Tests services.AddOptions(); services.AddDistributedMemoryCache(); - services.AddOpenIddict(options => configuration?.Invoke(options)); + services.AddOpenIddict() + .AddCore(options => options.UseDefaultModels()) + .AddServer(options => configuration?.Invoke(options)); }); builder.Configure(app => diff --git a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Authentication.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Authentication.cs similarity index 99% rename from test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Authentication.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Authentication.cs index 623019c2..6d9446b0 100644 --- a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Authentication.cs +++ b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Authentication.cs @@ -4,7 +4,6 @@ * the license and the contributors participating to this project. */ -using System.Collections.Immutable; using System.IO; using System.Security.Cryptography; using System.Threading; @@ -16,13 +15,14 @@ using Microsoft.Extensions.DependencyInjection; using Moq; using Newtonsoft.Json; using Newtonsoft.Json.Bson; +using OpenIddict.Abstractions; using OpenIddict.Core; using OpenIddict.Models; using Xunit; -namespace OpenIddict.Tests +namespace OpenIddict.Server.Tests { - public partial class OpenIddictProviderTests + public partial class OpenIddictServerProviderTests { [Fact] public async Task ExtractAuthorizationRequest_UnsupportedRequestParameterIsRejected() diff --git a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Discovery.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Discovery.cs similarity index 98% rename from test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Discovery.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Discovery.cs index 73b85c80..417e9273 100644 --- a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Discovery.cs +++ b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Discovery.cs @@ -10,14 +10,13 @@ using AspNet.Security.OpenIdConnect.Primitives; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Facebook; using Microsoft.AspNetCore.Authentication.Google; -using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json.Linq; -using OpenIddict.Core; +using OpenIddict.Abstractions; using Xunit; -namespace OpenIddict.Tests +namespace OpenIddict.Server.Tests { - public partial class OpenIddictProviderTests + public partial class OpenIddictServerProviderTests { [Fact] public async Task HandleConfigurationRequest_PlainCodeChallengeMethodIsNotReturned() diff --git a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Exchange.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Exchange.cs similarity index 99% rename from test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Exchange.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Exchange.cs index 750af5de..afe4b383 100644 --- a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Exchange.cs +++ b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Exchange.cs @@ -16,13 +16,14 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Moq; +using OpenIddict.Abstractions; using OpenIddict.Core; using OpenIddict.Models; using Xunit; -namespace OpenIddict.Tests +namespace OpenIddict.Server.Tests { - public partial class OpenIddictProviderTests + public partial class OpenIddictServerProviderTests { [Theory] [InlineData(OpenIdConnectConstants.GrantTypes.AuthorizationCode)] diff --git a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Introspection.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Introspection.cs similarity index 99% rename from test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Introspection.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Introspection.cs index 7be4104b..331bec2b 100644 --- a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Introspection.cs +++ b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Introspection.cs @@ -15,13 +15,14 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Moq; +using OpenIddict.Abstractions; using OpenIddict.Core; using OpenIddict.Models; using Xunit; -namespace OpenIddict.Tests +namespace OpenIddict.Server.Tests { - public partial class OpenIddictProviderTests + public partial class OpenIddictServerProviderTests { [Fact] public async Task ExtractIntrospectionRequest_GetRequestsAreRejected() diff --git a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Revocation.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Revocation.cs similarity index 99% rename from test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Revocation.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Revocation.cs index 5678746b..c17cd42c 100644 --- a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Revocation.cs +++ b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Revocation.cs @@ -17,13 +17,14 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using Moq; +using OpenIddict.Abstractions; using OpenIddict.Core; using OpenIddict.Models; using Xunit; -namespace OpenIddict.Tests +namespace OpenIddict.Server.Tests { - public partial class OpenIddictProviderTests + public partial class OpenIddictServerProviderTests { [Theory] [InlineData(OpenIdConnectConstants.TokenTypeHints.AccessToken)] diff --git a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Serialization.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Serialization.cs similarity index 99% rename from test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Serialization.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Serialization.cs index 80664613..cf36c521 100644 --- a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Serialization.cs +++ b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Serialization.cs @@ -16,13 +16,14 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Moq; +using OpenIddict.Abstractions; using OpenIddict.Core; using OpenIddict.Models; using Xunit; -namespace OpenIddict.Tests +namespace OpenIddict.Server.Tests { - public partial class OpenIddictProviderTests + public partial class OpenIddictServerProviderTests { [Fact] public async Task DeserializeAccessToken_ReturnsNullForMalformedReferenceToken() diff --git a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Session.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Session.cs similarity index 98% rename from test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Session.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Session.cs index b121ed41..03d01ccb 100644 --- a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Session.cs +++ b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Session.cs @@ -12,12 +12,13 @@ using AspNet.Security.OpenIdConnect.Primitives; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.DependencyInjection; using Moq; +using OpenIddict.Abstractions; using OpenIddict.Core; using Xunit; -namespace OpenIddict.Tests +namespace OpenIddict.Server.Tests { - public partial class OpenIddictProviderTests + public partial class OpenIddictServerProviderTests { [Fact] public async Task ExtractLogoutRequest_RequestIdParameterIsRejectedWhenRequestCachingIsDisabled() diff --git a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Userinfo.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Userinfo.cs similarity index 92% rename from test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Userinfo.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Userinfo.cs index 883f383d..b591bd0e 100644 --- a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Userinfo.cs +++ b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.Userinfo.cs @@ -9,9 +9,9 @@ using AspNet.Security.OpenIdConnect.Client; using AspNet.Security.OpenIdConnect.Primitives; using Xunit; -namespace OpenIddict.Tests +namespace OpenIddict.Server.Tests { - public partial class OpenIddictProviderTests + public partial class OpenIddictServerProviderTests { [Fact] public async Task ExtractUserinfoRequest_RequestIsHandledByUserCode() diff --git a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.cs similarity index 95% rename from test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.cs index c8995cc8..cd9c21b5 100644 --- a/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.cs +++ b/test/OpenIddict.Server.Tests/Internal/OpenIddictServerProviderTests.cs @@ -25,17 +25,19 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; using Moq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using OpenIddict.Abstractions; using OpenIddict.Core; using OpenIddict.Models; using Xunit; -namespace OpenIddict.Tests +namespace OpenIddict.Server.Tests { - public partial class OpenIddictProviderTests + public partial class OpenIddictServerProviderTests { public const string AuthorizationEndpoint = "/connect/authorize"; public const string ConfigurationEndpoint = "/.well-known/openid-configuration"; @@ -1331,7 +1333,7 @@ namespace OpenIddict.Tests Assert.Equal("value", (string) response["custom_string_parameter"]); } - private static TestServer CreateAuthorizationServer(Action configuration = null) + private static TestServer CreateAuthorizationServer(Action configuration = null) { var builder = new WebHostBuilder(); @@ -1361,47 +1363,52 @@ namespace OpenIddict.Tests options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; }); - // Replace the default OpenIddict managers. - services.AddSingleton(CreateApplicationManager()); - services.AddSingleton(CreateAuthorizationManager()); - services.AddSingleton(CreateScopeManager()); - services.AddSingleton(CreateTokenManager()); + services.AddOpenIddict() + .AddCore(options => + { + options.UseDefaultModels(); - services.AddOpenIddict(options => - { - // Disable the transport security requirement during testing. - options.DisableHttpsRequirement(); - - // Enable the tested endpoints. - options.EnableAuthorizationEndpoint(AuthorizationEndpoint) - .EnableIntrospectionEndpoint(IntrospectionEndpoint) - .EnableLogoutEndpoint(LogoutEndpoint) - .EnableRevocationEndpoint(RevocationEndpoint) - .EnableTokenEndpoint(TokenEndpoint) - .EnableUserinfoEndpoint(UserinfoEndpoint); - - // Enable the tested flows. - options.AllowAuthorizationCodeFlow() - .AllowClientCredentialsFlow() - .AllowImplicitFlow() - .AllowPasswordFlow() - .AllowRefreshTokenFlow(); - - // Register the X.509 certificate used to sign the identity tokens. - options.AddSigningCertificate( - assembly: typeof(OpenIddictProviderTests).GetTypeInfo().Assembly, - resource: "OpenIddict.Server.Tests.Certificate.pfx", - password: "OpenIddict"); - - // Note: overriding the default data protection provider is not necessary for the tests to pass, - // but is useful to ensure unnecessary keys are not persisted in testing environments, which also - // helps make the unit tests run faster, as no registry or disk access is required in this case. - options.UseDataProtectionProvider(new EphemeralDataProtectionProvider(new LoggerFactory())); - - // Run the configuration delegate - // registered by the unit tests. - configuration?.Invoke(options); - }); + options.Services.AddSingleton(CreateApplicationManager()); + options.Services.AddSingleton(CreateAuthorizationManager()); + options.Services.AddSingleton(CreateScopeManager()); + options.Services.AddSingleton(CreateTokenManager()); + }) + + .AddServer(options => + { + // Disable the transport security requirement during testing. + options.DisableHttpsRequirement(); + + // Enable the tested endpoints. + options.EnableAuthorizationEndpoint(AuthorizationEndpoint) + .EnableIntrospectionEndpoint(IntrospectionEndpoint) + .EnableLogoutEndpoint(LogoutEndpoint) + .EnableRevocationEndpoint(RevocationEndpoint) + .EnableTokenEndpoint(TokenEndpoint) + .EnableUserinfoEndpoint(UserinfoEndpoint); + + // Enable the tested flows. + options.AllowAuthorizationCodeFlow() + .AllowClientCredentialsFlow() + .AllowImplicitFlow() + .AllowPasswordFlow() + .AllowRefreshTokenFlow(); + + // Register the X.509 certificate used to sign the identity tokens. + options.AddSigningCertificate( + assembly: typeof(OpenIddictServerProviderTests).GetTypeInfo().Assembly, + resource: "OpenIddict.Server.Tests.Certificate.pfx", + password: "OpenIddict"); + + // Note: overriding the default data protection provider is not necessary for the tests to pass, + // but is useful to ensure unnecessary keys are not persisted in testing environments, which also + // helps make the unit tests run faster, as no registry or disk access is required in this case. + options.UseDataProtectionProvider(new EphemeralDataProtectionProvider()); + + // Run the configuration delegate + // registered by the unit tests. + configuration?.Invoke(options); + }); }); builder.Configure(app => @@ -1511,8 +1518,9 @@ namespace OpenIddict.Tests Action>> configuration = null) { var manager = new Mock>( - Mock.Of>(), - Mock.Of>>()); + Mock.Of(), + Mock.Of>>(), + Mock.Of>()); configuration?.Invoke(manager); @@ -1523,8 +1531,9 @@ namespace OpenIddict.Tests Action>> configuration = null) { var manager = new Mock>( - Mock.Of>(), - Mock.Of>>()); + Mock.Of(), + Mock.Of>>(), + Mock.Of>()); configuration?.Invoke(manager); @@ -1535,8 +1544,9 @@ namespace OpenIddict.Tests Action>> configuration = null) { var manager = new Mock>( - Mock.Of>(), - Mock.Of>>()); + Mock.Of(), + Mock.Of>>(), + Mock.Of>()); configuration?.Invoke(manager); @@ -1547,8 +1557,9 @@ namespace OpenIddict.Tests Action>> configuration = null) { var manager = new Mock>( - Mock.Of>(), - Mock.Of>>()); + Mock.Of(), + Mock.Of>>(), + Mock.Of>()); configuration?.Invoke(manager); diff --git a/test/OpenIddict.Server.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs similarity index 93% rename from test/OpenIddict.Server.Tests/OpenIddictExtensionsTests.cs rename to test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs index ca6704a2..e0e83146 100644 --- a/test/OpenIddict.Server.Tests/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.Server.Tests/OpenIddictServerBuilderTests.cs @@ -9,23 +9,20 @@ using System.IdentityModel.Tokens.Jwt; using System.Reflection; using AspNet.Security.OpenIdConnect.Primitives; using AspNet.Security.OpenIdConnect.Server; -using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Internal; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Moq; -using OpenIddict.Models; using Xunit; -namespace OpenIddict.Tests +namespace OpenIddict.Server.Tests { - public class OpenIddictExtensionsTests + public class OpenIddictServerBuilderTests { [Fact] public void Configure_OptionsAreCorrectlyAmended() @@ -43,21 +40,6 @@ namespace OpenIddict.Tests Assert.Equal(TimeSpan.FromDays(1), options.AccessTokenLifetime); } - [Fact] - public void AddDevelopmentSigningCertificate_ThrowsAnExceptionForNullBuilder() - { - // Arrange - var builder = (OpenIddictBuilder) null; - - // Act and assert - var exception = Assert.Throws(delegate - { - builder.AddDevelopmentSigningCertificate(); - }); - - Assert.Equal("builder", exception.ParamName); - } - [Fact] public void AddDevelopmentSigningCertificate_ThrowsAnExceptionForNullSubject() { @@ -211,7 +193,7 @@ namespace OpenIddict.Tests // Act builder.AddSigningCertificate( - assembly: typeof(OpenIddictExtensionsTests).GetTypeInfo().Assembly, + assembly: typeof(OpenIddictServerBuilderTests).GetTypeInfo().Assembly, resource: "OpenIddict.Server.Tests.Certificate.pfx", password: "OpenIddict"); @@ -647,7 +629,7 @@ namespace OpenIddict.Tests var builder = CreateBuilder(services); // Act - builder.UseDataProtectionProvider(new EphemeralDataProtectionProvider(new LoggerFactory())); + builder.UseDataProtectionProvider(new EphemeralDataProtectionProvider()); var options = GetOptions(services); @@ -687,14 +669,10 @@ namespace OpenIddict.Tests Assert.True(options.UseReferenceTokens); } - private static OpenIddictBuilder CreateBuilder(IServiceCollection services) - => new OpenIddictBuilder(services) - { - ApplicationType = typeof(OpenIddictApplication), - AuthorizationType = typeof(OpenIddictAuthorization), - ScopeType = typeof(OpenIddictScope), - TokenType = typeof(OpenIddictToken) - }; + private static OpenIddictServerBuilder CreateBuilder(IServiceCollection services) + => services.AddOpenIddict() + .AddCore(options => options.UseDefaultModels()) + .AddServer(); private static IServiceCollection CreateServices() { @@ -707,14 +685,14 @@ namespace OpenIddict.Tests return services; } - private static OpenIddictOptions GetOptions(IServiceCollection services) + private static OpenIddictServerOptions GetOptions(IServiceCollection services) { services.RemoveAll>(); - services.RemoveAll>(); + services.RemoveAll>(); var provider = services.BuildServiceProvider(); - var options = provider.GetRequiredService>(); + var options = provider.GetRequiredService>(); return options.Get(OpenIdConnectServerDefaults.AuthenticationScheme); } } diff --git a/test/OpenIddict.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.Tests/OpenIddictExtensionsTests.cs index f6566cca..25de42ab 100644 --- a/test/OpenIddict.Tests/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.Tests/OpenIddictExtensionsTests.cs @@ -6,6 +6,7 @@ using System; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using OpenIddict.Core; using OpenIddict.Models; using Xunit; @@ -14,38 +15,39 @@ namespace OpenIddict.Tests { public class OpenIddictExtensionsTests { - [Theory] - [InlineData(typeof(OpenIddictApplicationManager))] - [InlineData(typeof(OpenIddictAuthorizationManager))] - [InlineData(typeof(OpenIddictScopeManager))] - [InlineData(typeof(OpenIddictTokenManager))] - public void AddOpenIddict_KeyTypeDefaultsToString(Type type) + public void UseDefaultModels_KeyTypeDefaultsToString() { // Arrange var services = new ServiceCollection(); + var builder = services.AddOpenIddict().AddCore(); // Act - services.AddOpenIddict(); + builder.UseDefaultModels(); // Assert - Assert.Contains(services, service => service.ImplementationType == type); + var provider = services.BuildServiceProvider(); + var options = provider.GetRequiredService>().CurrentValue; + + Assert.Equal(typeof(OpenIddictApplication), options.DefaultApplicationType); + Assert.Equal(typeof(OpenIddictAuthorization), options.DefaultAuthorizationType); + Assert.Equal(typeof(OpenIddictScope), options.DefaultScopeType); + Assert.Equal(typeof(OpenIddictToken), options.DefaultTokenType); } - [Theory] - [InlineData(typeof(OpenIddictApplicationManager>))] - [InlineData(typeof(OpenIddictAuthorizationManager>))] - [InlineData(typeof(OpenIddictScopeManager>))] - [InlineData(typeof(OpenIddictTokenManager>))] - public void AddOpenIddict_KeyTypeCanBeOverriden(Type type) + public void UseDefaultModels_KeyTypeCanBeOverriden() { // Arrange var services = new ServiceCollection(); + var builder = services.AddOpenIddict().AddCore(); // Act - services.AddOpenIddict(); + var provider = services.BuildServiceProvider(); + var options = provider.GetRequiredService>().CurrentValue; - // Assert - Assert.Contains(services, service => service.ImplementationType == type); + Assert.Equal(typeof(OpenIddictApplication), options.DefaultApplicationType); + Assert.Equal(typeof(OpenIddictAuthorization), options.DefaultAuthorizationType); + Assert.Equal(typeof(OpenIddictScope), options.DefaultScopeType); + Assert.Equal(typeof(OpenIddictToken), options.DefaultTokenType); } } }