diff --git a/samples/Mvc.Server/Certificate.pfx b/samples/Mvc.Server/Certificate.pfx new file mode 100644 index 00000000..8c05f9d4 Binary files /dev/null and b/samples/Mvc.Server/Certificate.pfx differ diff --git a/samples/Mvc.Server/Startup.cs b/samples/Mvc.Server/Startup.cs index d9483681..439516b3 100644 --- a/samples/Mvc.Server/Startup.cs +++ b/samples/Mvc.Server/Startup.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Reflection; using AspNet.Security.OAuth.GitHub; using CryptoHelper; using Microsoft.AspNetCore.Builder; @@ -83,6 +84,25 @@ namespace Mvc.Server { app.UseOpenIddict(builder => { builder.Options.AllowInsecureHttp = true; + // Note: if you don't explicitly register a signing key, one is automatically generated and + // persisted on the disk. If the key cannot be persisted, an in-memory key is used instead: + // when the application shuts down, the key is definitely lost and the access/identity tokens + // will be considered as invalid by client applications/resource servers when validating them. + // + // On production, using a X.509 certificate stored in the machine store is recommended. + // You can generate a self-signed certificate using Pluralsight's self-cert utility: + // https://s3.amazonaws.com/pluralsight-free/keith-brown/samples/SelfCert.zip + // + // builder.UseSigningCertificate("7D2A741FE34CC2C7369237A5F2078988E17A6A75"); + // + // Alternatively, you can also store the certificate as an embedded .pfx resource + // directly in this assembly or in a file published alongside this project: + // + // builder.UseSigningCertificate( + // assembly: typeof(Startup).GetTypeInfo().Assembly, + // resource: "Nancy.Server.Certificate.pfx", + // password: "Owin.Security.OpenIdConnect.Server"); + // 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. builder.UseNWebsec(directives => { diff --git a/samples/Mvc.Server/project.json b/samples/Mvc.Server/project.json index c2f3504a..ada35516 100644 --- a/samples/Mvc.Server/project.json +++ b/samples/Mvc.Server/project.json @@ -5,6 +5,10 @@ "warningsAsErrors": true, "preserveCompilationContext": true, + "embed": { + "include": [ "Certificate.pfx" ] + }, + "copyToOutput": { "include": [ "wwwroot", diff --git a/src/OpenIddict.Core/OpenIddictExtensions.cs b/src/OpenIddict.Core/OpenIddictExtensions.cs index a8201bf5..ff7d5911 100644 --- a/src/OpenIddict.Core/OpenIddictExtensions.cs +++ b/src/OpenIddict.Core/OpenIddictExtensions.cs @@ -5,14 +5,17 @@ */ using System; +using System.IO; using System.Linq; using System.Reflection; +using System.Security.Cryptography.X509Certificates; using AspNet.Security.OpenIdConnect.Server; using JetBrains.Annotations; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.IdentityModel.Tokens; using OpenIddict; namespace Microsoft.AspNetCore.Builder { @@ -145,13 +148,14 @@ namespace Microsoft.AspNetCore.Builder { var environment = app.ApplicationServices.GetRequiredService(); builder.Options.AllowInsecureHttp = environment.IsDevelopment() || environment.IsEnvironment("Testing"); - configuration(builder); + // Run the configuration delegate + // provided by the application. + configuration.Invoke(builder); - - // Add OpenIdConnectServerMiddleware to the ASP.NET 5 pipeline. + // Add OpenIdConnectServerMiddleware to the ASP.NET Core pipeline. builder.AddModule("ASOS", 0, map => map.UseOpenIdConnectServer(builder.Options)); - // Register the OpenIddict modules in the ASP.NET 5 pipeline. + // Register the OpenIddict modules in the ASP.NET Core pipeline. foreach (var module in builder.Modules.OrderBy(module => module.Position)) { if (module.Registration == null) { throw new InvalidOperationException("The registration delegate cannot be null."); @@ -162,5 +166,129 @@ namespace Microsoft.AspNetCore.Builder { return app; } + + /// + /// Registers a used to sign the tokens issued by OpenIddict. + /// + /// The builder used to configure OpenIddict. + /// The certificate used to sign the security tokens issued by the server. + /// The . + public static OpenIddictBuilder UseSigningCertificate( + [NotNull] this OpenIddictBuilder builder, [NotNull] X509Certificate2 certificate) { + if (builder == null) { + throw new ArgumentNullException(nameof(builder)); + } + + // Register the certificate in the ASOS/OpenIddict options. + builder.Options.SigningCredentials.AddCertificate(certificate); + + return builder; + } + + /// + /// Registers a retrieved from + /// an embedded resource to sign the tokens issued by OpenIddict. + /// + /// The builder used to configure OpenIddict. + /// The assembly containing the certificate. + /// The name of the embedded resource. + /// The password used to open the certificate. + /// The . + public static OpenIddictBuilder UseSigningCertificate( + [NotNull] this OpenIddictBuilder builder, [NotNull] Assembly assembly, + [NotNull] string resource, [NotNull] string password) { + if (builder == null) { + throw new ArgumentNullException(nameof(builder)); + } + + // Register the certificate in the ASOS/OpenIddict options. + builder.Options.SigningCredentials.AddCertificate(assembly, resource, password); + + return builder; + } + + /// + /// Registers a extracted + /// from a stream to sign the tokens issued by OpenIddict. + /// + /// The builder used to configure OpenIddict. + /// The stream containing the certificate. + /// The password used to open the certificate. + /// The . + public static OpenIddictBuilder UseSigningCertificate( + [NotNull] this OpenIddictBuilder builder, + [NotNull] Stream stream, [NotNull] string password) { + if (builder == null) { + throw new ArgumentNullException(nameof(builder)); + } + + // Register the certificate in the ASOS/OpenIddict options. + builder.Options.SigningCredentials.AddCertificate(stream, password); + + return builder; + } + + /// + /// Registers a extracted + /// from a stream to sign the tokens issued by OpenIddict. + /// + /// The builder used to configure OpenIddict. + /// The stream containing the certificate. + /// The password used to open the certificate. + /// An enumeration of flags indicating how and where to store the private key of the certificate. + /// The . + public static OpenIddictBuilder UseSigningCertificate( + [NotNull] this OpenIddictBuilder builder, [NotNull] Stream stream, + [NotNull] string password, X509KeyStorageFlags flags) { + if (builder == null) { + throw new ArgumentNullException(nameof(builder)); + } + + // Register the certificate in the ASOS/OpenIddict options. + builder.Options.SigningCredentials.AddCertificate(stream, password, flags); + + return builder; + } + + /// + /// Registers a retrieved from the + /// X.509 machine store to sign the tokens issued by OpenIddict. + /// + /// The builder used to configure OpenIddict. + /// The thumbprint of the certificate used to identify it in the X.509 store. + /// The . + public static OpenIddictBuilder UseSigningCertificate( + [NotNull] this OpenIddictBuilder builder, [NotNull] string thumbprint) { + if (builder == null) { + throw new ArgumentNullException(nameof(builder)); + } + + // Register the certificate in the ASOS/OpenIddict options. + builder.Options.SigningCredentials.AddCertificate(thumbprint); + + return builder; + } + + /// + /// Registers a retrieved from the + /// given X.509 store to sign the tokens issued by OpenIddict. + /// + /// The builder used to configure OpenIddict. + /// The thumbprint of the certificate used to identify it in the X.509 store. + /// The name of the X.509 store. + /// The location of the X.509 store. + /// The . + public static OpenIddictBuilder UseSigningCertificate( + [NotNull] this OpenIddictBuilder builder, + [NotNull] string thumbprint, StoreName name, StoreLocation location) { + if (builder == null) { + throw new ArgumentNullException(nameof(builder)); + } + + // Register the certificate in the ASOS/OpenIddict options. + builder.Options.SigningCredentials.AddCertificate(thumbprint, name, location); + + return builder; + } } } \ No newline at end of file