diff --git a/OpenIddict.sln b/OpenIddict.sln index f3763ff4..d35800a1 100644 --- a/OpenIddict.sln +++ b/OpenIddict.sln @@ -17,6 +17,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.Assets", "src\Op EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.Models", "src\OpenIddict.Models\OpenIddict.Models.xproj", "{79AE02C3-2AB4-4495-BEDD-685A714EA51C}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.EF", "src\OpenIddict.EF\OpenIddict.EF.xproj", "{D2450929-ED0E-420D-B475-327924F9701C}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.Core", "src\OpenIddict.Core\OpenIddict.Core.xproj", "{E60CF8CA-6313-4359-BE43-AFCBB927EA30}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -43,6 +47,14 @@ Global {79AE02C3-2AB4-4495-BEDD-685A714EA51C}.Debug|Any CPU.Build.0 = Debug|Any CPU {79AE02C3-2AB4-4495-BEDD-685A714EA51C}.Release|Any CPU.ActiveCfg = Release|Any CPU {79AE02C3-2AB4-4495-BEDD-685A714EA51C}.Release|Any CPU.Build.0 = Release|Any CPU + {D2450929-ED0E-420D-B475-327924F9701C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2450929-ED0E-420D-B475-327924F9701C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2450929-ED0E-420D-B475-327924F9701C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2450929-ED0E-420D-B475-327924F9701C}.Release|Any CPU.Build.0 = Release|Any CPU + {E60CF8CA-6313-4359-BE43-AFCBB927EA30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E60CF8CA-6313-4359-BE43-AFCBB927EA30}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E60CF8CA-6313-4359-BE43-AFCBB927EA30}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E60CF8CA-6313-4359-BE43-AFCBB927EA30}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -53,5 +65,7 @@ Global {7CBEAFD2-E3D0-4424-9B78-E87AB52327A6} = {F47D1283-0EE9-4728-8026-58405C29B786} {86293E11-DD31-4D54-BCAD-8788B5C9972F} = {D544447C-D701-46BB-9A5B-C76C612A596B} {79AE02C3-2AB4-4495-BEDD-685A714EA51C} = {D544447C-D701-46BB-9A5B-C76C612A596B} + {D2450929-ED0E-420D-B475-327924F9701C} = {D544447C-D701-46BB-9A5B-C76C612A596B} + {E60CF8CA-6313-4359-BE43-AFCBB927EA30} = {D544447C-D701-46BB-9A5B-C76C612A596B} EndGlobalSection EndGlobal diff --git a/samples/Mvc.Server/Startup.cs b/samples/Mvc.Server/Startup.cs index d00e669d..11d8d895 100644 --- a/samples/Mvc.Server/Startup.cs +++ b/samples/Mvc.Server/Startup.cs @@ -34,7 +34,8 @@ namespace Mvc.Server { services.AddIdentity() .AddEntityFrameworkStores() - .AddDefaultTokenProviders(); + .AddDefaultTokenProviders() + .AddOpenIddict(); services.AddTransient(); services.AddTransient(); @@ -75,7 +76,7 @@ namespace Mvc.Server { // Note: OpenIddict must be added after // ASP.NET Identity and the external providers. - app.UseOpenIddict(options => { + app.UseOpenIddict(options => { options.AllowInsecureHttp = true; }); diff --git a/src/OpenIddict.Core/IOpenIddictStore.cs b/src/OpenIddict.Core/IOpenIddictStore.cs new file mode 100644 index 00000000..243763ac --- /dev/null +++ b/src/OpenIddict.Core/IOpenIddictStore.cs @@ -0,0 +1,14 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNet.Identity; + +namespace OpenIddict { + public interface IOpenIddictStore : IUserStore where TUser : class where TApplication : class { + Task FindApplicationByIdAsync(string identifier, CancellationToken cancellationToken); + Task FindApplicationByLogoutRedirectUri(string url, CancellationToken cancellationToken); + Task GetApplicationTypeAsync(TApplication application, CancellationToken cancellationToken); + Task GetDisplayNameAsync(TApplication application, CancellationToken cancellationToken); + Task GetRedirectUriAsync(TApplication application, CancellationToken cancellationToken); + Task ValidateSecretAsync(TApplication application, string secret, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/OpenIddict.Core/OpenIddict.Core.xproj b/src/OpenIddict.Core/OpenIddict.Core.xproj new file mode 100644 index 00000000..330b0364 --- /dev/null +++ b/src/OpenIddict.Core/OpenIddict.Core.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + e60cf8ca-6313-4359-be43-afcbb927ea30 + OpenIddict.Core + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + diff --git a/src/OpenIddict.Core/OpenIddictBuilder.cs b/src/OpenIddict.Core/OpenIddictBuilder.cs new file mode 100644 index 00000000..0db22be7 --- /dev/null +++ b/src/OpenIddict.Core/OpenIddictBuilder.cs @@ -0,0 +1,36 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using Microsoft.Extensions.DependencyInjection; + +namespace OpenIddict { + public class OpenIddictBuilder { + internal OpenIddictBuilder(IServiceCollection services) { + Services = services; + } + + /// + /// Gets or sets the type corresponding to the Application entity. + /// + public Type ApplicationType { get; set; } + + /// + /// Gets or sets the type corresponding to the Role entity. + /// + public Type RoleType { get; set; } + + /// + /// Gets or sets the type corresponding to the User entity. + /// + public Type UserType { get; set; } + + /// + /// Gets the services used by OpenIddict. + /// + public IServiceCollection Services { get; } + } +} \ No newline at end of file diff --git a/src/OpenIddict.Core/OpenIddictConstants.cs b/src/OpenIddict.Core/OpenIddictConstants.cs new file mode 100644 index 00000000..eed6c08d --- /dev/null +++ b/src/OpenIddict.Core/OpenIddictConstants.cs @@ -0,0 +1,8 @@ +namespace OpenIddict { + public static class OpenIddictConstants { + public static class ApplicationTypes { + public const string Confidential = "confidential"; + public const string Public = "public"; + } + } +} diff --git a/src/OpenIddict/OpenIddictController.cs b/src/OpenIddict.Core/OpenIddictController.cs similarity index 84% rename from src/OpenIddict/OpenIddictController.cs rename to src/OpenIddict.Core/OpenIddictController.cs index c0d1d696..a3271bbc 100644 --- a/src/OpenIddict/OpenIddictController.cs +++ b/src/OpenIddict.Core/OpenIddictController.cs @@ -13,30 +13,21 @@ using AspNet.Security.OpenIdConnect.Extensions; using AspNet.Security.OpenIdConnect.Server; using Microsoft.AspNet.Authorization; using Microsoft.AspNet.Http.Authentication; -using Microsoft.AspNet.Identity; -using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.AspNet.Mvc; -using Microsoft.Data.Entity; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Internal; using Microsoft.IdentityModel.Protocols.OpenIdConnect; namespace OpenIddict { - // Note: this controller is generic and doesn't need to - // be marked as internal to prevent MVC from discovering it. - public class OpenIddictController : Controller - where TContext : OpenIddictContext - where TUser : IdentityUser - where TRole : IdentityRole - where TKey : IEquatable { - public OpenIddictController([NotNull] TContext context) { - Context = context; + // Note: this controller is generic and doesn't need to be marked as internal to prevent MVC from discovering it. + public class OpenIddictController : Controller where TUser : class where TApplication : class { + public OpenIddictController([NotNull] OpenIddictManager manager) { + Manager = manager; } /// - /// Gets the OpenIddict context. + /// Gets the OpenIddict manager used by the controller. /// - protected TContext Context { get; } + protected virtual OpenIddictManager Manager { get; } /// /// Gets the OpenIddict options used by the server. @@ -83,9 +74,7 @@ namespace OpenIddict { // Note: AspNet.Security.OpenIdConnect.Server automatically ensures an application // corresponds to the client_id specified in the authorization request using // IOpenIdConnectServerProvider.ValidateClientRedirectUri (see OpenIddictProvider.cs). - var application = await (from entity in Context.Applications - where entity.ApplicationID == request.ClientId - select entity).SingleOrDefaultAsync(HttpContext.RequestAborted); + var application = await Manager.FindApplicationByIdAsync(request.ClientId); // In theory, this null check is thus not strictly necessary. That said, a race condition // and a null reference exception could appear here if you manually removed the application @@ -97,11 +86,10 @@ namespace OpenIddict { }); } - return View("Authorize", Tuple.Create(request, application)); + return View("Authorize", Tuple.Create(request, await Manager.GetDisplayNameAsync(application))); } - [Authorize] - [HttpPost, ValidateAntiForgeryToken] + [Authorize, HttpPost, ValidateAntiForgeryToken] public async Task Accept() { // Extract the authorization request from the cache, // the query string or the request form. @@ -113,11 +101,8 @@ namespace OpenIddict { }); } - // Resolve the user manager from the services provider. - var manager = HttpContext.RequestServices.GetRequiredService>(); - // Retrieve the user data using the unique identifier. - var user = await manager.FindByIdAsync(User.GetUserId()); + var user = await Manager.FindByIdAsync(User.GetUserId()); if (user == null) { return View("Error", new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.ServerError, @@ -128,24 +113,22 @@ namespace OpenIddict { // Create a new ClaimsIdentity containing the claims that // will be used to create an id_token, a token or a code. var identity = new ClaimsIdentity(Options.AuthenticationScheme); - identity.AddClaim(ClaimTypes.NameIdentifier, user.Id.ToString()); + identity.AddClaim(ClaimTypes.NameIdentifier, await Manager.GetUserIdAsync(user)); // Only add the name claim if the "profile" scope was present in the token request. if (request.GetScopes().Contains("profile", StringComparer.OrdinalIgnoreCase)) { - identity.AddClaim(ClaimTypes.Name, user.UserName, destination: "id_token token"); + identity.AddClaim(ClaimTypes.Name, await Manager.GetUserNameAsync(user), destination: "id_token token"); } // Only add the email address if the "email" scope was present in the token request. if (request.GetScopes().Contains("email", StringComparer.OrdinalIgnoreCase)) { - identity.AddClaim(ClaimTypes.Email, user.Email, destination: "id_token token"); + identity.AddClaim(ClaimTypes.Email, await Manager.GetEmailAsync(user), destination: "id_token token"); } // Note: AspNet.Security.OpenIdConnect.Server automatically ensures an application // corresponds to the client_id specified in the authorization request using // IOpenIdConnectServerProvider.ValidateClientRedirectUri (see OpenIddictProvider.cs). - var application = await (from entity in Context.Applications - where entity.ApplicationID == request.ClientId - select entity).SingleOrDefaultAsync(HttpContext.RequestAborted); + var application = await Manager.FindApplicationByIdAsync(request.ClientId); // In theory, this null check is thus not strictly necessary. That said, a race condition // and a null reference exception could appear here if you manually removed the application @@ -161,8 +144,8 @@ namespace OpenIddict { // Note: setting identity.Actor is not mandatory but can be useful to access // the whole delegation chain from the resource server (see ResourceController.cs). identity.Actor = new ClaimsIdentity(Options.AuthenticationScheme); - identity.Actor.AddClaim(ClaimTypes.NameIdentifier, application.ApplicationID); - identity.Actor.AddClaim(ClaimTypes.Name, application.DisplayName, destination: "id_token token"); + identity.Actor.AddClaim(ClaimTypes.NameIdentifier, request.ClientId); + identity.Actor.AddClaim(ClaimTypes.Name, await Manager.GetDisplayNameAsync(application), destination: "id_token token"); // This call will instruct AspNet.Security.OpenIdConnect.Server to serialize // the specified identity to build appropriate tokens (id_token and token). @@ -174,8 +157,7 @@ namespace OpenIddict { return new EmptyResult(); } - [Authorize] - [HttpPost, ValidateAntiForgeryToken] + [Authorize, HttpPost, ValidateAntiForgeryToken] public IActionResult Deny() { // Extract the authorization request from the cache, // the query string or the request form. diff --git a/src/OpenIddict/OpenIddictDefaults.cs b/src/OpenIddict.Core/OpenIddictDefaults.cs similarity index 100% rename from src/OpenIddict/OpenIddictDefaults.cs rename to src/OpenIddict.Core/OpenIddictDefaults.cs diff --git a/src/OpenIddict.Core/OpenIddictExtensions.cs b/src/OpenIddict.Core/OpenIddictExtensions.cs new file mode 100644 index 00000000..0117a44e --- /dev/null +++ b/src/OpenIddict.Core/OpenIddictExtensions.cs @@ -0,0 +1,201 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using System.Diagnostics; +using System.Reflection; +using AspNet.Security.OpenIdConnect.Server; +using Microsoft.AspNet.FileProviders; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Identity; +using Microsoft.AspNet.StaticFiles; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Internal; +using OpenIddict; + +#if DNX451 +using NWebsec.Owin; +#endif + +namespace Microsoft.AspNet.Builder { + public static class OpenIddictExtensions { + public static OpenIddictBuilder AddOpenIddictCore( + [NotNull] this IdentityBuilder builder) where TApplication : class { + builder.Services.AddSingleton( + typeof(OpenIdConnectServerProvider), + typeof(OpenIddictProvider<,>).MakeGenericType( + builder.UserType, typeof(TApplication))); + + builder.Services.AddScoped( + typeof(OpenIddictManager<,>).MakeGenericType( + builder.UserType, typeof(TApplication))); + + var services = new OpenIddictBuilder(builder.Services) { + ApplicationType = typeof(TApplication), + RoleType = builder.RoleType, + UserType = builder.UserType + }; + + builder.Services.AddInstance(services); + + return services; + } + + public static IApplicationBuilder UseOpenIddict( + [NotNull] this IApplicationBuilder app, + [NotNull] Action configuration) { + var instance = new OpenIddictOptions(); + + // Turn ApplicationCanDisplayErrors on to ensure ASP.NET MVC 6 + // handles errored requests and returns an appropriate error page. + instance.ApplicationCanDisplayErrors = true; + + // Call the configuration delegate defined by the user. + configuration(instance); + + var types = app.ApplicationServices.GetRequiredService(); + + // Run OpenIddict in an isolated environment. + return app.Isolate(builder => { + // Add the options to the ASP.NET context + // before executing the rest of the pipeline. + builder.Use(next => context => { + context.Items[typeof(OpenIddictOptions)] = instance; + + return next(context); + }); + +#if DNX451 + builder.UseKatana(owin => { + // Insert a new middleware responsible of setting the Content-Security-Policy header. + // See https://nwebsec.codeplex.com/wikipage?title=Configuring%20Content%20Security%20Policy&referringTitle=NWebsec + owin.UseCsp(options => options.DefaultSources(directive => directive.Self()) + .ImageSources(directive => directive.Self().CustomSources("*")) + .ScriptSources(directive => directive.UnsafeInline()) + .StyleSources(directive => directive.Self().UnsafeInline())); + + // Insert a new middleware responsible of setting the X-Content-Type-Options header. + // See https://nwebsec.codeplex.com/wikipage?title=Configuring%20security%20headers&referringTitle=NWebsec + owin.UseXContentTypeOptions(); + + // Insert a new middleware responsible of setting the X-Frame-Options header. + // See https://nwebsec.codeplex.com/wikipage?title=Configuring%20security%20headers&referringTitle=NWebsec + owin.UseXfo(options => options.Deny()); + + // Insert a new middleware responsible of setting the X-Xss-Protection header. + // See https://nwebsec.codeplex.com/wikipage?title=Configuring%20security%20headers&referringTitle=NWebsec + owin.UseXXssProtection(options => options.EnabledWithBlockMode()); + }); +#endif + if (!instance.UseCustomViews) { + builder.UseStaticFiles(new StaticFileOptions { + FileProvider = new EmbeddedFileProvider( + assembly: Assembly.Load(new AssemblyName("OpenIddict.Assets")), + baseNamespace: "OpenIddict.Assets") + }); + } + + builder.UseCors(options => { + options.AllowAnyHeader(); + options.AllowAnyMethod(); + options.AllowAnyOrigin(); + options.AllowCredentials(); + }); + + // Add OpenIdConnectServerMiddleware to the ASP.NET 5 pipeline. + builder.UseOpenIdConnectServer(options => { + // Resolve the OpenIddict provider from the global services container. + options.Provider = app.ApplicationServices.GetRequiredService(); + + // Copy the OpenIddict options to the ASOS configuration. + options.Options.AuthenticationScheme = instance.AuthenticationScheme; + + options.Options.Issuer = instance.Issuer; + + options.Options.AuthorizationEndpointPath = instance.AuthorizationEndpointPath; + options.Options.LogoutEndpointPath = instance.LogoutEndpointPath; + + options.Options.AccessTokenLifetime = instance.AccessTokenLifetime; + options.Options.AuthorizationCodeLifetime = instance.AuthorizationCodeLifetime; + options.Options.IdentityTokenLifetime = instance.IdentityTokenLifetime; + options.Options.RefreshTokenLifetime = instance.RefreshTokenLifetime; + + options.Options.ApplicationCanDisplayErrors = instance.ApplicationCanDisplayErrors; + options.Options.AllowInsecureHttp = instance.AllowInsecureHttp; + }); + + // Register ASP.NET MVC 6 and the actions + // associated to the OpenIddict controller. + builder.UseMvc(routes => { + // Register the actions corresponding to the authorization endpoint. + if (instance.AuthorizationEndpointPath.HasValue) { + routes.MapRoute("{D97891B4}", instance.AuthorizationEndpointPath.Value.Substring(1), new { + controller = typeof(OpenIddictController<,>).Name, + action = nameof(OpenIddictController.Authorize) + }); + + routes.MapRoute("{7148DB83}", instance.AuthorizationEndpointPath.Value.Substring(1) + "/accept", new { + controller = typeof(OpenIddictController<,>).Name, + action = nameof(OpenIddictController.Accept) + }); + + routes.MapRoute("{23438BCC}", instance.AuthorizationEndpointPath.Value.Substring(1) + "/deny", new { + controller = typeof(OpenIddictController<,>).Name, + action = nameof(OpenIddictController.Deny) + }); + } + + // Register the action corresponding to the logout endpoint. + if (instance.LogoutEndpointPath.HasValue) { + routes.MapRoute("{C7DB102A}", instance.LogoutEndpointPath.Value.Substring(1), new { + controller = typeof(OpenIddictController<,>).Name, + action = nameof(OpenIddictController.Logout) + }); + } + }); + }, services => { + services.AddAuthentication(); + services.AddCaching(); + + services.AddMvc() + // Register the OpenIddict controller. + .AddControllersAsServices(new[] { + typeof(OpenIddictController<,>).MakeGenericType(types.UserType, types.ApplicationType) + }) + + // Update the Razor options to use an embedded provider + // extracting its views from the current assembly. + .AddRazorOptions(options => { + if (!instance.UseCustomViews) { + options.FileProvider = new EmbeddedFileProvider( + assembly: typeof(OpenIddictOptions).GetTypeInfo().Assembly, + baseNamespace: "OpenIddict.Core"); + } + }); + + // Register the sign-in manager in the isolated container. + services.AddScoped(typeof(SignInManager<>).MakeGenericType(types.UserType), provider => { + var accessor = provider.GetRequiredService(); + var container = (IServiceProvider) accessor.HttpContext.Items[typeof(IServiceProvider)]; + Debug.Assert(container != null); + + // Resolve the sign-in manager from the parent container. + return container.GetRequiredService(typeof(SignInManager<>).MakeGenericType(types.UserType)); + }); + + // Register the user manager in the isolated container. + services.AddScoped(typeof(OpenIddictManager<,>).MakeGenericType(types.UserType, types.ApplicationType), provider => { + var accessor = provider.GetRequiredService(); + var container = (IServiceProvider) accessor.HttpContext.Items[typeof(IServiceProvider)]; + Debug.Assert(container != null); + + // Resolve the user manager from the parent container. + return container.GetRequiredService(typeof(OpenIddictManager<,>).MakeGenericType(types.UserType, types.ApplicationType)); + }); + }); + } + } +} \ No newline at end of file diff --git a/src/OpenIddict.Core/OpenIddictManager.cs b/src/OpenIddict.Core/OpenIddictManager.cs new file mode 100644 index 00000000..5017985e --- /dev/null +++ b/src/OpenIddict.Core/OpenIddictManager.cs @@ -0,0 +1,78 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Identity; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Internal; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.OptionsModel; + +namespace OpenIddict { + public class OpenIddictManager : UserManager where TUser : class where TApplication : class { + public OpenIddictManager([NotNull] IServiceProvider services) + : base(services: services, + store: services.GetService>(), + optionsAccessor: services.GetService>(), + passwordHasher: services.GetService>(), + userValidators: services.GetServices>(), + passwordValidators: services.GetServices>(), + keyNormalizer: services.GetService(), + errors: services.GetService(), + logger: services.GetService>>(), + contextAccessor: services.GetService()) { + Context = services.GetRequiredService().HttpContext; + } + + /// + /// Gets the HTTP context associated with the current manager. + /// + public virtual HttpContext Context { get; } + + /// + /// Gets the store associated with the current manager. + /// + public virtual new IOpenIddictStore Store { + get { return base.Store as IOpenIddictStore; } + } + + public virtual Task FindApplicationByIdAsync(string identifier) { + return Store.FindApplicationByIdAsync(identifier, Context.RequestAborted); + } + + public virtual Task FindApplicationByLogoutRedirectUri(string url) { + return Store.FindApplicationByLogoutRedirectUri(url, Context.RequestAborted); + } + + public virtual Task GetApplicationTypeAsync(TApplication application) { + if (application == null) { + throw new ArgumentNullException(nameof(application)); + } + + return Store.GetApplicationTypeAsync(application, Context.RequestAborted); + } + + public virtual Task GetDisplayNameAsync(TApplication application) { + if (application == null) { + throw new ArgumentNullException(nameof(application)); + } + + return Store.GetDisplayNameAsync(application, Context.RequestAborted); + } + + public virtual Task GetRedirectUriAsync(TApplication application) { + if (application == null) { + throw new ArgumentNullException(nameof(application)); + } + + return Store.GetRedirectUriAsync(application, Context.RequestAborted); + } + + public virtual Task ValidateSecretAsync(TApplication application, string secret) { + if (application == null) { + throw new ArgumentNullException(nameof(application)); + } + + return Store.ValidateSecretAsync(application, secret, Context.RequestAborted); + } + } +} \ No newline at end of file diff --git a/src/OpenIddict/OpenIddictOptions.cs b/src/OpenIddict.Core/OpenIddictOptions.cs similarity index 100% rename from src/OpenIddict/OpenIddictOptions.cs rename to src/OpenIddict.Core/OpenIddictOptions.cs diff --git a/src/OpenIddict/OpenIddictProvider.cs b/src/OpenIddict.Core/OpenIddictProvider.cs similarity index 83% rename from src/OpenIddict/OpenIddictProvider.cs rename to src/OpenIddict.Core/OpenIddictProvider.cs index bda9325c..e4a501b5 100644 --- a/src/OpenIddict/OpenIddictProvider.cs +++ b/src/OpenIddict.Core/OpenIddictProvider.cs @@ -12,19 +12,11 @@ using System.Security.Claims; using System.Threading.Tasks; using AspNet.Security.OpenIdConnect.Extensions; using AspNet.Security.OpenIdConnect.Server; -using Microsoft.AspNet.Identity; -using Microsoft.AspNet.Identity.EntityFramework; -using Microsoft.Data.Entity; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Internal; -using OpenIddict.Models; namespace OpenIddict { - public class OpenIddictProvider : OpenIdConnectServerProvider - where TContext : OpenIddictContext - where TUser : IdentityUser - where TRole : IdentityRole - where TKey : IEquatable { + public class OpenIddictProvider : OpenIdConnectServerProvider where TUser : class where TApplication : class { public override Task MatchEndpoint([NotNull] MatchEndpointContext context) { // Note: by default, OpenIdConnectServerHandler only handles authorization requests made to AuthorizationEndpointPath. // This context handler uses a more relaxed policy that allows extracting authorization requests received at @@ -48,13 +40,10 @@ namespace OpenIddict { return; } - var database = context.HttpContext.RequestServices.GetRequiredService(); - // Retrieve the application details corresponding to the requested client_id. - var application = await (from entity in database.Applications - where entity.ApplicationID == context.ClientId - select entity).SingleOrDefaultAsync(context.HttpContext.RequestAborted); + var manager = context.HttpContext.RequestServices.GetRequiredService>(); + var application = await manager.FindApplicationByIdAsync(context.ClientId); if (application == null) { context.Rejected( error: OpenIdConnectConstants.Errors.InvalidClient, @@ -63,7 +52,7 @@ namespace OpenIddict { return; } - if (!string.Equals(context.RedirectUri, application.RedirectUri, StringComparison.Ordinal)) { + if (!string.Equals(context.RedirectUri, await manager.GetRedirectUriAsync(application), StringComparison.Ordinal)) { context.Rejected( error: OpenIdConnectConstants.Errors.InvalidClient, description: "Invalid redirect_uri"); @@ -75,9 +64,10 @@ namespace OpenIddict { } public override async Task ValidateClientLogoutRedirectUri([NotNull] ValidateClientLogoutRedirectUriContext context) { - var database = context.HttpContext.RequestServices.GetRequiredService(); + var manager = context.HttpContext.RequestServices.GetRequiredService>(); - if (!await database.Applications.AnyAsync(application => application.LogoutRedirectUri == context.PostLogoutRedirectUri)) { + var application = await manager.FindApplicationByLogoutRedirectUri(context.PostLogoutRedirectUri); + if (application == null) { context.Rejected( error: OpenIdConnectConstants.Errors.InvalidClient, description: "Invalid post_logout_redirect_uri"); @@ -111,13 +101,10 @@ namespace OpenIddict { return; } - var database = context.HttpContext.RequestServices.GetRequiredService(); - // Retrieve the application details corresponding to the requested client_id. - var application = await (from entity in database.Applications - where entity.ApplicationID == context.ClientId - select entity).SingleOrDefaultAsync(context.HttpContext.RequestAborted); + var manager = context.HttpContext.RequestServices.GetRequiredService>(); + var application = await manager.FindApplicationByIdAsync(context.ClientId); if (application == null) { context.Rejected( error: OpenIdConnectConstants.Errors.InvalidClient, @@ -126,8 +113,10 @@ namespace OpenIddict { return; } - // Reject tokens requests containing a client_secret if the client application is not confidential. - if (application.Type == ApplicationType.Public && !string.IsNullOrEmpty(context.ClientSecret)) { + // Reject tokens requests containing a client_secret + // if the client application is not confidential. + var type = await manager.GetApplicationTypeAsync(application); + if (type == OpenIddictConstants.ApplicationTypes.Public && !string.IsNullOrEmpty(context.ClientSecret)) { context.Rejected( error: OpenIdConnectConstants.Errors.InvalidRequest, description: "Public clients are not allowed to send a client_secret"); @@ -136,8 +125,8 @@ namespace OpenIddict { } // Confidential applications MUST authenticate. - else if (application.Type == ApplicationType.Confidential && - !string.Equals(context.ClientSecret, application.Secret, StringComparison.Ordinal)) { + else if (type == OpenIddictConstants.ApplicationTypes.Confidential && + !await manager.ValidateSecretAsync(application, context.ClientSecret)) { context.Rejected( error: OpenIdConnectConstants.Errors.InvalidClient, description: "Invalid credentials: ensure that you specified a correct client_secret"); @@ -173,7 +162,7 @@ namespace OpenIddict { return; } - var manager = context.HttpContext.RequestServices.GetRequiredService>(); + var manager = context.HttpContext.RequestServices.GetRequiredService>(); // Ensure the user still exists. var user = await manager.FindByIdAsync(principal.GetUserId()); @@ -208,7 +197,7 @@ namespace OpenIddict { return; } - var manager = context.HttpContext.RequestServices.GetRequiredService>(); + var manager = context.HttpContext.RequestServices.GetRequiredService>(); // Note: principal is guaranteed to be non-null since ValidateAuthorizationRequest // rejects prompt=none requests missing or having an invalid id_token_hint. @@ -225,16 +214,16 @@ namespace OpenIddict { } var identity = new ClaimsIdentity(context.Options.AuthenticationScheme); - identity.AddClaim(ClaimTypes.NameIdentifier, user.Id.ToString()); + identity.AddClaim(ClaimTypes.NameIdentifier, await manager.GetUserIdAsync(user)); // Only add the name claim if the "profile" scope was present in the token request. if (context.Request.GetScopes().Contains("profile", StringComparer.OrdinalIgnoreCase)) { - identity.AddClaim(ClaimTypes.Name, user.UserName, destination: "id_token token"); + identity.AddClaim(ClaimTypes.Name, await manager.GetUserNameAsync(user), destination: "id_token token"); } // Only add the email address if the "email" scope was present in the token request. if (context.Request.GetScopes().Contains("email", StringComparer.OrdinalIgnoreCase)) { - identity.AddClaim(ClaimTypes.Email, user.Email, destination: "id_token token"); + identity.AddClaim(ClaimTypes.Email, await manager.GetEmailAsync(user), destination: "id_token token"); } // Call SignInAsync to create and return a new OpenID Connect response containing the serialized code/tokens. @@ -246,7 +235,7 @@ namespace OpenIddict { } public override async Task GrantResourceOwnerCredentials([NotNull] GrantResourceOwnerCredentialsContext context) { - var manager = context.HttpContext.RequestServices.GetRequiredService>(); + var manager = context.HttpContext.RequestServices.GetRequiredService>(); var user = await manager.FindByNameAsync(context.UserName); if (user == null) { @@ -291,18 +280,18 @@ namespace OpenIddict { } var identity = new ClaimsIdentity(context.Options.AuthenticationScheme); - identity.AddClaim(ClaimTypes.NameIdentifier, user.Id.ToString()); + identity.AddClaim(ClaimTypes.NameIdentifier, await manager.GetUserIdAsync(user)); // Only add the name claim if the "profile" scope was present in the token request. if (context.Request.GetScopes().Contains("profile", StringComparer.OrdinalIgnoreCase)) { - identity.AddClaim(ClaimTypes.Name, user.UserName, destination: "id_token token"); + identity.AddClaim(ClaimTypes.Name, await manager.GetUserNameAsync(user), destination: "id_token token"); } // Only add the email address if the "email" scope was present in the token request. if (context.Request.GetScopes().Contains("email", StringComparer.OrdinalIgnoreCase)) { - identity.AddClaim(ClaimTypes.Email, user.Email, destination: "id_token token"); + identity.AddClaim(ClaimTypes.Email, await manager.GetEmailAsync(user), destination: "id_token token"); } - + context.Validated(new ClaimsPrincipal(identity)); } } diff --git a/src/OpenIddict.Core/Properties/AssemblyInfo.cs b/src/OpenIddict.Core/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..65f5614e --- /dev/null +++ b/src/OpenIddict.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,23 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenIddict.Core")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("OpenIddict.Core")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e60cf8ca-6313-4359-be43-afcbb927ea30")] diff --git a/src/OpenIddict/Views/Shared/Authorize.cshtml b/src/OpenIddict.Core/Views/Shared/Authorize.cshtml similarity index 66% rename from src/OpenIddict/Views/Shared/Authorize.cshtml rename to src/OpenIddict.Core/Views/Shared/Authorize.cshtml index ccdfd7c1..37454929 100644 --- a/src/OpenIddict/Views/Shared/Authorize.cshtml +++ b/src/OpenIddict.Core/Views/Shared/Authorize.cshtml @@ -1,14 +1,13 @@ @using AspNet.Security.OpenIdConnect.Extensions @using Microsoft.IdentityModel.Protocols.OpenIdConnect -@using OpenIddict.Models -@model Tuple +@model Tuple

Authorization

-

Do you wanna grant @Model.Item2.DisplayName an access to your resources? (scopes requested: @Model.Item1.Scope)

-

@Model.Item2.DisplayName will be able to access the following endpoints: @string.Join(" ; ", Model.Item1.GetResources())

+

Do you wanna grant @Model.Item2 an access to your resources? (scopes requested: @Model.Item1.Scope)

+

@Model.Item2 will be able to access the following endpoints: @string.Join(" ; ", Model.Item1.GetResources())

@Html.AntiForgeryToken() diff --git a/src/OpenIddict/Views/Shared/Error.cshtml b/src/OpenIddict.Core/Views/Shared/Error.cshtml similarity index 100% rename from src/OpenIddict/Views/Shared/Error.cshtml rename to src/OpenIddict.Core/Views/Shared/Error.cshtml diff --git a/src/OpenIddict/Views/Shared/Logout.cshtml b/src/OpenIddict.Core/Views/Shared/Logout.cshtml similarity index 100% rename from src/OpenIddict/Views/Shared/Logout.cshtml rename to src/OpenIddict.Core/Views/Shared/Logout.cshtml diff --git a/src/OpenIddict/Views/Shared/SignIn.cshtml b/src/OpenIddict.Core/Views/Shared/SignIn.cshtml similarity index 100% rename from src/OpenIddict/Views/Shared/SignIn.cshtml rename to src/OpenIddict.Core/Views/Shared/SignIn.cshtml diff --git a/src/OpenIddict/Views/Shared/_Layout.cshtml b/src/OpenIddict.Core/Views/Shared/_Layout.cshtml similarity index 100% rename from src/OpenIddict/Views/Shared/_Layout.cshtml rename to src/OpenIddict.Core/Views/Shared/_Layout.cshtml diff --git a/src/OpenIddict/Views/_ViewStart.cshtml b/src/OpenIddict.Core/Views/_ViewStart.cshtml similarity index 100% rename from src/OpenIddict/Views/_ViewStart.cshtml rename to src/OpenIddict.Core/Views/_ViewStart.cshtml diff --git a/src/OpenIddict.Core/project.json b/src/OpenIddict.Core/project.json new file mode 100644 index 00000000..90b0bb9e --- /dev/null +++ b/src/OpenIddict.Core/project.json @@ -0,0 +1,41 @@ +{ + "version": "1.0.0-alpha1-*", + + "resource": "Views/**", + + "dependencies": { + "Microsoft.AspNet.Cors": "6.0.0-*", + "Microsoft.AspNet.FileProviders.Embedded": "1.0.0-*", + "Microsoft.AspNet.Identity": "3.0.0-*", + "Microsoft.AspNet.Mvc": "6.0.0-*", + "Microsoft.AspNet.StaticFiles": "1.0.0-*", + + "Microsoft.Extensions.Configuration.Json": "1.0.0-*", + + "Microsoft.Extensions.NotNullAttribute.Sources": { + "type": "build", + "version": "1.0.0-*" + }, + + "AspNet.Hosting.Extensions": "1.0.0-*", + "AspNet.Security.OpenIdConnect.Server": "1.0.0-*", + + "OpenIddict.Assets": "1.0.0-*" + }, + + "frameworks": { + "dnx451": { + "dependencies": { + "AspNet.Hosting.Katana.Extensions": "1.0.0-*", + "NWebsec.Owin": "1.0.0" + } + }, + + "dnxcore50": { + "dependencies": { + "System.Linq": "4.0.1-*", + "System.Runtime.Serialization.Primitives": "4.0.11-*" + } + } + } +} \ No newline at end of file diff --git a/src/OpenIddict.EF/OpenIddict.EF.xproj b/src/OpenIddict.EF/OpenIddict.EF.xproj new file mode 100644 index 00000000..1f041da1 --- /dev/null +++ b/src/OpenIddict.EF/OpenIddict.EF.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + d2450929-ed0e-420d-b475-327924f9701c + OpenIddict.EF + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + diff --git a/src/OpenIddict/OpenIddictContext.cs b/src/OpenIddict.EF/OpenIddictContext.cs similarity index 61% rename from src/OpenIddict/OpenIddictContext.cs rename to src/OpenIddict.EF/OpenIddictContext.cs index ec89a944..776e77f8 100644 --- a/src/OpenIddict/OpenIddictContext.cs +++ b/src/OpenIddict.EF/OpenIddictContext.cs @@ -10,12 +10,15 @@ using Microsoft.Data.Entity; using OpenIddict.Models; namespace OpenIddict { - public class OpenIddictContext : IdentityDbContext + public class OpenIddictContext : IdentityDbContext where TUser : IdentityUser + where TApplication : Application where TRole : IdentityRole where TKey : IEquatable { - public DbSet Applications { get; set; } + public DbSet Applications { get; set; } } - public class OpenIddictContext : OpenIddictContext where TUser : IdentityUser { } + public class OpenIddictContext : OpenIddictContext where TUser : IdentityUser { } + + public class OpenIddictContext : OpenIddictContext { } } \ No newline at end of file diff --git a/src/OpenIddict.EF/OpenIddictExtensions.cs b/src/OpenIddict.EF/OpenIddictExtensions.cs new file mode 100644 index 00000000..9d970547 --- /dev/null +++ b/src/OpenIddict.EF/OpenIddictExtensions.cs @@ -0,0 +1,74 @@ +/* + * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * See https://github.com/openiddict/core for more information concerning + * the license and the contributors participating to this project. + */ + +using System; +using System.Linq; +using System.Reflection; +using Microsoft.AspNet.Identity; +using Microsoft.AspNet.Identity.EntityFramework; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Internal; +using OpenIddict; + +namespace Microsoft.AspNet.Builder { + public static class OpenIddictExtensions { + public static OpenIddictBuilder AddEntityFrameworkStore([NotNull] this OpenIddictBuilder builder) { + // Resolve the key type from the user type definition. + var keyType = ResolveKeyType(builder); + + builder.Services.AddScoped( + typeof(IOpenIddictStore<,>).MakeGenericType(builder.UserType, builder.ApplicationType), + typeof(OpenIddictStore<,,,>).MakeGenericType(builder.UserType, builder.ApplicationType, builder.RoleType, keyType)); + + var type = typeof(OpenIddictContext<,,,>).MakeGenericType(new[] { + /* TUser: */ builder.UserType, + /* TApplication: */ builder.ApplicationType, + /* TRole: */ builder.RoleType, + /* TKey: */ keyType + }); + + builder.Services.AddScoped(type, provider => { + // Resolve the user store from the parent container and extract the associated context. + dynamic store = provider.GetRequiredService(typeof(IUserStore<>).MakeGenericType(builder.UserType)); + + dynamic context = store?.Context; + if (!type.GetTypeInfo().IsAssignableFrom(context?.GetType())) { + throw new InvalidOperationException( + "Only EntityFramework contexts derived from " + + "OpenIddictContext can be used with OpenIddict."); + } + + return context; + }); + + return builder; + } + + private static Type ResolveKeyType([NotNull] OpenIddictBuilder builder) { + TypeInfo type; + for (type = builder.UserType.GetTypeInfo(); type != null; type = type.BaseType?.GetTypeInfo()) { + if (!type.IsGenericType) { + continue; + } + + var definition = type.GetGenericTypeDefinition(); + if (definition == null) { + continue; + } + + if (definition != typeof(IdentityUser<>)) { + continue; + } + + return type.AsType().GetGenericArguments().Single(); + } + + throw new InvalidOperationException( + "The type of the key identifier used by the user " + + $"entity '{builder.UserType}' cannot be automatically inferred."); + } + } +} \ No newline at end of file diff --git a/src/OpenIddict.EF/OpenIddictStore.cs b/src/OpenIddict.EF/OpenIddictStore.cs new file mode 100644 index 00000000..7d785bdd --- /dev/null +++ b/src/OpenIddict.EF/OpenIddictStore.cs @@ -0,0 +1,67 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNet.Identity.EntityFramework; +using Microsoft.Data.Entity; +using OpenIddict.Models; + +namespace OpenIddict { + public class OpenIddictStore : UserStore, TKey>, IOpenIddictStore + where TUser : IdentityUser + where TApplication : Application + where TRole : IdentityRole + where TKey : IEquatable { + public OpenIddictStore(OpenIddictContext context) + : base(context) { + } + + public virtual Task FindApplicationByIdAsync(string identifier, CancellationToken cancellationToken) { + return Context.Applications.SingleOrDefaultAsync(application => application.ApplicationID == identifier, cancellationToken); + } + + public virtual Task FindApplicationByLogoutRedirectUri(string url, CancellationToken cancellationToken) { + return Context.Applications.SingleOrDefaultAsync(application => application.LogoutRedirectUri == url, cancellationToken); + } + + public virtual Task GetApplicationTypeAsync(TApplication application, CancellationToken cancellationToken) { + if (application == null) { + throw new ArgumentNullException(nameof(application)); + } + + switch (application.Type) { + case ApplicationType.Confidential: + return Task.FromResult(OpenIddictConstants.ApplicationTypes.Confidential); + + case ApplicationType.Public: + return Task.FromResult(OpenIddictConstants.ApplicationTypes.Public); + + default: + throw new InvalidOperationException($"Unsupported application type ('{application.Type.ToString()}')."); + } + } + + public virtual Task GetDisplayNameAsync(TApplication application, CancellationToken cancellationToken) { + if (application == null) { + throw new ArgumentNullException(nameof(application)); + } + + return Task.FromResult(application.DisplayName); + } + + public virtual Task GetRedirectUriAsync(TApplication application, CancellationToken cancellationToken) { + if (application == null) { + throw new ArgumentNullException(nameof(application)); + } + + return Task.FromResult(application.RedirectUri); + } + + public virtual Task ValidateSecretAsync(TApplication application, string secret, CancellationToken cancellationToken) { + if (application == null) { + throw new ArgumentNullException(nameof(application)); + } + + return Task.FromResult(string.Equals(application.Secret, secret, StringComparison.Ordinal)); + } + } +} \ No newline at end of file diff --git a/src/OpenIddict.EF/project.json b/src/OpenIddict.EF/project.json new file mode 100644 index 00000000..b045bb6f --- /dev/null +++ b/src/OpenIddict.EF/project.json @@ -0,0 +1,20 @@ +{ + "version": "1.0.0-alpha1-*", + + "dependencies": { + "Microsoft.AspNet.Identity.EntityFramework": "3.0.0-*", + + "Microsoft.Extensions.NotNullAttribute.Sources": { + "type": "build", + "version": "1.0.0-*" + }, + + "OpenIddict.Core": "1.0.0-*", + "OpenIddict.Models": "1.0.0-*" + }, + + "frameworks": { + "dnx451": { }, + "dnxcore50": { } + } +} \ No newline at end of file diff --git a/src/OpenIddict/OpenIddictExtensions.cs b/src/OpenIddict/OpenIddictExtensions.cs index 87d972f2..7ac05a37 100644 --- a/src/OpenIddict/OpenIddictExtensions.cs +++ b/src/OpenIddict/OpenIddictExtensions.cs @@ -4,232 +4,22 @@ * the license and the contributors participating to this project. */ -using System; -using System.Diagnostics; -using System.Reflection; -using AspNet.Security.OpenIdConnect.Server; -using Microsoft.AspNet.FileProviders; -using Microsoft.AspNet.Http; using Microsoft.AspNet.Identity; -using Microsoft.AspNet.Identity.EntityFramework; -using Microsoft.AspNet.StaticFiles; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Internal; using OpenIddict; - -#if DNX451 -using NWebsec.Owin; -#endif +using OpenIddict.Models; namespace Microsoft.AspNet.Builder { public static class OpenIddictExtensions { - public static IApplicationBuilder UseOpenIddict([NotNull] this IApplicationBuilder app) - where TContext : OpenIddictContext { - return app.UseOpenIddict(configuration => { }); - } - - public static IApplicationBuilder UseOpenIddict( - [NotNull] this IApplicationBuilder app, - [NotNull] Action configuration) - where TContext : OpenIddictContext { - return app.UseOpenIddict(configuration); - } - - public static IApplicationBuilder UseOpenIddict([NotNull] this IApplicationBuilder app) - where TContext : OpenIddictContext - where TUser : IdentityUser { - return app.UseOpenIddict(configuration => { }); + public static OpenIddictBuilder AddOpenIddict([NotNull] this IdentityBuilder builder) { + return builder.AddOpenIddictCore() + .AddEntityFrameworkStore(); } - public static IApplicationBuilder UseOpenIddict( - [NotNull] this IApplicationBuilder app, - [NotNull] Action configuration) - where TContext : OpenIddictContext - where TUser : IdentityUser { - return app.UseOpenIddict(configuration); - } - - public static IApplicationBuilder UseOpenIddict([NotNull] this IApplicationBuilder app) - where TContext : OpenIddictContext - where TRole : IdentityRole - where TUser : IdentityUser { - return app.UseOpenIddict(configuration => { }); - } - - public static IApplicationBuilder UseOpenIddict( - [NotNull] this IApplicationBuilder app, - [NotNull] Action configuration) - where TContext : OpenIddictContext - where TRole : IdentityRole - where TUser : IdentityUser { - return app.UseOpenIddict(configuration); - } - - public static IApplicationBuilder UseOpenIddict( - [NotNull] this IApplicationBuilder app, - [NotNull] Action configuration) - where TContext : OpenIddictContext - where TUser : IdentityUser - where TRole : IdentityRole - where TKey : IEquatable { - var instance = new OpenIddictOptions(); - - // Turn ApplicationCanDisplayErrors on to ensure ASP.NET MVC 6 - // handles errored requests and returns an appropriate error page. - instance.ApplicationCanDisplayErrors = true; - - // Call the configuration delegate defined by the user. - configuration(instance); - - // Run OpenIddict in an isolated environment. - return app.Isolate(builder => { - // Add the options to the ASP.NET context - // before executing the rest of the pipeline. - builder.Use(next => context => { - context.Items[typeof(OpenIddictOptions)] = instance; - - return next(context); - }); - -#if DNX451 - builder.UseKatana(owin => { - // Insert a new middleware responsible of setting the Content-Security-Policy header. - // See https://nwebsec.codeplex.com/wikipage?title=Configuring%20Content%20Security%20Policy&referringTitle=NWebsec - owin.UseCsp(options => options.DefaultSources(directive => directive.Self()) - .ImageSources(directive => directive.Self().CustomSources("*")) - .ScriptSources(directive => directive.UnsafeInline()) - .StyleSources(directive => directive.Self().UnsafeInline())); - - // Insert a new middleware responsible of setting the X-Content-Type-Options header. - // See https://nwebsec.codeplex.com/wikipage?title=Configuring%20security%20headers&referringTitle=NWebsec - owin.UseXContentTypeOptions(); - - // Insert a new middleware responsible of setting the X-Frame-Options header. - // See https://nwebsec.codeplex.com/wikipage?title=Configuring%20security%20headers&referringTitle=NWebsec - owin.UseXfo(options => options.Deny()); - - // Insert a new middleware responsible of setting the X-Xss-Protection header. - // See https://nwebsec.codeplex.com/wikipage?title=Configuring%20security%20headers&referringTitle=NWebsec - owin.UseXXssProtection(options => options.EnabledWithBlockMode()); - }); -#endif - if (!instance.UseCustomViews) { - builder.UseStaticFiles(new StaticFileOptions { - FileProvider = new EmbeddedFileProvider( - assembly: Assembly.Load(new AssemblyName("OpenIddict.Assets")), - baseNamespace: "OpenIddict.Assets") - }); - } - - builder.UseCors(options => { - options.AllowAnyHeader(); - options.AllowAnyMethod(); - options.AllowAnyOrigin(); - options.AllowCredentials(); - }); - - // Add OpenIdConnectServerMiddleware to the ASP.NET 5 pipeline. - builder.UseOpenIdConnectServer(options => { - options.Provider = new OpenIddictProvider(); - - // Copy the OpenIddict options to the ASOS configuration. - options.Options.AuthenticationScheme = instance.AuthenticationScheme; - - options.Options.Issuer = instance.Issuer; - - options.Options.AuthorizationEndpointPath = instance.AuthorizationEndpointPath; - options.Options.LogoutEndpointPath = instance.LogoutEndpointPath; - - options.Options.AccessTokenLifetime = instance.AccessTokenLifetime; - options.Options.AuthorizationCodeLifetime = instance.AuthorizationCodeLifetime; - options.Options.IdentityTokenLifetime = instance.IdentityTokenLifetime; - options.Options.RefreshTokenLifetime = instance.RefreshTokenLifetime; - - options.Options.ApplicationCanDisplayErrors = instance.ApplicationCanDisplayErrors; - options.Options.AllowInsecureHttp = instance.AllowInsecureHttp; - }); - - // Register ASP.NET MVC 6 and the actions - // associated to the OpenIddict controller. - builder.UseMvc(routes => { - // Register the actions corresponding to the authorization endpoint. - if (instance.AuthorizationEndpointPath.HasValue) { - routes.MapRoute("{D97891B4}", instance.AuthorizationEndpointPath.Value.Substring(1), new { - controller = typeof(OpenIddictController).Name, - action = nameof(OpenIddictController.Authorize) - }); - - routes.MapRoute("{7148DB83}", instance.AuthorizationEndpointPath.Value.Substring(1) + "/accept", new { - controller = typeof(OpenIddictController).Name, - action = nameof(OpenIddictController.Accept) - }); - - routes.MapRoute("{23438BCC}", instance.AuthorizationEndpointPath.Value.Substring(1) + "/deny", new { - controller = typeof(OpenIddictController).Name, - action = nameof(OpenIddictController.Deny) - }); - } - - // Register the action corresponding to the logout endpoint. - if (instance.LogoutEndpointPath.HasValue) { - routes.MapRoute("{C7DB102A}", instance.LogoutEndpointPath.Value.Substring(1), new { - controller = typeof(OpenIddictController).Name, - action = nameof(OpenIddictController.Logout) - }); - } - }); - }, services => { - services.AddAuthentication(); - services.AddCaching(); - services.AddCors(); - services.AddOptions(); - - services.AddMvc() - // Register the OpenIddict controller. - .AddControllersAsServices(new[] { - typeof(OpenIddictController) - }) - - // Update the Razor options to use an embedded provider - // extracting its views from the current assembly. - .AddRazorOptions(options => { - if (!instance.UseCustomViews) { - options.FileProvider = new EmbeddedFileProvider( - assembly: typeof(OpenIddictOptions).GetTypeInfo().Assembly, - baseNamespace: typeof(OpenIddictOptions).Namespace); - } - }); - - // Register the sign-in manager in the isolated container. - services.AddScoped(provider => { - var accessor = provider.GetRequiredService(); - var container = (IServiceProvider) accessor.HttpContext.Items[typeof(IServiceProvider)]; - Debug.Assert(container != null); - - // Resolve the sign-in manager from the parent container. - return container.GetRequiredService>(); - }); - - // Register the user manager in the isolated container. - services.AddScoped(provider => { - var accessor = provider.GetRequiredService(); - var container = (IServiceProvider) accessor.HttpContext.Items[typeof(IServiceProvider)]; - Debug.Assert(container != null); - - // Resolve the user manager from the parent container. - return container.GetRequiredService>(); - }); - - // Register the OpenIddict context in the isolated container. - services.AddScoped(provider => { - var accessor = provider.GetRequiredService(); - var container = (IServiceProvider) accessor.HttpContext.Items[typeof(IServiceProvider)]; - Debug.Assert(container != null); - - // Resolve the EntityFramework context from the parent container. - return container.GetRequiredService(); - }); - }); + public static OpenIddictBuilder AddOpenIddict([NotNull] this IdentityBuilder builder) + where TApplication : Application { + return builder.AddOpenIddictCore() + .AddEntityFrameworkStore(); } } } \ No newline at end of file diff --git a/src/OpenIddict/project.json b/src/OpenIddict/project.json index 15b16c37..8a2c2107 100644 --- a/src/OpenIddict/project.json +++ b/src/OpenIddict/project.json @@ -1,42 +1,17 @@ { "version": "1.0.0-alpha1-*", - "resource": "Views/**", - "dependencies": { - "Microsoft.AspNet.Cors": "6.0.0-*", - "Microsoft.AspNet.FileProviders.Embedded": "1.0.0-*", - "Microsoft.AspNet.Identity.EntityFramework": "3.0.0-*", - "Microsoft.AspNet.Mvc": "6.0.0-*", - "Microsoft.AspNet.StaticFiles": "1.0.0-*", - - "Microsoft.Extensions.Configuration.Json": "1.0.0-*", + "OpenIddict.EF": "1.0.0-*", "Microsoft.Extensions.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" - }, - - "AspNet.Hosting.Extensions": "1.0.0-*", - "AspNet.Security.OpenIdConnect.Server": "1.0.0-*", - - "OpenIddict.Assets": "1.0.0-*", - "OpenIddict.Models": "1.0.0-*" + } }, "frameworks": { - "dnx451": { - "dependencies": { - "AspNet.Hosting.Katana.Extensions": "1.0.0-*", - "NWebsec.Owin": "1.0.0" - } - }, - - "dnxcore50": { - "dependencies": { - "System.Linq": "4.0.1-*", - "System.Runtime.Serialization.Primitives": "4.0.11-*" - } - } + "dnx451": { }, + "dnxcore50": { } } } \ No newline at end of file