diff --git a/OpenIddict.sln b/OpenIddict.sln index d9c82fb6..49bf3dab 100644 --- a/OpenIddict.sln +++ b/OpenIddict.sln @@ -27,6 +27,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "external", "external", "{DE EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NWebsec", "external\NWebsec\NWebsec.xproj", "{38C8E88F-1D01-466F-B47D-6D67F13C1594}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIddict.Security", "src\OpenIddict.Security\OpenIddict.Security.xproj", "{3744B1BC-3498-4958-B020-B2688A78B989}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -69,6 +71,10 @@ Global {38C8E88F-1D01-466F-B47D-6D67F13C1594}.Debug|Any CPU.Build.0 = Debug|Any CPU {38C8E88F-1D01-466F-B47D-6D67F13C1594}.Release|Any CPU.ActiveCfg = Release|Any CPU {38C8E88F-1D01-466F-B47D-6D67F13C1594}.Release|Any CPU.Build.0 = Release|Any CPU + {3744B1BC-3498-4958-B020-B2688A78B989}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3744B1BC-3498-4958-B020-B2688A78B989}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3744B1BC-3498-4958-B020-B2688A78B989}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3744B1BC-3498-4958-B020-B2688A78B989}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -83,5 +89,6 @@ Global {E60CF8CA-6313-4359-BE43-AFCBB927EA30} = {D544447C-D701-46BB-9A5B-C76C612A596B} {7AE46E2F-E93B-4FF9-B941-6CD7A3E1BF84} = {D544447C-D701-46BB-9A5B-C76C612A596B} {38C8E88F-1D01-466F-B47D-6D67F13C1594} = {DE26CC68-28BA-44BB-B28E-43B949C6C606} + {3744B1BC-3498-4958-B020-B2688A78B989} = {D544447C-D701-46BB-9A5B-C76C612A596B} EndGlobalSection EndGlobal diff --git a/samples/Mvc.Server/Startup.cs b/samples/Mvc.Server/Startup.cs index 6fe79697..256798bb 100644 --- a/samples/Mvc.Server/Startup.cs +++ b/samples/Mvc.Server/Startup.cs @@ -10,9 +10,11 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Mvc.Server.Models; using Mvc.Server.Services; +using NWebsec.Middleware; using OpenIddict; using OpenIddict.Models; + namespace Mvc.Server { public class Startup { public static void Main(string[] args) { @@ -92,7 +94,19 @@ namespace Mvc.Server { // Note: OpenIddict must be added after // ASP.NET Identity and the external providers. - app.UseOpenIddict(); + app.UseOpenIddict(options => { + // You can customize the default Content Security Policy (CSP) by calling UseNWebsec explicitly. + // This can be useful to allow your HTML views to reference remote scripts/images/styles. + options.UseNWebsec(directives => { + directives.DefaultSources(directive => directive.Self()) + .ImageSources(directive => directive.Self().CustomSources("*")) + .ScriptSources(directive => directive + .Self() + .UnsafeInline() + .CustomSources("https://my.custom.url")) + .StyleSources(directive => directive.Self().UnsafeInline()); + }); + }); app.UseMvcWithDefaultRoute(); diff --git a/src/OpenIddict.Core/OpenIddictBuilder.cs b/src/OpenIddict.Core/OpenIddictBuilder.cs index 02b3b543..87e42042 100644 --- a/src/OpenIddict.Core/OpenIddictBuilder.cs +++ b/src/OpenIddict.Core/OpenIddictBuilder.cs @@ -3,7 +3,7 @@ using OpenIddict; namespace Microsoft.AspNet.Builder { /// - /// Holds various properties allowing to configure OpenIddct. + /// Holds various properties allowing to configure OpenIddict. /// public class OpenIddictBuilder { /// diff --git a/src/OpenIddict.Core/OpenIddictExtensions.cs b/src/OpenIddict.Core/OpenIddictExtensions.cs index b7846519..ce930ef4 100644 --- a/src/OpenIddict.Core/OpenIddictExtensions.cs +++ b/src/OpenIddict.Core/OpenIddictExtensions.cs @@ -49,9 +49,10 @@ namespace Microsoft.AspNet.Builder { [NotNull] this OpenIddictBuilder builder, [NotNull] string name, int position, [NotNull] Action registration) { - // By default, prevent duplicate registrations. - if (builder.Modules.Any(module => string.Equals(module.Name, name))) { - return builder; + // Note: always call ToArray to make sure the foreach + // block doesn't iterate on the modified collection. + foreach (var module in builder.Modules.Where(module => string.Equals(module.Name, name)).ToArray()) { + builder.Modules.Remove(module); } builder.Modules.Add(new OpenIddictModule { @@ -81,12 +82,6 @@ namespace Microsoft.AspNet.Builder { configuration(builder); - builder.AddModule("CORS", -10, map => map.UseCors(options => { - options.AllowAnyHeader(); - options.AllowAnyMethod(); - options.AllowAnyOrigin(); - options.AllowCredentials(); - })); // Add OpenIdConnectServerMiddleware to the ASP.NET 5 pipeline. builder.AddModule("ASOS", 0, map => map.UseOpenIdConnectServer(builder.Options)); diff --git a/src/OpenIddict.Mvc/OpenIddictExtensions.cs b/src/OpenIddict.Mvc/OpenIddictExtensions.cs index 696ecce7..cf5f751d 100644 --- a/src/OpenIddict.Mvc/OpenIddictExtensions.cs +++ b/src/OpenIddict.Mvc/OpenIddictExtensions.cs @@ -13,36 +13,14 @@ using Microsoft.AspNet.Identity; using Microsoft.AspNet.Mvc.ApplicationModels; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Internal; -using NWebsec.Middleware; using OpenIddict; using OpenIddict.Mvc; namespace Microsoft.AspNet.Builder { public static class OpenIddictExtensions { public static OpenIddictBuilder UseMvc([NotNull] this OpenIddictBuilder builder) { - builder.AddModule("NWebsec", -20, app => { - // 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 - app.UseCsp(options => options.DefaultSources(directive => directive.Self()) - .ImageSources(directive => directive.Self().CustomSources("*")) - .ScriptSources(directive => directive.Self().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 - app.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 - app.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 - app.UseXXssProtection(options => options.EnabledWithBlockMode()); - }); - - // Run the rest of the pipeline in an isolated environment. - builder.AddModule("MVC", 10, app => app.Isolate(map => map.UseMvc(routes => { + // Run MVC in an isolated environment. + return builder.AddModule("MVC", 10, app => app.Isolate(map => map.UseMvc(routes => { // Register the actions corresponding to the authorization endpoint. if (builder.Options.AuthorizationEndpointPath.HasValue) { routes.MapRoute("{D97891B4}", builder.Options.AuthorizationEndpointPath.Value.Substring(1), new { @@ -110,8 +88,6 @@ namespace Microsoft.AspNet.Builder { // Register the options in the isolated container. services.AddScoped(provider => builder.Options); })); - - return builder; } private class OpenIddictConvention : IControllerModelConvention { diff --git a/src/OpenIddict.Mvc/project.json b/src/OpenIddict.Mvc/project.json index 1c117790..5168cbe2 100644 --- a/src/OpenIddict.Mvc/project.json +++ b/src/OpenIddict.Mvc/project.json @@ -17,12 +17,6 @@ "version": "1.0.0-*" }, - "NWebsec": { - "type": "build", - "version": "1.0.0-internal-*", - "target": "project" - }, - "OpenIddict.Core": "1.0.0-*" }, diff --git a/src/OpenIddict.Security/OpenIddict.Security.xproj b/src/OpenIddict.Security/OpenIddict.Security.xproj new file mode 100644 index 00000000..ef5bd5ca --- /dev/null +++ b/src/OpenIddict.Security/OpenIddict.Security.xproj @@ -0,0 +1,25 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + 3744b1bc-3498-4958-b020-b2688a78b989 + OpenIddict.Security + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + + 2.0 + + + + + + + + diff --git a/src/OpenIddict.Security/OpenIddictExtensions.cs b/src/OpenIddict.Security/OpenIddictExtensions.cs new file mode 100644 index 00000000..bda7d6e6 --- /dev/null +++ b/src/OpenIddict.Security/OpenIddictExtensions.cs @@ -0,0 +1,50 @@ +using System; +using Microsoft.Extensions.Internal; +using NWebsec.Middleware; + +namespace Microsoft.AspNet.Builder { + public static class OpenIddictExtensions { + public static OpenIddictBuilder UseNWebsec([NotNull] this OpenIddictBuilder builder) { + return builder.UseNWebsec(options => { + options.DefaultSources(directive => directive.Self()) + .ImageSources(directive => directive.Self().CustomSources("*")) + .ScriptSources(directive => directive.Self().UnsafeInline()) + .StyleSources(directive => directive.Self().UnsafeInline()); + }); + } + + public static OpenIddictBuilder UseNWebsec( + [NotNull] this OpenIddictBuilder builder, + [NotNull] Action configuration) { + return builder.AddModule("NWebsec", -20, app => { + // 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 + app.UseCsp(configuration); + + // 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 + app.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 + app.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 + app.UseXXssProtection(options => options.EnabledWithBlockMode()); + }); + } + + public static OpenIddictBuilder UseCors([NotNull] this OpenIddictBuilder builder) { + //Add CORS to the app + builder.AddModule("CORS", -10, map => map.UseCors(options => { + options.AllowAnyHeader(); + options.AllowAnyMethod(); + options.AllowAnyOrigin(); + options.AllowCredentials(); + })); + + return builder; + } + } +} diff --git a/src/OpenIddict.Security/project.json b/src/OpenIddict.Security/project.json new file mode 100644 index 00000000..32fa44fa --- /dev/null +++ b/src/OpenIddict.Security/project.json @@ -0,0 +1,22 @@ +{ + "version": "1.0.0-*", + + "description": "Security headers module for OpenIddict.", + + "dependencies": { + "OpenIddict.Core": "1.0.0-*", + "NWebsec": { + "type": "build", + "version": "1.0.0-internal-*" + }, + "Microsoft.Extensions.NotNullAttribute.Sources": { + "type": "build", + "version": "1.0.0-*" + } + }, + + "frameworks": { + "dnx451": { }, + "dnxcore50": { } + } +} diff --git a/src/OpenIddict/OpenIddictExtensions.cs b/src/OpenIddict/OpenIddictExtensions.cs index 6d5167d7..52ab7e86 100644 --- a/src/OpenIddict/OpenIddictExtensions.cs +++ b/src/OpenIddict/OpenIddictExtensions.cs @@ -34,8 +34,8 @@ namespace Microsoft.AspNet.Builder { [NotNull] this IApplicationBuilder app, [NotNull] Action configuration) { return app.UseOpenIddictCore(builder => { - // By default, both the assets - // and the MVC modules are enabled. + builder.UseNWebsec(); + builder.UseCors(); builder.UseAssets(); builder.UseMvc(); diff --git a/src/OpenIddict/project.json b/src/OpenIddict/project.json index a2d6756f..6380531a 100644 --- a/src/OpenIddict/project.json +++ b/src/OpenIddict/project.json @@ -7,6 +7,7 @@ "OpenIddict.Assets": "1.0.0-*", "OpenIddict.EF": "1.0.0-*", "OpenIddict.Mvc": "1.0.0-*", + "OpenIddict.Security": "1.0.0-*", "Microsoft.Extensions.NotNullAttribute.Sources": { "type": "build",