From ef139e23d1012f642350cb4ab3c0981bbe623562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Sat, 24 Mar 2018 19:08:43 +0100 Subject: [PATCH] Move the default stores to OpenIddict.Stores and the server components to OpenIddict.Server --- OpenIddict.sln | 21 + src/OpenIddict.Core/OpenIddict.Core.csproj | 5 - src/OpenIddict.Core/OpenIddictBuilder.cs | 9 +- src/OpenIddict.Core/OpenIddictExtensions.cs | 67 -- .../OpenIddict.EntityFramework.csproj | 2 +- .../Stores/OpenIddictApplicationStore.cs | 2 +- .../Stores/OpenIddictAuthorizationStore.cs | 1 + .../Stores/OpenIddictScopeStore.cs | 2 +- .../Stores/OpenIddictTokenStore.cs | 1 + .../OpenIddict.EntityFrameworkCore.csproj | 2 +- .../Stores/OpenIddictApplicationStore.cs | 4 +- .../Stores/OpenIddictAuthorizationStore.cs | 3 +- .../Stores/OpenIddictScopeStore.cs | 3 +- .../Stores/OpenIddictTokenStore.cs | 3 +- src/OpenIddict.Mvc/OpenIddict.Mvc.csproj | 3 +- .../Internal}/OpenIddictHandler.cs | 0 .../Internal}/OpenIddictInitializer.cs | 0 .../OpenIddictProvider.Authentication.cs | 1 - .../Internal}/OpenIddictProvider.Discovery.cs | 0 .../Internal}/OpenIddictProvider.Exchange.cs | 0 .../Internal}/OpenIddictProvider.Helpers.cs | 0 .../OpenIddictProvider.Introspection.cs | 0 .../OpenIddictProvider.Revocation.cs | 0 .../OpenIddictProvider.Serialization.cs | 0 .../Internal}/OpenIddictProvider.Session.cs | 0 .../Internal}/OpenIddictProvider.Userinfo.cs | 0 .../Internal}/OpenIddictProvider.cs | 0 .../OpenIddict.Server.csproj | 26 + src/OpenIddict.Server/OpenIddictExtensions.cs | 1008 ++++++++++++++++ .../OpenIddictOptions.cs | 0 .../OpenIddict.Stores.csproj | 24 + .../Stores/OpenIddictApplicationStore.cs | 3 +- .../Stores/OpenIddictAuthorizationStore.cs | 4 +- .../Stores/OpenIddictScopeStore.cs | 3 +- .../Stores/OpenIddictTokenStore.cs | 4 +- src/OpenIddict/OpenIddict.csproj | 7 +- src/OpenIddict/OpenIddictExtensions.cs | 1017 +---------------- .../OpenIddictExtensionsTests.cs | 35 +- .../OpenIddictExtensionsTests.cs | 26 +- .../OpenIddictExtensionsTests.cs | 26 +- .../Certificate.pfx | Bin .../Internal}/OpenIddictInitializerTests.cs | 0 .../OpenIddictProviderTests.Authentication.cs | 0 .../OpenIddictProviderTests.Discovery.cs | 0 .../OpenIddictProviderTests.Exchange.cs | 0 .../OpenIddictProviderTests.Introspection.cs | 0 .../OpenIddictProviderTests.Revocation.cs | 0 .../OpenIddictProviderTests.Serialization.cs | 0 .../OpenIddictProviderTests.Session.cs | 0 .../OpenIddictProviderTests.Userinfo.cs | 0 .../Internal}/OpenIddictProviderTests.cs | 2 +- .../OpenIddict.Server.Tests.csproj | 46 + .../OpenIddictExtensionsTests.cs | 721 ++++++++++++ test/OpenIddict.Tests/OpenIddict.Tests.csproj | 21 +- .../OpenIddictExtensionsTests.cs | 696 +---------- 55 files changed, 1988 insertions(+), 1810 deletions(-) rename src/{OpenIddict => OpenIddict.Server/Internal}/OpenIddictHandler.cs (100%) rename src/{OpenIddict => OpenIddict.Server/Internal}/OpenIddictInitializer.cs (100%) rename src/{OpenIddict => OpenIddict.Server/Internal}/OpenIddictProvider.Authentication.cs (99%) rename src/{OpenIddict => OpenIddict.Server/Internal}/OpenIddictProvider.Discovery.cs (100%) rename src/{OpenIddict => OpenIddict.Server/Internal}/OpenIddictProvider.Exchange.cs (100%) rename src/{OpenIddict => OpenIddict.Server/Internal}/OpenIddictProvider.Helpers.cs (100%) rename src/{OpenIddict => OpenIddict.Server/Internal}/OpenIddictProvider.Introspection.cs (100%) rename src/{OpenIddict => OpenIddict.Server/Internal}/OpenIddictProvider.Revocation.cs (100%) rename src/{OpenIddict => OpenIddict.Server/Internal}/OpenIddictProvider.Serialization.cs (100%) rename src/{OpenIddict => OpenIddict.Server/Internal}/OpenIddictProvider.Session.cs (100%) rename src/{OpenIddict => OpenIddict.Server/Internal}/OpenIddictProvider.Userinfo.cs (100%) rename src/{OpenIddict => OpenIddict.Server/Internal}/OpenIddictProvider.cs (100%) create mode 100644 src/OpenIddict.Server/OpenIddict.Server.csproj create mode 100644 src/OpenIddict.Server/OpenIddictExtensions.cs rename src/{OpenIddict => OpenIddict.Server}/OpenIddictOptions.cs (100%) create mode 100644 src/OpenIddict.Stores/OpenIddict.Stores.csproj rename src/{OpenIddict.Core => OpenIddict.Stores}/Stores/OpenIddictApplicationStore.cs (99%) rename src/{OpenIddict.Core => OpenIddict.Stores}/Stores/OpenIddictAuthorizationStore.cs (99%) rename src/{OpenIddict.Core => OpenIddict.Stores}/Stores/OpenIddictScopeStore.cs (99%) rename src/{OpenIddict.Core => OpenIddict.Stores}/Stores/OpenIddictTokenStore.cs (99%) rename test/{OpenIddict.Tests => OpenIddict.Server.Tests}/Certificate.pfx (100%) rename test/{OpenIddict.Tests => OpenIddict.Server.Tests/Internal}/OpenIddictInitializerTests.cs (100%) rename test/{OpenIddict.Tests => OpenIddict.Server.Tests/Internal}/OpenIddictProviderTests.Authentication.cs (100%) rename test/{OpenIddict.Tests => OpenIddict.Server.Tests/Internal}/OpenIddictProviderTests.Discovery.cs (100%) rename test/{OpenIddict.Tests => OpenIddict.Server.Tests/Internal}/OpenIddictProviderTests.Exchange.cs (100%) rename test/{OpenIddict.Tests => OpenIddict.Server.Tests/Internal}/OpenIddictProviderTests.Introspection.cs (100%) rename test/{OpenIddict.Tests => OpenIddict.Server.Tests/Internal}/OpenIddictProviderTests.Revocation.cs (100%) rename test/{OpenIddict.Tests => OpenIddict.Server.Tests/Internal}/OpenIddictProviderTests.Serialization.cs (100%) rename test/{OpenIddict.Tests => OpenIddict.Server.Tests/Internal}/OpenIddictProviderTests.Session.cs (100%) rename test/{OpenIddict.Tests => OpenIddict.Server.Tests/Internal}/OpenIddictProviderTests.Userinfo.cs (100%) rename test/{OpenIddict.Tests => OpenIddict.Server.Tests/Internal}/OpenIddictProviderTests.cs (99%) create mode 100644 test/OpenIddict.Server.Tests/OpenIddict.Server.Tests.csproj create mode 100644 test/OpenIddict.Server.Tests/OpenIddictExtensionsTests.cs diff --git a/OpenIddict.sln b/OpenIddict.sln index fa904b3d..a3571712 100644 --- a/OpenIddict.sln +++ b/OpenIddict.sln @@ -46,6 +46,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.EntityFramework" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.EntityFramework.Tests", "test\OpenIddict.EntityFramework.Tests\OpenIddict.EntityFramework.Tests.csproj", "{96325E37-9897-43AC-8408-7B17F58E8788}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Stores", "src\OpenIddict.Stores\OpenIddict.Stores.csproj", "{275D888A-B4C8-4E93-AC4B-B1AA25D98159}" +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}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -104,6 +110,18 @@ Global {96325E37-9897-43AC-8408-7B17F58E8788}.Debug|Any CPU.Build.0 = Debug|Any CPU {96325E37-9897-43AC-8408-7B17F58E8788}.Release|Any CPU.ActiveCfg = Release|Any CPU {96325E37-9897-43AC-8408-7B17F58E8788}.Release|Any CPU.Build.0 = Release|Any CPU + {275D888A-B4C8-4E93-AC4B-B1AA25D98159}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {275D888A-B4C8-4E93-AC4B-B1AA25D98159}.Debug|Any CPU.Build.0 = Debug|Any CPU + {275D888A-B4C8-4E93-AC4B-B1AA25D98159}.Release|Any CPU.ActiveCfg = Release|Any CPU + {275D888A-B4C8-4E93-AC4B-B1AA25D98159}.Release|Any CPU.Build.0 = Release|Any CPU + {21A7F241-CBE7-4F5C-9787-F2C50D135AEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {21A7F241-CBE7-4F5C-9787-F2C50D135AEA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {21A7F241-CBE7-4F5C-9787-F2C50D135AEA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {21A7F241-CBE7-4F5C-9787-F2C50D135AEA}.Release|Any CPU.Build.0 = Release|Any CPU + {07B02B98-8A68-432D-A932-48E6D52B221A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -122,6 +140,9 @@ Global {0102A6CC-41A6-4B34-B49E-65AFE95882BB} = {D544447C-D701-46BB-9A5B-C76C612A596B} {BF42CC6C-0B56-4F66-9866-18B8393F3C06} = {D544447C-D701-46BB-9A5B-C76C612A596B} {96325E37-9897-43AC-8408-7B17F58E8788} = {5FC71D6A-A994-4F62-977F-88A7D25379D7} + {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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A710059F-0466-4D48-9B3A-0EF4F840B616} diff --git a/src/OpenIddict.Core/OpenIddict.Core.csproj b/src/OpenIddict.Core/OpenIddict.Core.csproj index 32092ff8..1573cb23 100644 --- a/src/OpenIddict.Core/OpenIddict.Core.csproj +++ b/src/OpenIddict.Core/OpenIddict.Core.csproj @@ -12,17 +12,12 @@ aspnetcore;authentication;jwt;openidconnect;openiddict;security - - - - - diff --git a/src/OpenIddict.Core/OpenIddictBuilder.cs b/src/OpenIddict.Core/OpenIddictBuilder.cs index 8b0f8dfe..888ce3d3 100644 --- a/src/OpenIddict.Core/OpenIddictBuilder.cs +++ b/src/OpenIddict.Core/OpenIddictBuilder.cs @@ -8,7 +8,6 @@ using System; using System.ComponentModel; using JetBrains.Annotations; using OpenIddict.Core; -using OpenIddict.Models; namespace Microsoft.Extensions.DependencyInjection { @@ -35,25 +34,25 @@ namespace Microsoft.Extensions.DependencyInjection /// Gets or sets the type corresponding to the Application entity. /// [EditorBrowsable(EditorBrowsableState.Never)] - public Type ApplicationType { get; set; } = typeof(OpenIddictApplication); + public Type ApplicationType { get; set; } /// /// Gets or sets the type corresponding to the Authorization entity. /// [EditorBrowsable(EditorBrowsableState.Never)] - public Type AuthorizationType { get; set; } = typeof(OpenIddictAuthorization); + public Type AuthorizationType { get; set; } /// /// Gets or sets the type corresponding to the Scope entity. /// [EditorBrowsable(EditorBrowsableState.Never)] - public Type ScopeType { get; set; } = typeof(OpenIddictScope); + public Type ScopeType { get; set; } /// /// Gets or sets the type corresponding to the Token entity. /// [EditorBrowsable(EditorBrowsableState.Never)] - public Type TokenType { get; set; } = typeof(OpenIddictToken); + public Type TokenType { get; set; } /// /// Gets the services collection. diff --git a/src/OpenIddict.Core/OpenIddictExtensions.cs b/src/OpenIddict.Core/OpenIddictExtensions.cs index a1714b6f..9be79446 100644 --- a/src/OpenIddict.Core/OpenIddictExtensions.cs +++ b/src/OpenIddict.Core/OpenIddictExtensions.cs @@ -8,42 +8,11 @@ using System; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection.Extensions; using OpenIddict.Core; -using OpenIddict.Models; 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. - /// - /// The services collection. - /// The . - public static OpenIddictBuilder AddOpenIddict([NotNull] this IServiceCollection services) - { - 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>(); - } - /// /// Registers the default OpenIddict services in the DI container, using the specified entities. /// @@ -83,42 +52,6 @@ namespace Microsoft.Extensions.DependencyInjection }; } - /// - /// 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); - } - - /// - /// 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 configuration delegate used to register new services. - /// The . - public static IServiceCollection AddOpenIddict( - [NotNull] this IServiceCollection services, - [NotNull] Action configuration) - where TKey : IEquatable - { - return services.AddOpenIddict, - OpenIddictAuthorization, - OpenIddictScope, - OpenIddictToken>(configuration); - } - /// /// Registers the default OpenIddict services in the DI container, using the specified entities. /// diff --git a/src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj b/src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj index 2d72dcc4..ecfddf7b 100644 --- a/src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj +++ b/src/OpenIddict.EntityFramework/OpenIddict.EntityFramework.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs index e5d1717b..4b2b2e06 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs @@ -14,8 +14,8 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.Extensions.Caching.Memory; -using OpenIddict.Core; using OpenIddict.Models; +using OpenIddict.Stores; namespace OpenIddict.EntityFramework { diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs index e646c9ea..18ca3bb7 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictAuthorizationStore.cs @@ -16,6 +16,7 @@ using JetBrains.Annotations; using Microsoft.Extensions.Caching.Memory; using OpenIddict.Core; using OpenIddict.Models; +using OpenIddict.Stores; namespace OpenIddict.EntityFramework { diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs index 20aaab33..52a32538 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictScopeStore.cs @@ -56,7 +56,7 @@ namespace OpenIddict.EntityFramework /// The type of the Scope entity. /// The type of the Entity Framework database context. /// The type of the entity primary keys. - public class OpenIddictScopeStore : Core.OpenIddictScopeStore + public class OpenIddictScopeStore : Stores.OpenIddictScopeStore where TScope : OpenIddictScope, new() where TContext : DbContext where TKey : IEquatable diff --git a/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs index eb457884..26239b69 100644 --- a/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs +++ b/src/OpenIddict.EntityFramework/Stores/OpenIddictTokenStore.cs @@ -16,6 +16,7 @@ using JetBrains.Annotations; using Microsoft.Extensions.Caching.Memory; using OpenIddict.Core; using OpenIddict.Models; +using OpenIddict.Stores; namespace OpenIddict.EntityFramework { diff --git a/src/OpenIddict.EntityFrameworkCore/OpenIddict.EntityFrameworkCore.csproj b/src/OpenIddict.EntityFrameworkCore/OpenIddict.EntityFrameworkCore.csproj index 3a7c4e6e..ba729a49 100644 --- a/src/OpenIddict.EntityFrameworkCore/OpenIddict.EntityFrameworkCore.csproj +++ b/src/OpenIddict.EntityFrameworkCore/OpenIddict.EntityFrameworkCore.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs index ee794471..c378cfb8 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictApplicationStore.cs @@ -9,17 +9,15 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Data; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.Caching.Memory; -using OpenIddict.Core; using OpenIddict.Models; +using OpenIddict.Stores; namespace OpenIddict.EntityFrameworkCore { diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs index 7bb3e628..2e290309 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictAuthorizationStore.cs @@ -9,17 +9,16 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Data; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.Caching.Memory; using OpenIddict.Core; using OpenIddict.Models; +using OpenIddict.Stores; namespace OpenIddict.EntityFrameworkCore { diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs index c9155347..6366bba1 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictScopeStore.cs @@ -11,7 +11,6 @@ using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Query; using Microsoft.Extensions.Caching.Memory; using OpenIddict.Models; @@ -54,7 +53,7 @@ namespace OpenIddict.EntityFrameworkCore /// The type of the Scope entity. /// The type of the Entity Framework database context. /// The type of the entity primary keys. - public class OpenIddictScopeStore : Core.OpenIddictScopeStore + public class OpenIddictScopeStore : Stores.OpenIddictScopeStore where TScope : OpenIddictScope, new() where TContext : DbContext where TKey : IEquatable diff --git a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs index 34bcc3e9..696d8f55 100644 --- a/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs +++ b/src/OpenIddict.EntityFrameworkCore/Stores/OpenIddictTokenStore.cs @@ -9,17 +9,16 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Data; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.Caching.Memory; using OpenIddict.Core; using OpenIddict.Models; +using OpenIddict.Stores; namespace OpenIddict.EntityFrameworkCore { diff --git a/src/OpenIddict.Mvc/OpenIddict.Mvc.csproj b/src/OpenIddict.Mvc/OpenIddict.Mvc.csproj index 883679fb..84b356e6 100644 --- a/src/OpenIddict.Mvc/OpenIddict.Mvc.csproj +++ b/src/OpenIddict.Mvc/OpenIddict.Mvc.csproj @@ -13,11 +13,10 @@ - + - diff --git a/src/OpenIddict/OpenIddictHandler.cs b/src/OpenIddict.Server/Internal/OpenIddictHandler.cs similarity index 100% rename from src/OpenIddict/OpenIddictHandler.cs rename to src/OpenIddict.Server/Internal/OpenIddictHandler.cs diff --git a/src/OpenIddict/OpenIddictInitializer.cs b/src/OpenIddict.Server/Internal/OpenIddictInitializer.cs similarity index 100% rename from src/OpenIddict/OpenIddictInitializer.cs rename to src/OpenIddict.Server/Internal/OpenIddictInitializer.cs diff --git a/src/OpenIddict/OpenIddictProvider.Authentication.cs b/src/OpenIddict.Server/Internal/OpenIddictProvider.Authentication.cs similarity index 99% rename from src/OpenIddict/OpenIddictProvider.Authentication.cs rename to src/OpenIddict.Server/Internal/OpenIddictProvider.Authentication.cs index 1c25d163..7405b974 100644 --- a/src/OpenIddict/OpenIddictProvider.Authentication.cs +++ b/src/OpenIddict.Server/Internal/OpenIddictProvider.Authentication.cs @@ -5,7 +5,6 @@ */ using System; -using System.Collections.Immutable; using System.IO; using System.Threading.Tasks; using AspNet.Security.OpenIdConnect.Extensions; diff --git a/src/OpenIddict/OpenIddictProvider.Discovery.cs b/src/OpenIddict.Server/Internal/OpenIddictProvider.Discovery.cs similarity index 100% rename from src/OpenIddict/OpenIddictProvider.Discovery.cs rename to src/OpenIddict.Server/Internal/OpenIddictProvider.Discovery.cs diff --git a/src/OpenIddict/OpenIddictProvider.Exchange.cs b/src/OpenIddict.Server/Internal/OpenIddictProvider.Exchange.cs similarity index 100% rename from src/OpenIddict/OpenIddictProvider.Exchange.cs rename to src/OpenIddict.Server/Internal/OpenIddictProvider.Exchange.cs diff --git a/src/OpenIddict/OpenIddictProvider.Helpers.cs b/src/OpenIddict.Server/Internal/OpenIddictProvider.Helpers.cs similarity index 100% rename from src/OpenIddict/OpenIddictProvider.Helpers.cs rename to src/OpenIddict.Server/Internal/OpenIddictProvider.Helpers.cs diff --git a/src/OpenIddict/OpenIddictProvider.Introspection.cs b/src/OpenIddict.Server/Internal/OpenIddictProvider.Introspection.cs similarity index 100% rename from src/OpenIddict/OpenIddictProvider.Introspection.cs rename to src/OpenIddict.Server/Internal/OpenIddictProvider.Introspection.cs diff --git a/src/OpenIddict/OpenIddictProvider.Revocation.cs b/src/OpenIddict.Server/Internal/OpenIddictProvider.Revocation.cs similarity index 100% rename from src/OpenIddict/OpenIddictProvider.Revocation.cs rename to src/OpenIddict.Server/Internal/OpenIddictProvider.Revocation.cs diff --git a/src/OpenIddict/OpenIddictProvider.Serialization.cs b/src/OpenIddict.Server/Internal/OpenIddictProvider.Serialization.cs similarity index 100% rename from src/OpenIddict/OpenIddictProvider.Serialization.cs rename to src/OpenIddict.Server/Internal/OpenIddictProvider.Serialization.cs diff --git a/src/OpenIddict/OpenIddictProvider.Session.cs b/src/OpenIddict.Server/Internal/OpenIddictProvider.Session.cs similarity index 100% rename from src/OpenIddict/OpenIddictProvider.Session.cs rename to src/OpenIddict.Server/Internal/OpenIddictProvider.Session.cs diff --git a/src/OpenIddict/OpenIddictProvider.Userinfo.cs b/src/OpenIddict.Server/Internal/OpenIddictProvider.Userinfo.cs similarity index 100% rename from src/OpenIddict/OpenIddictProvider.Userinfo.cs rename to src/OpenIddict.Server/Internal/OpenIddictProvider.Userinfo.cs diff --git a/src/OpenIddict/OpenIddictProvider.cs b/src/OpenIddict.Server/Internal/OpenIddictProvider.cs similarity index 100% rename from src/OpenIddict/OpenIddictProvider.cs rename to src/OpenIddict.Server/Internal/OpenIddictProvider.cs diff --git a/src/OpenIddict.Server/OpenIddict.Server.csproj b/src/OpenIddict.Server/OpenIddict.Server.csproj new file mode 100644 index 00000000..d7162d51 --- /dev/null +++ b/src/OpenIddict.Server/OpenIddict.Server.csproj @@ -0,0 +1,26 @@ + + + + + + netstandard2.0 + + + + OpenID Connect server components for OpenIddict. + Kévin Chalet + aspnetcore;authentication;jwt;openidconnect;openiddict;security + + + + + + + + + + + + + + diff --git a/src/OpenIddict.Server/OpenIddictExtensions.cs b/src/OpenIddict.Server/OpenIddictExtensions.cs new file mode 100644 index 00000000..33443e64 --- /dev/null +++ b/src/OpenIddict.Server/OpenIddictExtensions.cs @@ -0,0 +1,1008 @@ +/* + * 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/OpenIddictOptions.cs b/src/OpenIddict.Server/OpenIddictOptions.cs similarity index 100% rename from src/OpenIddict/OpenIddictOptions.cs rename to src/OpenIddict.Server/OpenIddictOptions.cs diff --git a/src/OpenIddict.Stores/OpenIddict.Stores.csproj b/src/OpenIddict.Stores/OpenIddict.Stores.csproj new file mode 100644 index 00000000..1a91dee5 --- /dev/null +++ b/src/OpenIddict.Stores/OpenIddict.Stores.csproj @@ -0,0 +1,24 @@ + + + + + + netstandard2.0 + + + + Default base stores for OpenIddict. + Kévin Chalet + aspnetcore;authentication;jwt;openidconnect;openiddict;security + + + + + + + + + + + + diff --git a/src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs b/src/OpenIddict.Stores/Stores/OpenIddictApplicationStore.cs similarity index 99% rename from src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs rename to src/OpenIddict.Stores/Stores/OpenIddictApplicationStore.cs index dca463fc..9a5b2e1a 100644 --- a/src/OpenIddict.Core/Stores/OpenIddictApplicationStore.cs +++ b/src/OpenIddict.Stores/Stores/OpenIddictApplicationStore.cs @@ -14,9 +14,10 @@ using JetBrains.Annotations; using Microsoft.Extensions.Caching.Memory; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using OpenIddict.Core; using OpenIddict.Models; -namespace OpenIddict.Core +namespace OpenIddict.Stores { /// /// Provides methods allowing to manage the applications stored in a database. diff --git a/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs b/src/OpenIddict.Stores/Stores/OpenIddictAuthorizationStore.cs similarity index 99% rename from src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs rename to src/OpenIddict.Stores/Stores/OpenIddictAuthorizationStore.cs index 9b9a92f5..118ccc1e 100644 --- a/src/OpenIddict.Core/Stores/OpenIddictAuthorizationStore.cs +++ b/src/OpenIddict.Stores/Stores/OpenIddictAuthorizationStore.cs @@ -5,7 +5,6 @@ */ using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Linq; @@ -15,9 +14,10 @@ using JetBrains.Annotations; using Microsoft.Extensions.Caching.Memory; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using OpenIddict.Core; using OpenIddict.Models; -namespace OpenIddict.Core +namespace OpenIddict.Stores { /// /// Provides methods allowing to manage the authorizations stored in a database. diff --git a/src/OpenIddict.Core/Stores/OpenIddictScopeStore.cs b/src/OpenIddict.Stores/Stores/OpenIddictScopeStore.cs similarity index 99% rename from src/OpenIddict.Core/Stores/OpenIddictScopeStore.cs rename to src/OpenIddict.Stores/Stores/OpenIddictScopeStore.cs index 728b698d..5871229c 100644 --- a/src/OpenIddict.Core/Stores/OpenIddictScopeStore.cs +++ b/src/OpenIddict.Stores/Stores/OpenIddictScopeStore.cs @@ -14,9 +14,10 @@ using JetBrains.Annotations; using Microsoft.Extensions.Caching.Memory; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using OpenIddict.Core; using OpenIddict.Models; -namespace OpenIddict.Core +namespace OpenIddict.Stores { /// /// Provides methods allowing to manage the scopes stored in a database. diff --git a/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs b/src/OpenIddict.Stores/Stores/OpenIddictTokenStore.cs similarity index 99% rename from src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs rename to src/OpenIddict.Stores/Stores/OpenIddictTokenStore.cs index 3334fb16..a5938aae 100644 --- a/src/OpenIddict.Core/Stores/OpenIddictTokenStore.cs +++ b/src/OpenIddict.Stores/Stores/OpenIddictTokenStore.cs @@ -5,7 +5,6 @@ */ using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Linq; @@ -15,9 +14,10 @@ using JetBrains.Annotations; using Microsoft.Extensions.Caching.Memory; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using OpenIddict.Core; using OpenIddict.Models; -namespace OpenIddict.Core +namespace OpenIddict.Stores { /// /// Provides methods allowing to manage the tokens stored in a database. diff --git a/src/OpenIddict/OpenIddict.csproj b/src/OpenIddict/OpenIddict.csproj index e1f26ed7..22454bcd 100644 --- a/src/OpenIddict/OpenIddict.csproj +++ b/src/OpenIddict/OpenIddict.csproj @@ -13,15 +13,12 @@ - + + - - - - diff --git a/src/OpenIddict/OpenIddictExtensions.cs b/src/OpenIddict/OpenIddictExtensions.cs index 33443e64..1338e89e 100644 --- a/src/OpenIddict/OpenIddictExtensions.cs +++ b/src/OpenIddict/OpenIddictExtensions.cs @@ -5,1004 +5,77 @@ */ 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; +using OpenIddict.Models; namespace Microsoft.Extensions.DependencyInjection { public static class OpenIddictExtensions { /// - /// Amends the default OpenIddict configuration. + /// Registers the default OpenIddict services in the DI container, + /// using the default entities and the default entity key type. /// - /// 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 services collection. /// The . - public static OpenIddictBuilder Configure( - [NotNull] this OpenIddictBuilder builder, - [NotNull] Action configuration) + public static OpenIddictBuilder AddOpenIddict([NotNull] this IServiceCollection services) { - 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)); + return services.AddOpenIddict(); } /// - /// Disables the configuration endpoint. + /// Registers the default OpenIddict services in the DI container, + /// using the default entities and the specified entity key type. /// - /// The services builder used by OpenIddict to register new services. + /// The type of the entity primary keys. + /// The services collection. /// The . - public static OpenIddictBuilder DisableConfigurationEndpoint([NotNull] this OpenIddictBuilder builder) + public static OpenIddictBuilder AddOpenIddict([NotNull] this IServiceCollection services) + where TKey : IEquatable { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.ConfigurationEndpointPath = PathString.Empty); + return services.AddOpenIddict, + OpenIddictAuthorization, + OpenIddictScope, + OpenIddictToken>(); } /// - /// Disables the cryptography endpoint. + /// Registers the default OpenIddict services in the DI container, + /// using the default entities and the default entity key type. /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder DisableCryptographyEndpoint([NotNull] this OpenIddictBuilder builder) + /// The services collection. + /// The configuration delegate used to register new services. + /// The . + public static IServiceCollection AddOpenIddict( + [NotNull] this IServiceCollection services, + [NotNull] Action configuration) { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - return builder.Configure(options => options.CryptographyEndpointPath = PathString.Empty); + return services.AddOpenIddict(configuration); } /// - /// Disables the HTTPS requirement during development. + /// Registers the default OpenIddict services in the DI container, + /// using the default entities and the specified entity key type. /// - /// The services builder used by OpenIddict to register new services. - /// The . - public static OpenIddictBuilder DisableHttpsRequirement([NotNull] this OpenIddictBuilder builder) + /// 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) + where TKey : IEquatable { - 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); + return services.AddOpenIddict, + OpenIddictAuthorization, + OpenIddictScope, + OpenIddictToken>(configuration); } } } \ No newline at end of file diff --git a/test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs index edb2ccfa..b3467ae8 100644 --- a/test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.Core.Tests/OpenIddictExtensionsTests.cs @@ -6,45 +6,26 @@ using System; using Microsoft.Extensions.DependencyInjection; -using OpenIddict.Models; using Xunit; namespace OpenIddict.Core.Tests { public class OpenIddictExtensionsTests { - [Theory] - [InlineData(typeof(OpenIddictApplicationManager))] - [InlineData(typeof(OpenIddictAuthorizationManager))] - [InlineData(typeof(OpenIddictScopeManager))] - [InlineData(typeof(OpenIddictTokenManager))] - public void AddOpenIddict_KeyTypeDefaultsToString(Type type) - { - // Arrange - var services = new ServiceCollection(); - - // Act - services.AddOpenIddict(); - - // Assert - Assert.Contains(services, service => service.ImplementationType == type); - } - - [Theory] - [InlineData(typeof(OpenIddictApplicationManager>))] - [InlineData(typeof(OpenIddictAuthorizationManager>))] - [InlineData(typeof(OpenIddictScopeManager>))] - [InlineData(typeof(OpenIddictTokenManager>))] - public void AddOpenIddict_KeyTypeCanBeOverriden(Type type) + [Fact] + public void AddOpenIddict_CustomEntitiesAreCorrectlySet() { // Arrange var services = new ServiceCollection(); // Act - services.AddOpenIddict(); + var builder = services.AddOpenIddict(); // Assert - Assert.Contains(services, service => service.ImplementationType == type); + 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] @@ -52,7 +33,7 @@ namespace OpenIddict.Core.Tests [InlineData(typeof(OpenIddictAuthorizationManager))] [InlineData(typeof(OpenIddictScopeManager))] [InlineData(typeof(OpenIddictTokenManager))] - public void AddOpenIddict_DefaultEntitiesCanBeReplaced(Type type) + public void AddOpenIddict_ManagersForCustomEntitiesAreCorrectlyRegistered(Type type) { // Arrange var services = new ServiceCollection(); diff --git a/test/OpenIddict.EntityFramework.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.EntityFramework.Tests/OpenIddictExtensionsTests.cs index 45d9007c..874bd0b3 100644 --- a/test/OpenIddict.EntityFramework.Tests/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.EntityFramework.Tests/OpenIddictExtensionsTests.cs @@ -21,7 +21,10 @@ namespace OpenIddict.EntityFrameworkCore.Tests // Arrange var builder = new OpenIddictBuilder(new ServiceCollection()) { - ApplicationType = typeof(object) + ApplicationType = typeof(object), + AuthorizationType = typeof(OpenIddictAuthorization), + ScopeType = typeof(OpenIddictScope), + TokenType = typeof(OpenIddictToken) }; // Act and assert @@ -40,7 +43,10 @@ namespace OpenIddict.EntityFrameworkCore.Tests // Arrange var builder = new OpenIddictBuilder(new ServiceCollection()) { - AuthorizationType = typeof(object) + ApplicationType = typeof(OpenIddictApplication), + AuthorizationType = typeof(object), + ScopeType = typeof(OpenIddictScope), + TokenType = typeof(OpenIddictToken) }; // Act and assert @@ -59,7 +65,10 @@ namespace OpenIddict.EntityFrameworkCore.Tests // Arrange var builder = new OpenIddictBuilder(new ServiceCollection()) { - ScopeType = typeof(object) + ApplicationType = typeof(OpenIddictApplication), + AuthorizationType = typeof(OpenIddictAuthorization), + ScopeType = typeof(object), + TokenType = typeof(OpenIddictToken) }; // Act and assert @@ -78,6 +87,9 @@ namespace OpenIddict.EntityFrameworkCore.Tests // Arrange var builder = new OpenIddictBuilder(new ServiceCollection()) { + ApplicationType = typeof(OpenIddictApplication), + AuthorizationType = typeof(OpenIddictAuthorization), + ScopeType = typeof(OpenIddictScope), TokenType = typeof(object) }; @@ -100,7 +112,13 @@ namespace OpenIddict.EntityFrameworkCore.Tests { // Arrange var services = new ServiceCollection(); - var builder = new OpenIddictBuilder(services); + var builder = new OpenIddictBuilder(services) + { + ApplicationType = typeof(OpenIddictApplication), + AuthorizationType = typeof(OpenIddictAuthorization), + ScopeType = typeof(OpenIddictScope), + TokenType = typeof(OpenIddictToken) + }; // Act builder.AddEntityFrameworkStores(); diff --git a/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs index 00b9f939..42752609 100644 --- a/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.EntityFrameworkCore.Tests/OpenIddictExtensionsTests.cs @@ -20,7 +20,10 @@ namespace OpenIddict.EntityFrameworkCore.Tests // Arrange var builder = new OpenIddictBuilder(new ServiceCollection()) { - ApplicationType = typeof(object) + ApplicationType = typeof(object), + AuthorizationType = typeof(OpenIddictAuthorization), + ScopeType = typeof(OpenIddictScope), + TokenType = typeof(OpenIddictToken) }; // Act and assert @@ -39,7 +42,10 @@ namespace OpenIddict.EntityFrameworkCore.Tests // Arrange var builder = new OpenIddictBuilder(new ServiceCollection()) { - AuthorizationType = typeof(object) + ApplicationType = typeof(OpenIddictApplication), + AuthorizationType = typeof(object), + ScopeType = typeof(OpenIddictScope), + TokenType = typeof(OpenIddictToken) }; // Act and assert @@ -58,7 +64,10 @@ namespace OpenIddict.EntityFrameworkCore.Tests // Arrange var builder = new OpenIddictBuilder(new ServiceCollection()) { - ScopeType = typeof(object) + ApplicationType = typeof(OpenIddictApplication), + AuthorizationType = typeof(OpenIddictAuthorization), + ScopeType = typeof(object), + TokenType = typeof(OpenIddictToken) }; // Act and assert @@ -77,6 +86,9 @@ namespace OpenIddict.EntityFrameworkCore.Tests // Arrange var builder = new OpenIddictBuilder(new ServiceCollection()) { + ApplicationType = typeof(OpenIddictApplication), + AuthorizationType = typeof(OpenIddictAuthorization), + ScopeType = typeof(OpenIddictScope), TokenType = typeof(object) }; @@ -99,7 +111,13 @@ namespace OpenIddict.EntityFrameworkCore.Tests { // Arrange var services = new ServiceCollection(); - var builder = new OpenIddictBuilder(services); + var builder = new OpenIddictBuilder(services) + { + ApplicationType = typeof(OpenIddictApplication), + AuthorizationType = typeof(OpenIddictAuthorization), + ScopeType = typeof(OpenIddictScope), + TokenType = typeof(OpenIddictToken) + }; // Act builder.AddEntityFrameworkCoreStores(); diff --git a/test/OpenIddict.Tests/Certificate.pfx b/test/OpenIddict.Server.Tests/Certificate.pfx similarity index 100% rename from test/OpenIddict.Tests/Certificate.pfx rename to test/OpenIddict.Server.Tests/Certificate.pfx diff --git a/test/OpenIddict.Tests/OpenIddictInitializerTests.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictInitializerTests.cs similarity index 100% rename from test/OpenIddict.Tests/OpenIddictInitializerTests.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictInitializerTests.cs diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Authentication.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Authentication.cs similarity index 100% rename from test/OpenIddict.Tests/OpenIddictProviderTests.Authentication.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Authentication.cs diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Discovery.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Discovery.cs similarity index 100% rename from test/OpenIddict.Tests/OpenIddictProviderTests.Discovery.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Discovery.cs diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Exchange.cs similarity index 100% rename from test/OpenIddict.Tests/OpenIddictProviderTests.Exchange.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Exchange.cs diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Introspection.cs similarity index 100% rename from test/OpenIddict.Tests/OpenIddictProviderTests.Introspection.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Introspection.cs diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Revocation.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Revocation.cs similarity index 100% rename from test/OpenIddict.Tests/OpenIddictProviderTests.Revocation.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Revocation.cs diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Serialization.cs similarity index 100% rename from test/OpenIddict.Tests/OpenIddictProviderTests.Serialization.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Serialization.cs diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Session.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Session.cs similarity index 100% rename from test/OpenIddict.Tests/OpenIddictProviderTests.Session.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Session.cs diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.Userinfo.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Userinfo.cs similarity index 100% rename from test/OpenIddict.Tests/OpenIddictProviderTests.Userinfo.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.Userinfo.cs diff --git a/test/OpenIddict.Tests/OpenIddictProviderTests.cs b/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.cs similarity index 99% rename from test/OpenIddict.Tests/OpenIddictProviderTests.cs rename to test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.cs index e9afe2d1..c8995cc8 100644 --- a/test/OpenIddict.Tests/OpenIddictProviderTests.cs +++ b/test/OpenIddict.Server.Tests/Internal/OpenIddictProviderTests.cs @@ -1390,7 +1390,7 @@ namespace OpenIddict.Tests // Register the X.509 certificate used to sign the identity tokens. options.AddSigningCertificate( assembly: typeof(OpenIddictProviderTests).GetTypeInfo().Assembly, - resource: "OpenIddict.Tests.Certificate.pfx", + resource: "OpenIddict.Server.Tests.Certificate.pfx", password: "OpenIddict"); // Note: overriding the default data protection provider is not necessary for the tests to pass, diff --git a/test/OpenIddict.Server.Tests/OpenIddict.Server.Tests.csproj b/test/OpenIddict.Server.Tests/OpenIddict.Server.Tests.csproj new file mode 100644 index 00000000..0cbd8285 --- /dev/null +++ b/test/OpenIddict.Server.Tests/OpenIddict.Server.Tests.csproj @@ -0,0 +1,46 @@ + + + + + + netcoreapp2.0;net461 + $(TargetFrameworks);net47 + netcoreapp2.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(DefineConstants);SUPPORTS_ECDSA + + + + $(DefineConstants);SUPPORTS_CERTIFICATE_GENERATION + + + diff --git a/test/OpenIddict.Server.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.Server.Tests/OpenIddictExtensionsTests.cs new file mode 100644 index 00000000..ca6704a2 --- /dev/null +++ b/test/OpenIddict.Server.Tests/OpenIddictExtensionsTests.cs @@ -0,0 +1,721 @@ +/* + * 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.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 +{ + public class OpenIddictExtensionsTests + { + [Fact] + public void Configure_OptionsAreCorrectlyAmended() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.Configure(configuration => configuration.AccessTokenLifetime = TimeSpan.FromDays(1)); + + var options = GetOptions(services); + + // Assert + 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() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act and assert + var exception = Assert.Throws(delegate + { + builder.AddDevelopmentSigningCertificate(subject: null); + }); + + Assert.Equal("subject", exception.ParamName); + } + +#if SUPPORTS_CERTIFICATE_GENERATION + [Fact] + public void AddDevelopmentSigningCertificate_CanGenerateCertificate() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AddDevelopmentSigningCertificate(); + + var options = GetOptions(services); + + // Assert + Assert.Equal(1, options.SigningCredentials.Count); + Assert.Equal(SecurityAlgorithms.RsaSha256, options.SigningCredentials[0].Algorithm); + Assert.NotNull(options.SigningCredentials[0].Kid); + } +#else + [Fact] + public void AddDevelopmentSigningCertificate_ThrowsAnExceptionOnUnsupportedPlatforms() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + builder.AddDevelopmentSigningCertificate(); + + // Act and assert + var exception = Assert.Throws(delegate + { + return GetOptions(services); + }); + + Assert.Equal("X.509 certificate generation is not supported on this platform.", exception.Message); + } +#endif + + [Fact] + public void AddEphemeralSigningKey_SigningKeyIsCorrectlyAdded() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AddEphemeralSigningKey(); + + var options = GetOptions(services); + + // Assert + Assert.Equal(1, options.SigningCredentials.Count); + } + + [Theory] + [InlineData(SecurityAlgorithms.RsaSha256)] + [InlineData(SecurityAlgorithms.RsaSha384)] + [InlineData(SecurityAlgorithms.RsaSha512)] +#if SUPPORTS_ECDSA + [InlineData(SecurityAlgorithms.EcdsaSha256)] + [InlineData(SecurityAlgorithms.EcdsaSha384)] + [InlineData(SecurityAlgorithms.EcdsaSha512)] +#endif + public void AddEphemeralSigningKey_SigningCredentialsUseSpecifiedAlgorithm(string algorithm) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AddEphemeralSigningKey(algorithm); + + var options = GetOptions(services); + var credentials = options.SigningCredentials[0]; + + // Assert + Assert.Equal(algorithm, credentials.Algorithm); + } + + [Fact] + public void AddEncryptingKey_EncryptingKeyIsCorrectlyAdded() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + var factory = Mock.Of(mock => + mock.IsSupportedAlgorithm(SecurityAlgorithms.Aes256KW, It.IsAny())); + + var key = Mock.Of(mock => mock.CryptoProviderFactory == factory); + + // Act + builder.AddEncryptingKey(key); + + var options = GetOptions(services); + + // Assert + Assert.Same(key, options.EncryptingCredentials[0].Key); + } + + [Theory] + [InlineData(SecurityAlgorithms.HmacSha256)] + [InlineData(SecurityAlgorithms.RsaSha256)] +#if SUPPORTS_ECDSA + [InlineData(SecurityAlgorithms.EcdsaSha256)] + [InlineData(SecurityAlgorithms.EcdsaSha384)] + [InlineData(SecurityAlgorithms.EcdsaSha512)] +#endif + public void AddSigningKey_SigningKeyIsCorrectlyAdded(string algorithm) + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + var factory = Mock.Of(mock => + mock.IsSupportedAlgorithm(algorithm, It.IsAny())); + + var key = Mock.Of(mock => mock.CryptoProviderFactory == factory); + + // Act + builder.AddSigningKey(key); + + var options = GetOptions(services); + + // Assert + Assert.Same(key, options.SigningCredentials[0].Key); + } + + [Fact] + public void AddSigningCertificate_SigningKeyIsCorrectlyAdded() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AddSigningCertificate( + assembly: typeof(OpenIddictExtensionsTests).GetTypeInfo().Assembly, + resource: "OpenIddict.Server.Tests.Certificate.pfx", + password: "OpenIddict"); + + var options = GetOptions(services); + + // Assert + Assert.IsType(options.SigningCredentials[0].Key); + } + + [Fact] + public void AllowAuthorizationCodeFlow_CodeFlowIsAddedToGrantTypes() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AllowAuthorizationCodeFlow(); + + var options = GetOptions(services); + + // Assert + Assert.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode, options.GrantTypes); + } + + [Fact] + public void AllowClientCredentialsFlow_ClientCredentialsFlowIsAddedToGrantTypes() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AllowClientCredentialsFlow(); + + var options = GetOptions(services); + + // Assert + Assert.Contains(OpenIdConnectConstants.GrantTypes.ClientCredentials, options.GrantTypes); + } + + [Fact] + public void AllowCustomFlow_CustomFlowIsAddedToGrantTypes() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AllowCustomFlow("urn:ietf:params:oauth:grant-type:custom_grant"); + + var options = GetOptions(services); + + // Assert + Assert.Contains("urn:ietf:params:oauth:grant-type:custom_grant", options.GrantTypes); + } + + [Fact] + public void AllowImplicitFlow_ImplicitFlowIsAddedToGrantTypes() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AllowImplicitFlow(); + + var options = GetOptions(services); + + // Assert + Assert.Contains(OpenIdConnectConstants.GrantTypes.Implicit, options.GrantTypes); + } + + [Fact] + public void AllowPasswordFlow_PasswordFlowIsAddedToGrantTypes() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AllowPasswordFlow(); + + var options = GetOptions(services); + + // Assert + Assert.Contains(OpenIdConnectConstants.GrantTypes.Password, options.GrantTypes); + } + + [Fact] + public void AllowRefreshTokenFlow_RefreshTokenFlowIsAddedToGrantTypes() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.AllowRefreshTokenFlow(); + + var options = GetOptions(services); + + // Assert + Assert.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken, options.GrantTypes); + } + + [Fact] + public void DisableConfigurationEndpoint_ConfigurationEndpointIsDisabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.DisableConfigurationEndpoint(); + + var options = GetOptions(services); + + // Assert + Assert.Equal(PathString.Empty, options.ConfigurationEndpointPath); + } + + [Fact] + public void DisableCryptographyEndpoint_CryptographyEndpointIsDisabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.DisableCryptographyEndpoint(); + + var options = GetOptions(services); + + // Assert + Assert.Equal(PathString.Empty, options.CryptographyEndpointPath); + } + + [Fact] + public void DisableSlidingExpiration_SlidingExpirationIsDisabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.DisableSlidingExpiration(); + + var options = GetOptions(services); + + // Assert + Assert.False(options.UseSlidingExpiration); + } + + [Fact] + public void DisableTokenRevocation_TokenRevocationIsDisabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.DisableTokenRevocation(); + + var options = GetOptions(services); + + // Assert + Assert.True(options.DisableTokenRevocation); + } + + [Fact] + public void EnableAuthorizationEndpoint_AuthorizationEndpointIsEnabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.EnableAuthorizationEndpoint("/endpoint-path"); + + var options = GetOptions(services); + + // Assert + Assert.Equal("/endpoint-path", options.AuthorizationEndpointPath); + } + + [Fact] + public void EnableIntrospectionEndpoint_IntrospectionEndpointIsEnabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.EnableIntrospectionEndpoint("/endpoint-path"); + + var options = GetOptions(services); + + // Assert + Assert.Equal("/endpoint-path", options.IntrospectionEndpointPath); + } + + [Fact] + public void EnableLogoutEndpoint_LogoutEndpointIsEnabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.EnableLogoutEndpoint("/endpoint-path"); + + var options = GetOptions(services); + + // Assert + Assert.Equal("/endpoint-path", options.LogoutEndpointPath); + } + + [Fact] + public void EnableRequestCaching_RequestCachingIsEnabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.EnableRequestCaching(); + + var options = GetOptions(services); + + // Assert + Assert.True(options.EnableRequestCaching); + } + + [Fact] + public void EnableRevocationEndpoint_RevocationEndpointIsEnabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.EnableRevocationEndpoint("/endpoint-path"); + + var options = GetOptions(services); + + // Assert + Assert.Equal("/endpoint-path", options.RevocationEndpointPath); + } + + [Fact] + public void EnableScopeValidation_ScopeValidationIsDisabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.EnableScopeValidation(); + + var options = GetOptions(services); + + // Assert + Assert.True(options.EnableScopeValidation); + } + + [Fact] + public void EnableTokenEndpoint_TokenEndpointIsEnabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.EnableTokenEndpoint("/endpoint-path"); + + var options = GetOptions(services); + + // Assert + Assert.Equal("/endpoint-path", options.TokenEndpointPath); + } + + [Fact] + public void EnableUserinfoEndpoint_UserinfoEndpointIsEnabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.EnableUserinfoEndpoint("/endpoint-path"); + + var options = GetOptions(services); + + // Assert + Assert.Equal("/endpoint-path", options.UserinfoEndpointPath); + } + + [Fact] + public void RequireClientIdentification_ClientIdentificationIsEnforced() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.RequireClientIdentification(); + + var options = GetOptions(services); + + // Assert + Assert.True(options.RequireClientIdentification); + } + + [Fact] + public void SetAccessTokenLifetime_DefaultAccessTokenLifetimeIsReplaced() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetAccessTokenLifetime(TimeSpan.FromMinutes(42)); + + var options = GetOptions(services); + + // Assert + Assert.Equal(TimeSpan.FromMinutes(42), options.AccessTokenLifetime); + } + + [Fact] + public void SetAuthorizationCodeLifetime_DefaultAuthorizationCodeLifetimeIsReplaced() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetAuthorizationCodeLifetime(TimeSpan.FromMinutes(42)); + + var options = GetOptions(services); + + // Assert + Assert.Equal(TimeSpan.FromMinutes(42), options.AuthorizationCodeLifetime); + } + + [Fact] + public void SetIdentityTokenLifetime_DefaultIdentityTokenLifetimeIsReplaced() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetIdentityTokenLifetime(TimeSpan.FromMinutes(42)); + + var options = GetOptions(services); + + // Assert + Assert.Equal(TimeSpan.FromMinutes(42), options.IdentityTokenLifetime); + } + + [Fact] + public void SetRefreshTokenLifetime_DefaultRefreshTokenLifetimeIsReplaced() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetRefreshTokenLifetime(TimeSpan.FromMinutes(42)); + + var options = GetOptions(services); + + // Assert + Assert.Equal(TimeSpan.FromMinutes(42), options.RefreshTokenLifetime); + } + + [Fact] + public void SetIssuer_AddressIsReplaced() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.SetIssuer(new Uri("http://www.fabrikam.com/")); + + var options = GetOptions(services); + + // Assert + Assert.Equal(new Uri("http://www.fabrikam.com/"), options.Issuer); + } + + [Fact] + public void RegisterClaims_ClaimsAreAdded() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.RegisterClaims("custom_claim_1", "custom_claim_2"); + + var options = GetOptions(services); + + // Assert + Assert.Contains("custom_claim_1", options.Claims); + Assert.Contains("custom_claim_2", options.Claims); + } + + [Fact] + public void RegisterScopes_ScopesAreAdded() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.RegisterScopes("custom_scope_1", "custom_scope_2"); + + var options = GetOptions(services); + + // Assert + Assert.Contains("custom_scope_1", options.Scopes); + Assert.Contains("custom_scope_2", options.Scopes); + } + + [Fact] + public void UseDataProtectionProvider_DefaultProviderIsReplaced() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.UseDataProtectionProvider(new EphemeralDataProtectionProvider(new LoggerFactory())); + + var options = GetOptions(services); + + // Assert + Assert.IsType(options.DataProtectionProvider); + } + + [Fact] + public void UseJsonWebTokens_AccessTokenHandlerIsCorrectlySet() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.UseJsonWebTokens(); + + var options = GetOptions(services); + + // Assert + Assert.IsType(options.AccessTokenHandler); + } + + [Fact] + public void UseReferenceTokens_ReferenceTokensAreEnabled() + { + // Arrange + var services = CreateServices(); + var builder = CreateBuilder(services); + + // Act + builder.UseReferenceTokens(); + + var options = GetOptions(services); + + // Assert + 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 IServiceCollection CreateServices() + { + var services = new ServiceCollection(); + services.AddAuthentication(); + services.AddDistributedMemoryCache(); + services.AddLogging(); + services.AddSingleton(); + + return services; + } + + private static OpenIddictOptions GetOptions(IServiceCollection services) + { + services.RemoveAll>(); + services.RemoveAll>(); + + var provider = services.BuildServiceProvider(); + + var options = provider.GetRequiredService>(); + return options.Get(OpenIdConnectServerDefaults.AuthenticationScheme); + } + } +} diff --git a/test/OpenIddict.Tests/OpenIddict.Tests.csproj b/test/OpenIddict.Tests/OpenIddict.Tests.csproj index def91b32..ef85a9bc 100644 --- a/test/OpenIddict.Tests/OpenIddict.Tests.csproj +++ b/test/OpenIddict.Tests/OpenIddict.Tests.csproj @@ -8,23 +8,12 @@ netcoreapp2.0 - - - - - - - - - - - - + @@ -35,12 +24,4 @@ - - $(DefineConstants);SUPPORTS_ECDSA - - - - $(DefineConstants);SUPPORTS_CERTIFICATE_GENERATION - - diff --git a/test/OpenIddict.Tests/OpenIddictExtensionsTests.cs b/test/OpenIddict.Tests/OpenIddictExtensionsTests.cs index 1da25a46..f6566cca 100644 --- a/test/OpenIddict.Tests/OpenIddictExtensionsTests.cs +++ b/test/OpenIddict.Tests/OpenIddictExtensionsTests.cs @@ -5,707 +5,47 @@ */ using System; -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.Core; +using OpenIddict.Models; using Xunit; namespace OpenIddict.Tests { public class OpenIddictExtensionsTests { - [Fact] - public void Configure_OptionsAreCorrectlyAmended() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.Configure(configuration => configuration.AccessTokenLifetime = TimeSpan.FromDays(1)); - - var options = GetOptions(services); - - // Assert - 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() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act and assert - var exception = Assert.Throws(delegate - { - builder.AddDevelopmentSigningCertificate(subject: null); - }); - - Assert.Equal("subject", exception.ParamName); - } - -#if SUPPORTS_CERTIFICATE_GENERATION - [Fact] - public void AddDevelopmentSigningCertificate_CanGenerateCertificate() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.AddDevelopmentSigningCertificate(); - - var options = GetOptions(services); - - // Assert - Assert.Equal(1, options.SigningCredentials.Count); - Assert.Equal(SecurityAlgorithms.RsaSha256, options.SigningCredentials[0].Algorithm); - Assert.NotNull(options.SigningCredentials[0].Kid); - } -#else - [Fact] - public void AddDevelopmentSigningCertificate_ThrowsAnExceptionOnUnsupportedPlatforms() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - builder.AddDevelopmentSigningCertificate(); - - // Act and assert - var exception = Assert.Throws(delegate - { - return GetOptions(services); - }); - - Assert.Equal("X.509 certificate generation is not supported on this platform.", exception.Message); - } -#endif - - [Fact] - public void AddEphemeralSigningKey_SigningKeyIsCorrectlyAdded() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.AddEphemeralSigningKey(); - - var options = GetOptions(services); - - // Assert - Assert.Equal(1, options.SigningCredentials.Count); - } - [Theory] - [InlineData(SecurityAlgorithms.RsaSha256)] - [InlineData(SecurityAlgorithms.RsaSha384)] - [InlineData(SecurityAlgorithms.RsaSha512)] -#if SUPPORTS_ECDSA - [InlineData(SecurityAlgorithms.EcdsaSha256)] - [InlineData(SecurityAlgorithms.EcdsaSha384)] - [InlineData(SecurityAlgorithms.EcdsaSha512)] -#endif - public void AddEphemeralSigningKey_SigningCredentialsUseSpecifiedAlgorithm(string algorithm) - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.AddEphemeralSigningKey(algorithm); - - var options = GetOptions(services); - var credentials = options.SigningCredentials[0]; - - // Assert - Assert.Equal(algorithm, credentials.Algorithm); - } - - [Fact] - public void AddEncryptingKey_EncryptingKeyIsCorrectlyAdded() + [InlineData(typeof(OpenIddictApplicationManager))] + [InlineData(typeof(OpenIddictAuthorizationManager))] + [InlineData(typeof(OpenIddictScopeManager))] + [InlineData(typeof(OpenIddictTokenManager))] + public void AddOpenIddict_KeyTypeDefaultsToString(Type type) { // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - var factory = Mock.Of(mock => - mock.IsSupportedAlgorithm(SecurityAlgorithms.Aes256KW, It.IsAny())); - - var key = Mock.Of(mock => mock.CryptoProviderFactory == factory); + var services = new ServiceCollection(); // Act - builder.AddEncryptingKey(key); - - var options = GetOptions(services); + services.AddOpenIddict(); // Assert - Assert.Same(key, options.EncryptingCredentials[0].Key); + Assert.Contains(services, service => service.ImplementationType == type); } [Theory] - [InlineData(SecurityAlgorithms.HmacSha256)] - [InlineData(SecurityAlgorithms.RsaSha256)] -#if SUPPORTS_ECDSA - [InlineData(SecurityAlgorithms.EcdsaSha256)] - [InlineData(SecurityAlgorithms.EcdsaSha384)] - [InlineData(SecurityAlgorithms.EcdsaSha512)] -#endif - public void AddSigningKey_SigningKeyIsCorrectlyAdded(string algorithm) - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - var factory = Mock.Of(mock => - mock.IsSupportedAlgorithm(algorithm, It.IsAny())); - - var key = Mock.Of(mock => mock.CryptoProviderFactory == factory); - - // Act - builder.AddSigningKey(key); - - var options = GetOptions(services); - - // Assert - Assert.Same(key, options.SigningCredentials[0].Key); - } - - [Fact] - public void AddSigningCertificate_SigningKeyIsCorrectlyAdded() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.AddSigningCertificate( - assembly: typeof(OpenIddictExtensionsTests).GetTypeInfo().Assembly, - resource: "OpenIddict.Tests.Certificate.pfx", - password: "OpenIddict"); - - var options = GetOptions(services); - - // Assert - Assert.IsType(options.SigningCredentials[0].Key); - } - - [Fact] - public void AllowAuthorizationCodeFlow_CodeFlowIsAddedToGrantTypes() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.AllowAuthorizationCodeFlow(); - - var options = GetOptions(services); - - // Assert - Assert.Contains(OpenIdConnectConstants.GrantTypes.AuthorizationCode, options.GrantTypes); - } - - [Fact] - public void AllowClientCredentialsFlow_ClientCredentialsFlowIsAddedToGrantTypes() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.AllowClientCredentialsFlow(); - - var options = GetOptions(services); - - // Assert - Assert.Contains(OpenIdConnectConstants.GrantTypes.ClientCredentials, options.GrantTypes); - } - - [Fact] - public void AllowCustomFlow_CustomFlowIsAddedToGrantTypes() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.AllowCustomFlow("urn:ietf:params:oauth:grant-type:custom_grant"); - - var options = GetOptions(services); - - // Assert - Assert.Contains("urn:ietf:params:oauth:grant-type:custom_grant", options.GrantTypes); - } - - [Fact] - public void AllowImplicitFlow_ImplicitFlowIsAddedToGrantTypes() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.AllowImplicitFlow(); - - var options = GetOptions(services); - - // Assert - Assert.Contains(OpenIdConnectConstants.GrantTypes.Implicit, options.GrantTypes); - } - - [Fact] - public void AllowPasswordFlow_PasswordFlowIsAddedToGrantTypes() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.AllowPasswordFlow(); - - var options = GetOptions(services); - - // Assert - Assert.Contains(OpenIdConnectConstants.GrantTypes.Password, options.GrantTypes); - } - - [Fact] - public void AllowRefreshTokenFlow_RefreshTokenFlowIsAddedToGrantTypes() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.AllowRefreshTokenFlow(); - - var options = GetOptions(services); - - // Assert - Assert.Contains(OpenIdConnectConstants.GrantTypes.RefreshToken, options.GrantTypes); - } - - [Fact] - public void DisableConfigurationEndpoint_ConfigurationEndpointIsDisabled() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.DisableConfigurationEndpoint(); - - var options = GetOptions(services); - - // Assert - Assert.Equal(PathString.Empty, options.ConfigurationEndpointPath); - } - - [Fact] - public void DisableCryptographyEndpoint_CryptographyEndpointIsDisabled() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.DisableCryptographyEndpoint(); - - var options = GetOptions(services); - - // Assert - Assert.Equal(PathString.Empty, options.CryptographyEndpointPath); - } - - [Fact] - public void DisableSlidingExpiration_SlidingExpirationIsDisabled() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.DisableSlidingExpiration(); - - var options = GetOptions(services); - - // Assert - Assert.False(options.UseSlidingExpiration); - } - - [Fact] - public void DisableTokenRevocation_TokenRevocationIsDisabled() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.DisableTokenRevocation(); - - var options = GetOptions(services); - - // Assert - Assert.True(options.DisableTokenRevocation); - } - - [Fact] - public void EnableAuthorizationEndpoint_AuthorizationEndpointIsEnabled() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.EnableAuthorizationEndpoint("/endpoint-path"); - - var options = GetOptions(services); - - // Assert - Assert.Equal("/endpoint-path", options.AuthorizationEndpointPath); - } - - [Fact] - public void EnableIntrospectionEndpoint_IntrospectionEndpointIsEnabled() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.EnableIntrospectionEndpoint("/endpoint-path"); - - var options = GetOptions(services); - - // Assert - Assert.Equal("/endpoint-path", options.IntrospectionEndpointPath); - } - - [Fact] - public void EnableLogoutEndpoint_LogoutEndpointIsEnabled() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.EnableLogoutEndpoint("/endpoint-path"); - - var options = GetOptions(services); - - // Assert - Assert.Equal("/endpoint-path", options.LogoutEndpointPath); - } - - [Fact] - public void EnableRequestCaching_RequestCachingIsEnabled() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.EnableRequestCaching(); - - var options = GetOptions(services); - - // Assert - Assert.True(options.EnableRequestCaching); - } - - [Fact] - public void EnableRevocationEndpoint_RevocationEndpointIsEnabled() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.EnableRevocationEndpoint("/endpoint-path"); - - var options = GetOptions(services); - - // Assert - Assert.Equal("/endpoint-path", options.RevocationEndpointPath); - } - - [Fact] - public void EnableScopeValidation_ScopeValidationIsDisabled() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.EnableScopeValidation(); - - var options = GetOptions(services); - - // Assert - Assert.True(options.EnableScopeValidation); - } - - [Fact] - public void EnableTokenEndpoint_TokenEndpointIsEnabled() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.EnableTokenEndpoint("/endpoint-path"); - - var options = GetOptions(services); - - // Assert - Assert.Equal("/endpoint-path", options.TokenEndpointPath); - } - - [Fact] - public void EnableUserinfoEndpoint_UserinfoEndpointIsEnabled() + [InlineData(typeof(OpenIddictApplicationManager>))] + [InlineData(typeof(OpenIddictAuthorizationManager>))] + [InlineData(typeof(OpenIddictScopeManager>))] + [InlineData(typeof(OpenIddictTokenManager>))] + public void AddOpenIddict_KeyTypeCanBeOverriden(Type type) { // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.EnableUserinfoEndpoint("/endpoint-path"); - - var options = GetOptions(services); - - // Assert - Assert.Equal("/endpoint-path", options.UserinfoEndpointPath); - } - - [Fact] - public void RequireClientIdentification_ClientIdentificationIsEnforced() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.RequireClientIdentification(); - - var options = GetOptions(services); - - // Assert - Assert.True(options.RequireClientIdentification); - } - - [Fact] - public void SetAccessTokenLifetime_DefaultAccessTokenLifetimeIsReplaced() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.SetAccessTokenLifetime(TimeSpan.FromMinutes(42)); - - var options = GetOptions(services); - - // Assert - Assert.Equal(TimeSpan.FromMinutes(42), options.AccessTokenLifetime); - } - - [Fact] - public void SetAuthorizationCodeLifetime_DefaultAuthorizationCodeLifetimeIsReplaced() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.SetAuthorizationCodeLifetime(TimeSpan.FromMinutes(42)); - - var options = GetOptions(services); - - // Assert - Assert.Equal(TimeSpan.FromMinutes(42), options.AuthorizationCodeLifetime); - } - - [Fact] - public void SetIdentityTokenLifetime_DefaultIdentityTokenLifetimeIsReplaced() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.SetIdentityTokenLifetime(TimeSpan.FromMinutes(42)); - - var options = GetOptions(services); - - // Assert - Assert.Equal(TimeSpan.FromMinutes(42), options.IdentityTokenLifetime); - } - - [Fact] - public void SetRefreshTokenLifetime_DefaultRefreshTokenLifetimeIsReplaced() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.SetRefreshTokenLifetime(TimeSpan.FromMinutes(42)); - - var options = GetOptions(services); - - // Assert - Assert.Equal(TimeSpan.FromMinutes(42), options.RefreshTokenLifetime); - } - - [Fact] - public void SetIssuer_AddressIsReplaced() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.SetIssuer(new Uri("http://www.fabrikam.com/")); - - var options = GetOptions(services); - - // Assert - Assert.Equal(new Uri("http://www.fabrikam.com/"), options.Issuer); - } - - [Fact] - public void RegisterClaims_ClaimsAreAdded() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.RegisterClaims("custom_claim_1", "custom_claim_2"); - - var options = GetOptions(services); - - // Assert - Assert.Contains("custom_claim_1", options.Claims); - Assert.Contains("custom_claim_2", options.Claims); - } - - [Fact] - public void RegisterScopes_ScopesAreAdded() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.RegisterScopes("custom_scope_1", "custom_scope_2"); - - var options = GetOptions(services); - - // Assert - Assert.Contains("custom_scope_1", options.Scopes); - Assert.Contains("custom_scope_2", options.Scopes); - } - - [Fact] - public void UseDataProtectionProvider_DefaultProviderIsReplaced() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.UseDataProtectionProvider(new EphemeralDataProtectionProvider(new LoggerFactory())); - - var options = GetOptions(services); - - // Assert - Assert.IsType(options.DataProtectionProvider); - } - - [Fact] - public void UseJsonWebTokens_AccessTokenHandlerIsCorrectlySet() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); - - // Act - builder.UseJsonWebTokens(); - - var options = GetOptions(services); - - // Assert - Assert.IsType(options.AccessTokenHandler); - } - - [Fact] - public void UseReferenceTokens_ReferenceTokensAreEnabled() - { - // Arrange - var services = CreateServices(); - var builder = new OpenIddictBuilder(services); + var services = new ServiceCollection(); // Act - builder.UseReferenceTokens(); - - var options = GetOptions(services); + services.AddOpenIddict(); // Assert - Assert.True(options.UseReferenceTokens); - } - - private static IServiceCollection CreateServices() - { - var services = new ServiceCollection(); - services.AddAuthentication(); - services.AddDistributedMemoryCache(); - services.AddLogging(); - services.AddSingleton(); - - return services; - } - - private static OpenIddictOptions GetOptions(IServiceCollection services) - { - services.RemoveAll>(); - services.RemoveAll>(); - - var provider = services.BuildServiceProvider(); - - var options = provider.GetRequiredService>(); - return options.Get(OpenIdConnectServerDefaults.AuthenticationScheme); + Assert.Contains(services, service => service.ImplementationType == type); } } }