/* * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) * See https://github.com/openiddict/openiddict-core for more information concerning * the license and the contributors participating to this project. */ using System; using System.ComponentModel; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.IdentityModel.Tokens; using OpenIddict.Abstractions; using OpenIddict.Server; namespace Microsoft.Extensions.DependencyInjection { /// /// Exposes the necessary methods required to configure the OpenIddict server services. /// public class OpenIddictServerBuilder { /// /// Initializes a new instance of . /// /// The services collection. public OpenIddictServerBuilder([NotNull] IServiceCollection services) => Services = services ?? throw new ArgumentNullException(nameof(services)); /// /// Gets the services collection. /// [EditorBrowsable(EditorBrowsableState.Never)] public IServiceCollection Services { get; } /// /// Registers an event handler using the specified configuration delegate. /// /// The event context type. /// The configuration delegate. /// The . [EditorBrowsable(EditorBrowsableState.Advanced)] public OpenIddictServerBuilder AddEventHandler( [NotNull] Action> configuration) where TContext : OpenIddictServerEvents.BaseContext { if (configuration == null) { throw new ArgumentNullException(nameof(configuration)); } var builder = OpenIddictServerHandlerDescriptor.CreateBuilder(); configuration(builder); return AddEventHandler(builder.Build()); } /// /// Registers an event handler using the specified descriptor. /// /// The handler descriptor. /// The . [EditorBrowsable(EditorBrowsableState.Advanced)] public OpenIddictServerBuilder AddEventHandler([NotNull] OpenIddictServerHandlerDescriptor descriptor) { if (descriptor == null) { throw new ArgumentNullException(nameof(descriptor)); } // Register the handler in the services collection. Services.Add(descriptor.ServiceDescriptor); return Configure(options => options.CustomHandlers.Add(descriptor)); } /// /// Removes the event handler that matches the specified descriptor. /// /// The descriptor corresponding to the handler to remove. /// The . [EditorBrowsable(EditorBrowsableState.Advanced)] public OpenIddictServerBuilder RemoveEventHandler([NotNull] OpenIddictServerHandlerDescriptor descriptor) { if (descriptor == null) { throw new ArgumentNullException(nameof(descriptor)); } Services.RemoveAll(descriptor.ServiceDescriptor.ServiceType); Services.PostConfigure(options => { for (var index = options.CustomHandlers.Count - 1; index >= 0; index--) { if (options.CustomHandlers[index].ServiceDescriptor.ServiceType == descriptor.ServiceDescriptor.ServiceType) { options.CustomHandlers.RemoveAt(index); } } for (var index = options.DefaultHandlers.Count - 1; index >= 0; index--) { if (options.DefaultHandlers[index].ServiceDescriptor.ServiceType == descriptor.ServiceDescriptor.ServiceType) { options.DefaultHandlers.RemoveAt(index); } } }); return this; } /// /// Amends the default OpenIddict server configuration. /// /// The delegate used to configure the OpenIddict options. /// This extension can be safely called multiple times. /// The . public OpenIddictServerBuilder Configure([NotNull] Action configuration) { if (configuration == null) { throw new ArgumentNullException(nameof(configuration)); } Services.Configure(configuration); return this; } /// /// Makes client identification optional so that token, introspection and revocation /// requests that don't specify a client_id are not automatically rejected. /// Enabling this option is NOT recommended. /// /// The . public OpenIddictServerBuilder AcceptAnonymousClients() => Configure(options => options.AcceptAnonymousClients = true); /// /// Registers the used to encrypt the tokens issued by OpenIddict. /// /// The encrypting credentials. /// The . public OpenIddictServerBuilder AddEncryptionCredentials([NotNull] EncryptingCredentials credentials) { if (credentials == null) { throw new ArgumentNullException(nameof(credentials)); } return Configure(options => options.EncryptionCredentials.Add(credentials)); } /// /// Registers a used to encrypt the access tokens issued by OpenIddict. /// /// The security key. /// The . public OpenIddictServerBuilder AddEncryptionKey([NotNull] SecurityKey key) { if (key == null) { throw new ArgumentNullException(nameof(key)); } // If the encryption key is an asymmetric security key, ensure it has a private key. if (key is AsymmetricSecurityKey asymmetricSecurityKey && asymmetricSecurityKey.PrivateKeyStatus == PrivateKeyStatus.DoesNotExist) { throw new InvalidOperationException("The asymmetric encryption key doesn't contain the required private key."); } if (IsAlgorithmSupported(key, SecurityAlgorithms.Aes256KW)) { return AddEncryptionCredentials(new EncryptingCredentials(key, SecurityAlgorithms.Aes256KW, SecurityAlgorithms.Aes256CbcHmacSha512)); } if (IsAlgorithmSupported(key, SecurityAlgorithms.RsaOAEP)) { return AddEncryptionCredentials(new EncryptingCredentials(key, SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes256CbcHmacSha512)); } throw new InvalidOperationException(new StringBuilder() .AppendLine("An encryption algorithm cannot be automatically inferred from the encrypting key.") .Append("Consider using 'options.AddEncryptionCredentials(EncryptingCredentials)' instead.") .ToString()); static bool IsAlgorithmSupported(SecurityKey key, string algorithm) => key.CryptoProviderFactory.IsSupportedAlgorithm(algorithm, key); } /// /// Registers (and generates if necessary) a user-specific development /// certificate used to encrypt the tokens issued by OpenIddict. /// /// The . public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate() => AddDevelopmentEncryptionCertificate(new X500DistinguishedName("CN=OpenIddict Server Encryption Certificate")); /// /// Registers (and generates if necessary) a user-specific development /// certificate used to encrypt the tokens issued by OpenIddict. /// /// The subject name associated with the certificate. /// The . public OpenIddictServerBuilder AddDevelopmentEncryptionCertificate([NotNull] X500DistinguishedName subject) { if (subject == null) { throw new ArgumentNullException(nameof(subject)); } using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); // Try to retrieve the development certificate from the specified store. // If a certificate was found but is not yet or no longer valid, remove it // from the store before creating and persisting a new encryption certificate. var certificate = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, subject.Name, validOnly: false) .OfType() .SingleOrDefault(); if (certificate != null && (certificate.NotBefore > DateTime.Now || certificate.NotAfter < DateTime.Now)) { store.Remove(certificate); certificate = null; } #if SUPPORTS_CERTIFICATE_GENERATION // If no appropriate certificate can be found, generate and persist a new certificate in the specified store. if (certificate == null) { using var algorithm = RSA.Create(keySizeInBits: 2048); var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.KeyEncipherment, critical: true)); certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(2)); // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { certificate.FriendlyName = "OpenIddict Server Development Encryption Certificate"; } // Note: CertificateRequest.CreateSelfSigned() doesn't mark the key set associated with the certificate // as "persisted", which eventually prevents X509Store.Add() from correctly storing the private key. // To work around this issue, the certificate payload is manually exported and imported back // into a new X509Certificate2 instance specifying the X509KeyStorageFlags.PersistKeySet flag. var data = certificate.Export(X509ContentType.Pfx, string.Empty); try { var flags = X509KeyStorageFlags.PersistKeySet; // Note: macOS requires marking the certificate private key as exportable. // If this flag is not set, a CryptographicException is thrown at runtime. if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { flags |= X509KeyStorageFlags.Exportable; } certificate = new X509Certificate2(data, string.Empty, flags); } finally { Array.Clear(data, 0, data.Length); } store.Add(certificate); } return AddEncryptionCertificate(certificate); #else throw new PlatformNotSupportedException("X.509 certificate generation is not supported on this platform."); #endif } /// /// Registers a new ephemeral key used to encrypt the tokens issued by OpenIddict: the key /// is discarded when the application shuts down and tokens encrypted using this key are /// automatically invalidated. This method should only be used during development. /// On production, using a X.509 certificate stored in the machine store is recommended. /// /// The . public OpenIddictServerBuilder AddEphemeralEncryptionKey() => AddEphemeralEncryptionKey(SecurityAlgorithms.RsaOAEP); /// /// Registers a new ephemeral key used to encrypt the tokens issued by OpenIddict: the key /// is discarded when the application shuts down and tokens encrypted using this key are /// automatically invalidated. This method should only be used during development. /// On production, using a X.509 certificate stored in the machine store is recommended. /// /// The algorithm associated with the encryption key. /// The . public OpenIddictServerBuilder AddEphemeralEncryptionKey([NotNull] string algorithm) { if (string.IsNullOrEmpty(algorithm)) { throw new ArgumentException("The algorithm cannot be null or empty.", nameof(algorithm)); } switch (algorithm) { case SecurityAlgorithms.Aes256KW: return AddEncryptionCredentials(new EncryptingCredentials(CreateSymmetricSecurityKey(256), algorithm, SecurityAlgorithms.Aes256CbcHmacSha512)); case SecurityAlgorithms.RsaOAEP: case SecurityAlgorithms.RsaOaepKeyWrap: return AddEncryptionCredentials(new EncryptingCredentials(CreateRsaSecurityKey(2048), algorithm, SecurityAlgorithms.Aes256CbcHmacSha512)); default: throw new InvalidOperationException("The specified algorithm is not supported."); } static SymmetricSecurityKey CreateSymmetricSecurityKey(int size) { var data = new byte[size / 8]; #if SUPPORTS_STATIC_RANDOM_NUMBER_GENERATOR_METHODS RandomNumberGenerator.Fill(data); #else using var generator = RandomNumberGenerator.Create(); generator.GetBytes(data); #endif return new SymmetricSecurityKey(data); } static RsaSecurityKey CreateRsaSecurityKey(int size) { #if SUPPORTS_DIRECT_KEY_CREATION_WITH_SPECIFIED_SIZE return new RsaSecurityKey(RSA.Create(size)); #else // Note: a 1024-bit key might be returned by RSA.Create() on .NET Desktop/Mono, // where RSACryptoServiceProvider is still the default implementation and // where custom implementations can be registered via CryptoConfig. // To ensure the key size is always acceptable, replace it if necessary. var algorithm = RSA.Create(); if (algorithm.KeySize < size) { algorithm.KeySize = size; } if (algorithm.KeySize < size && algorithm is RSACryptoServiceProvider) { algorithm.Dispose(); algorithm = new RSACryptoServiceProvider(size); } if (algorithm.KeySize < size) { throw new InvalidOperationException("RSA key generation failed."); } return new RsaSecurityKey(algorithm); #endif } } /// /// Registers a that is used to encrypt the tokens issued by OpenIddict. /// /// The certificate used to encrypt the security tokens issued by the server. /// The . public OpenIddictServerBuilder AddEncryptionCertificate([NotNull] X509Certificate2 certificate) { if (certificate == null) { throw new ArgumentNullException(nameof(certificate)); } if (certificate.NotBefore > DateTime.Now) { throw new InvalidOperationException("The specified certificate is not yet valid."); } if (certificate.NotAfter < DateTime.Now) { throw new InvalidOperationException("The specified certificate is no longer valid."); } if (!certificate.HasPrivateKey) { throw new InvalidOperationException("The specified certificate doesn't contain the required private key."); } return AddEncryptionKey(new X509SecurityKey(certificate)); } /// /// Registers a retrieved from an /// embedded resource and used to encrypt the tokens issued by OpenIddict. /// /// The assembly containing the certificate. /// The name of the embedded resource. /// The password used to open the certificate. /// The . public OpenIddictServerBuilder AddEncryptionCertificate( [NotNull] Assembly assembly, [NotNull] string resource, [NotNull] string password) #if SUPPORTS_EPHEMERAL_KEY_SETS // Note: ephemeral key sets are currently not supported on macOS. => AddEncryptionCertificate(assembly, resource, password, RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? X509KeyStorageFlags.MachineKeySet : X509KeyStorageFlags.EphemeralKeySet); #else => AddEncryptionCertificate(assembly, resource, password, X509KeyStorageFlags.MachineKeySet); #endif /// /// Registers a retrieved from an /// embedded resource and used to encrypt the tokens issued by OpenIddict. /// /// The assembly containing the certificate. /// The name of the embedded resource. /// The password used to open the certificate. /// An enumeration of flags indicating how and where to store the private key of the certificate. /// The . public OpenIddictServerBuilder AddEncryptionCertificate( [NotNull] Assembly assembly, [NotNull] string resource, [NotNull] string password, X509KeyStorageFlags flags) { if (assembly == null) { throw new ArgumentNullException(nameof(assembly)); } if (string.IsNullOrEmpty(resource)) { throw new ArgumentException("The resource cannot be null or empty.", nameof(resource)); } if (string.IsNullOrEmpty(password)) { throw new ArgumentException("The password cannot be null or empty.", nameof(password)); } using var stream = assembly.GetManifestResourceStream(resource); if (stream == null) { throw new InvalidOperationException("The certificate was not found in the specified assembly."); } return AddEncryptionCertificate(stream, password, flags); } /// /// Registers a extracted from a /// stream and used to encrypt the tokens issued by OpenIddict. /// /// The stream containing the certificate. /// The password used to open the certificate. /// The . public OpenIddictServerBuilder AddEncryptionCertificate([NotNull] Stream stream, [NotNull] string password) #if SUPPORTS_EPHEMERAL_KEY_SETS // Note: ephemeral key sets are currently not supported on macOS. => AddEncryptionCertificate(stream, password, RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? X509KeyStorageFlags.MachineKeySet : X509KeyStorageFlags.EphemeralKeySet); #else => AddEncryptionCertificate(stream, password, X509KeyStorageFlags.MachineKeySet); #endif /// /// Registers a extracted from a /// stream and used to encrypt the tokens issued by OpenIddict. /// /// The stream containing the certificate. /// The password used to open the certificate. /// /// An enumeration of flags indicating how and where /// to store the private key of the certificate. /// /// The . public OpenIddictServerBuilder AddEncryptionCertificate( [NotNull] Stream stream, [NotNull] string password, X509KeyStorageFlags flags) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (string.IsNullOrEmpty(password)) { throw new ArgumentException("The password cannot be null or empty.", nameof(password)); } using var buffer = new MemoryStream(); stream.CopyTo(buffer); return AddEncryptionCertificate(new X509Certificate2(buffer.ToArray(), password, flags)); } /// /// Registers a retrieved from the X.509 /// machine store and used to encrypt the tokens issued by OpenIddict. /// /// The thumbprint of the certificate used to identify it in the X.509 store. /// The . public OpenIddictServerBuilder AddEncryptionCertificate([NotNull] string thumbprint) { if (string.IsNullOrEmpty(thumbprint)) { throw new ArgumentException("The thumbprint cannot be null or empty.", nameof(thumbprint)); } var certificate = GetCertificate(StoreLocation.CurrentUser, thumbprint) ?? GetCertificate(StoreLocation.LocalMachine, thumbprint); if (certificate == null) { throw new InvalidOperationException("The certificate corresponding to the specified thumbprint was not found."); } return AddEncryptionCertificate(certificate); static X509Certificate2 GetCertificate(StoreLocation location, string thumbprint) { using var store = new X509Store(StoreName.My, location); store.Open(OpenFlags.ReadOnly); return store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, validOnly: false) .OfType() .SingleOrDefault(); } } /// /// Registers a retrieved from the given /// X.509 store and used to encrypt the tokens issued by OpenIddict. /// /// The thumbprint of the certificate used to identify it in the X.509 store. /// The name of the X.509 store. /// The location of the X.509 store. /// The . public OpenIddictServerBuilder AddEncryptionCertificate( [NotNull] string thumbprint, StoreName name, StoreLocation location) { if (string.IsNullOrEmpty(thumbprint)) { throw new ArgumentException("The thumbprint cannot be null or empty.", nameof(thumbprint)); } using var store = new X509Store(name, location); store.Open(OpenFlags.ReadOnly); var certificate = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, validOnly: false) .OfType() .SingleOrDefault(); if (certificate == null) { throw new InvalidOperationException("The certificate corresponding to the specified thumbprint was not found."); } return AddEncryptionCertificate(certificate); } /// /// Registers the used to sign the tokens issued by OpenIddict. /// Note: using asymmetric keys is recommended on production. /// /// The signing credentials. /// The . public OpenIddictServerBuilder AddSigningCredentials([NotNull] SigningCredentials credentials) { if (credentials == null) { throw new ArgumentNullException(nameof(credentials)); } return Configure(options => options.SigningCredentials.Add(credentials)); } /// /// Registers a used to sign the tokens issued by OpenIddict. /// Note: using asymmetric keys is recommended on production. /// /// The security key. /// The . public OpenIddictServerBuilder AddSigningKey([NotNull] SecurityKey key) { if (key == null) { throw new ArgumentNullException(nameof(key)); } // If the signing key is an asymmetric security key, ensure it has a private key. if (key is AsymmetricSecurityKey asymmetricSecurityKey && asymmetricSecurityKey.PrivateKeyStatus == PrivateKeyStatus.DoesNotExist) { throw new InvalidOperationException("The asymmetric signing key doesn't contain the required private key."); } if (IsAlgorithmSupported(key, SecurityAlgorithms.RsaSha256)) { return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.RsaSha256)); } if (IsAlgorithmSupported(key, SecurityAlgorithms.HmacSha256)) { return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.HmacSha256)); } #if SUPPORTS_ECDSA // Note: ECDSA algorithms are bound to specific curves and must be treated separately. if (IsAlgorithmSupported(key, SecurityAlgorithms.EcdsaSha256)) { return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.EcdsaSha256)); } if (IsAlgorithmSupported(key, SecurityAlgorithms.EcdsaSha384)) { return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.EcdsaSha384)); } if (IsAlgorithmSupported(key, SecurityAlgorithms.EcdsaSha512)) { return AddSigningCredentials(new SigningCredentials(key, SecurityAlgorithms.EcdsaSha512)); } #else if (IsAlgorithmSupported(key, SecurityAlgorithms.EcdsaSha256) || IsAlgorithmSupported(key, SecurityAlgorithms.EcdsaSha384) || IsAlgorithmSupported(key, SecurityAlgorithms.EcdsaSha512)) { throw new PlatformNotSupportedException("ECDSA signing keys are not supported on this platform."); } #endif throw new InvalidOperationException(new StringBuilder() .AppendLine("A signature algorithm cannot be automatically inferred from the signing key.") .Append("Consider using 'options.AddSigningCredentials(SigningCredentials)' instead.") .ToString()); static bool IsAlgorithmSupported(SecurityKey key, string algorithm) => key.CryptoProviderFactory.IsSupportedAlgorithm(algorithm, key); } /// /// Registers (and generates if necessary) a user-specific development /// certificate used to sign the tokens issued by OpenIddict. /// /// The . public OpenIddictServerBuilder AddDevelopmentSigningCertificate() => AddDevelopmentSigningCertificate(new X500DistinguishedName("CN=OpenIddict Server Signing Certificate")); /// /// Registers (and generates if necessary) a user-specific development /// certificate used to sign the tokens issued by OpenIddict. /// /// The subject name associated with the certificate. /// The . public OpenIddictServerBuilder AddDevelopmentSigningCertificate([NotNull] X500DistinguishedName subject) { if (subject == null) { throw new ArgumentNullException(nameof(subject)); } using var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadWrite); // Try to retrieve the development certificate from the specified store. // If a certificate was found but is not yet or no longer valid, remove it // from the store before creating and persisting a new signing certificate. var certificate = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, subject.Name, validOnly: false) .OfType() .SingleOrDefault(); if (certificate != null && (certificate.NotBefore > DateTime.Now || certificate.NotAfter < DateTime.Now)) { store.Remove(certificate); certificate = null; } #if SUPPORTS_CERTIFICATE_GENERATION // If no appropriate certificate can be found, generate and persist a new certificate in the specified store. if (certificate == null) { using var algorithm = RSA.Create(keySizeInBits: 2048); var request = new CertificateRequest(subject, algorithm, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: true)); certificate = request.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(2)); // Note: setting the friendly name is not supported on Unix machines (including Linux and macOS). // To ensure an exception is not thrown by the property setter, an OS runtime check is used here. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { certificate.FriendlyName = "OpenIddict Server Development Signing Certificate"; } // Note: CertificateRequest.CreateSelfSigned() doesn't mark the key set associated with the certificate // as "persisted", which eventually prevents X509Store.Add() from correctly storing the private key. // To work around this issue, the certificate payload is manually exported and imported back // into a new X509Certificate2 instance specifying the X509KeyStorageFlags.PersistKeySet flag. var data = certificate.Export(X509ContentType.Pfx, string.Empty); try { var flags = X509KeyStorageFlags.PersistKeySet; // Note: macOS requires marking the certificate private key as exportable. // If this flag is not set, a CryptographicException is thrown at runtime. if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { flags |= X509KeyStorageFlags.Exportable; } certificate = new X509Certificate2(data, string.Empty, flags); } finally { Array.Clear(data, 0, data.Length); } store.Add(certificate); } return AddSigningCertificate(certificate); #else throw new PlatformNotSupportedException("X.509 certificate generation is not supported on this platform."); #endif } /// /// Registers a new ephemeral key used to sign the tokens issued by OpenIddict: the key /// is discarded when the application shuts down and tokens signed using this key are /// automatically invalidated. This method should only be used during development. /// On production, using a X.509 certificate stored in the machine store is recommended. /// /// The . public OpenIddictServerBuilder AddEphemeralSigningKey() => AddEphemeralSigningKey(SecurityAlgorithms.RsaSha256); /// /// Registers a new ephemeral key used to sign the tokens issued by OpenIddict: the key /// is discarded when the application shuts down and tokens signed using this key are /// automatically invalidated. This method should only be used during development. /// On production, using a X.509 certificate stored in the machine store is recommended. /// /// The algorithm associated with the signing key. /// The . public OpenIddictServerBuilder AddEphemeralSigningKey([NotNull] string algorithm) { if (string.IsNullOrEmpty(algorithm)) { throw new ArgumentException("The algorithm cannot be null or empty.", nameof(algorithm)); } switch (algorithm) { case SecurityAlgorithms.RsaSha256: case SecurityAlgorithms.RsaSha384: case SecurityAlgorithms.RsaSha512: case SecurityAlgorithms.RsaSha256Signature: case SecurityAlgorithms.RsaSha384Signature: case SecurityAlgorithms.RsaSha512Signature: case SecurityAlgorithms.RsaSsaPssSha256: case SecurityAlgorithms.RsaSsaPssSha384: case SecurityAlgorithms.RsaSsaPssSha512: case SecurityAlgorithms.RsaSsaPssSha256Signature: case SecurityAlgorithms.RsaSsaPssSha384Signature: case SecurityAlgorithms.RsaSsaPssSha512Signature: return AddSigningCredentials(new SigningCredentials(CreateRsaSecurityKey(2048), algorithm)); #if SUPPORTS_ECDSA case SecurityAlgorithms.EcdsaSha256: case SecurityAlgorithms.EcdsaSha256Signature: return AddSigningCredentials(new SigningCredentials(new ECDsaSecurityKey( ECDsa.Create(ECCurve.NamedCurves.nistP256)), algorithm)); case SecurityAlgorithms.EcdsaSha384: case SecurityAlgorithms.EcdsaSha384Signature: return AddSigningCredentials(new SigningCredentials(new ECDsaSecurityKey( ECDsa.Create(ECCurve.NamedCurves.nistP384)), algorithm)); case SecurityAlgorithms.EcdsaSha512: case SecurityAlgorithms.EcdsaSha512Signature: return AddSigningCredentials(new SigningCredentials(new ECDsaSecurityKey( ECDsa.Create(ECCurve.NamedCurves.nistP521)), algorithm)); #else case SecurityAlgorithms.EcdsaSha256: case SecurityAlgorithms.EcdsaSha384: case SecurityAlgorithms.EcdsaSha512: case SecurityAlgorithms.EcdsaSha256Signature: case SecurityAlgorithms.EcdsaSha384Signature: case SecurityAlgorithms.EcdsaSha512Signature: throw new PlatformNotSupportedException("ECDSA signing keys are not supported on this platform."); #endif default: throw new InvalidOperationException("The specified algorithm is not supported."); } static RsaSecurityKey CreateRsaSecurityKey(int size) { #if SUPPORTS_DIRECT_KEY_CREATION_WITH_SPECIFIED_SIZE return new RsaSecurityKey(RSA.Create(size)); #else // Note: a 1024-bit key might be returned by RSA.Create() on .NET Desktop/Mono, // where RSACryptoServiceProvider is still the default implementation and // where custom implementations can be registered via CryptoConfig. // To ensure the key size is always acceptable, replace it if necessary. var algorithm = RSA.Create(); if (algorithm.KeySize < size) { algorithm.KeySize = size; } if (algorithm.KeySize < size && algorithm is RSACryptoServiceProvider) { algorithm.Dispose(); algorithm = new RSACryptoServiceProvider(size); } if (algorithm.KeySize < size) { throw new InvalidOperationException("RSA key generation failed."); } return new RsaSecurityKey(algorithm); #endif } } /// /// Registers a that is used to sign the tokens issued by OpenIddict. /// /// The certificate used to sign the security tokens issued by the server. /// The . public OpenIddictServerBuilder AddSigningCertificate([NotNull] X509Certificate2 certificate) { if (certificate == null) { throw new ArgumentNullException(nameof(certificate)); } if (certificate.NotBefore > DateTime.Now) { throw new InvalidOperationException("The specified certificate is not yet valid."); } if (certificate.NotAfter < DateTime.Now) { throw new InvalidOperationException("The specified certificate is no longer valid."); } if (!certificate.HasPrivateKey) { throw new InvalidOperationException("The specified certificate doesn't contain the required private key."); } return AddSigningKey(new X509SecurityKey(certificate)); } /// /// Registers a retrieved from an /// embedded resource and used to sign the tokens issued by OpenIddict. /// /// The assembly containing the certificate. /// The name of the embedded resource. /// The password used to open the certificate. /// The . public OpenIddictServerBuilder AddSigningCertificate( [NotNull] Assembly assembly, [NotNull] string resource, [NotNull] string password) #if SUPPORTS_EPHEMERAL_KEY_SETS // Note: ephemeral key sets are currently not supported on macOS. => AddSigningCertificate(assembly, resource, password, RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? X509KeyStorageFlags.MachineKeySet : X509KeyStorageFlags.EphemeralKeySet); #else => AddSigningCertificate(assembly, resource, password, X509KeyStorageFlags.MachineKeySet); #endif /// /// Registers a retrieved from an /// embedded resource and used to sign the tokens issued by OpenIddict. /// /// The assembly containing the certificate. /// The name of the embedded resource. /// The password used to open the certificate. /// An enumeration of flags indicating how and where to store the private key of the certificate. /// The . public OpenIddictServerBuilder AddSigningCertificate( [NotNull] Assembly assembly, [NotNull] string resource, [NotNull] string password, X509KeyStorageFlags flags) { if (assembly == null) { throw new ArgumentNullException(nameof(assembly)); } if (string.IsNullOrEmpty(resource)) { throw new ArgumentException("The resource cannot be null or empty.", nameof(resource)); } if (string.IsNullOrEmpty(password)) { throw new ArgumentException("The password cannot be null or empty.", nameof(password)); } using var stream = assembly.GetManifestResourceStream(resource); if (stream == null) { throw new InvalidOperationException("The certificate was not found in the specified assembly."); } return AddSigningCertificate(stream, password, flags); } /// /// Registers a extracted from a /// stream and used to sign the tokens issued by OpenIddict. /// /// The stream containing the certificate. /// The password used to open the certificate. /// The . public OpenIddictServerBuilder AddSigningCertificate([NotNull] Stream stream, [NotNull] string password) #if SUPPORTS_EPHEMERAL_KEY_SETS // Note: ephemeral key sets are currently not supported on macOS. => AddSigningCertificate(stream, password, RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? X509KeyStorageFlags.MachineKeySet : X509KeyStorageFlags.EphemeralKeySet); #else => AddSigningCertificate(stream, password, X509KeyStorageFlags.MachineKeySet); #endif /// /// Registers a extracted from a /// stream and used to sign the tokens issued by OpenIddict. /// /// The stream containing the certificate. /// The password used to open the certificate. /// /// An enumeration of flags indicating how and where /// to store the private key of the certificate. /// /// The . public OpenIddictServerBuilder AddSigningCertificate( [NotNull] Stream stream, [NotNull] string password, X509KeyStorageFlags flags) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (string.IsNullOrEmpty(password)) { throw new ArgumentException("The password cannot be null or empty.", nameof(password)); } using var buffer = new MemoryStream(); stream.CopyTo(buffer); return AddSigningCertificate(new X509Certificate2(buffer.ToArray(), password, flags)); } /// /// Registers a retrieved from the X.509 /// machine store and used to sign the tokens issued by OpenIddict. /// /// The thumbprint of the certificate used to identify it in the X.509 store. /// The . public OpenIddictServerBuilder AddSigningCertificate([NotNull] string thumbprint) { if (string.IsNullOrEmpty(thumbprint)) { throw new ArgumentException("The thumbprint cannot be null or empty.", nameof(thumbprint)); } var certificate = GetCertificate(StoreLocation.CurrentUser, thumbprint) ?? GetCertificate(StoreLocation.LocalMachine, thumbprint); if (certificate == null) { throw new InvalidOperationException("The certificate corresponding to the specified thumbprint was not found."); } return AddSigningCertificate(certificate); static X509Certificate2 GetCertificate(StoreLocation location, string thumbprint) { using var store = new X509Store(StoreName.My, location); store.Open(OpenFlags.ReadOnly); return store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, validOnly: false) .OfType() .SingleOrDefault(); } } /// /// Registers a retrieved from the given /// X.509 store and used to sign the tokens issued by OpenIddict. /// /// The thumbprint of the certificate used to identify it in the X.509 store. /// The name of the X.509 store. /// The location of the X.509 store. /// The . public OpenIddictServerBuilder AddSigningCertificate( [NotNull] string thumbprint, StoreName name, StoreLocation location) { if (string.IsNullOrEmpty(thumbprint)) { throw new ArgumentException("The thumbprint cannot be null or empty.", nameof(thumbprint)); } using var store = new X509Store(name, location); store.Open(OpenFlags.ReadOnly); var certificate = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, validOnly: false) .OfType() .SingleOrDefault(); if (certificate == null) { throw new InvalidOperationException("The certificate corresponding to the specified thumbprint was not found."); } return AddSigningCertificate(certificate); } /// /// Enables authorization code flow support. For more information /// about this specific OAuth 2.0/OpenID Connect flow, visit /// https://tools.ietf.org/html/rfc6749#section-4.1 and /// http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth. /// /// The . public OpenIddictServerBuilder AllowAuthorizationCodeFlow() => Configure(options => options.GrantTypes.Add(OpenIddictConstants.GrantTypes.AuthorizationCode)); /// /// Enables client credentials flow support. For more information about this /// specific OAuth 2.0 flow, visit https://tools.ietf.org/html/rfc6749#section-4.4. /// /// The . public OpenIddictServerBuilder AllowClientCredentialsFlow() => Configure(options => options.GrantTypes.Add(OpenIddictConstants.GrantTypes.ClientCredentials)); /// /// Enables custom grant type support. /// /// The grant type associated with the flow. /// The . public OpenIddictServerBuilder AllowCustomFlow([NotNull] string type) { if (string.IsNullOrEmpty(type)) { throw new ArgumentException("The grant type cannot be null or empty.", nameof(type)); } return Configure(options => options.GrantTypes.Add(type)); } /// /// Enables device code flow support. For more information about this /// specific OAuth 2.0 flow, visit https://tools.ietf.org/html/rfc8628. /// /// The . public OpenIddictServerBuilder AllowDeviceCodeFlow() => Configure(options => options.GrantTypes.Add(OpenIddictConstants.GrantTypes.DeviceCode)); /// /// Enables implicit flow support. For more information /// about this specific OAuth 2.0/OpenID Connect flow, visit /// https://tools.ietf.org/html/rfc6749#section-4.2 and /// http://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth. /// /// The . public OpenIddictServerBuilder AllowImplicitFlow() => Configure(options => options.GrantTypes.Add(OpenIddictConstants.GrantTypes.Implicit)); /// /// Enables password flow support. For more information about this specific /// OAuth 2.0 flow, visit https://tools.ietf.org/html/rfc6749#section-4.3. /// /// The . public OpenIddictServerBuilder AllowPasswordFlow() => Configure(options => options.GrantTypes.Add(OpenIddictConstants.GrantTypes.Password)); /// /// Enables refresh token flow support. For more information about this /// specific OAuth 2.0 flow, visit https://tools.ietf.org/html/rfc6749#section-6. /// /// The . public OpenIddictServerBuilder AllowRefreshTokenFlow() => Configure(options => options.GrantTypes.Add(OpenIddictConstants.GrantTypes.RefreshToken)); /// /// Sets the relative or absolute URLs associated to the authorization endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetAuthorizationEndpointUris([NotNull] params string[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } return SetAuthorizationEndpointUris(addresses.Select(address => new Uri(address, UriKind.RelativeOrAbsolute)).ToArray()); } /// /// Sets the relative or absolute URLs associated to the authorization endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetAuthorizationEndpointUris([NotNull] params Uri[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } if (addresses.Any(address => !address.IsWellFormedOriginalString())) { throw new ArgumentException("One of the specified addresses is not valid.", nameof(addresses)); } if (addresses.Any(address => !address.IsAbsoluteUri && !address.OriginalString.StartsWith("/", StringComparison.OrdinalIgnoreCase))) { throw new ArgumentException("Relative URLs must start with a '/'.", nameof(addresses)); } return Configure(options => { options.AuthorizationEndpointUris.Clear(); foreach (var address in addresses) { options.AuthorizationEndpointUris.Add(address); } }); } /// /// Sets the relative or absolute URLs associated to the configuration endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetConfigurationEndpointUris([NotNull] params string[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } return SetConfigurationEndpointUris(addresses.Select(address => new Uri(address, UriKind.RelativeOrAbsolute)).ToArray()); } /// /// Sets the relative or absolute URLs associated to the configuration endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetConfigurationEndpointUris([NotNull] params Uri[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } if (addresses.Any(address => !address.IsWellFormedOriginalString())) { throw new ArgumentException("One of the specified addresses is not valid.", nameof(addresses)); } if (addresses.Any(address => !address.IsAbsoluteUri && !address.OriginalString.StartsWith("/", StringComparison.OrdinalIgnoreCase))) { throw new ArgumentException("Relative URLs must start with a '/'.", nameof(addresses)); } return Configure(options => { options.ConfigurationEndpointUris.Clear(); foreach (var address in addresses) { options.ConfigurationEndpointUris.Add(address); } }); } /// /// Sets the relative or absolute URLs associated to the cryptography endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetCryptographyEndpointUris([NotNull] params string[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } return SetCryptographyEndpointUris(addresses.Select(address => new Uri(address, UriKind.RelativeOrAbsolute)).ToArray()); } /// /// Sets the relative or absolute URLs associated to the cryptography endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetCryptographyEndpointUris([NotNull] params Uri[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } if (addresses.Any(address => !address.IsWellFormedOriginalString())) { throw new ArgumentException("One of the specified addresses is not valid.", nameof(addresses)); } if (addresses.Any(address => !address.IsAbsoluteUri && !address.OriginalString.StartsWith("/", StringComparison.OrdinalIgnoreCase))) { throw new ArgumentException("Relative URLs must start with a '/'.", nameof(addresses)); } return Configure(options => { options.CryptographyEndpointUris.Clear(); foreach (var address in addresses) { options.CryptographyEndpointUris.Add(address); } }); } /// /// Sets the relative or absolute URLs associated to the device endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetDeviceEndpointUris([NotNull] params string[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } return SetDeviceEndpointUris(addresses.Select(address => new Uri(address, UriKind.RelativeOrAbsolute)).ToArray()); } /// /// Sets the relative or absolute URLs associated to the device endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetDeviceEndpointUris([NotNull] params Uri[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } if (addresses.Any(address => !address.IsWellFormedOriginalString())) { throw new ArgumentException("One of the specified addresses is not valid.", nameof(addresses)); } if (addresses.Any(address => !address.IsAbsoluteUri && !address.OriginalString.StartsWith("/", StringComparison.OrdinalIgnoreCase))) { throw new ArgumentException("Relative URLs must start with a '/'.", nameof(addresses)); } return Configure(options => { options.DeviceEndpointUris.Clear(); foreach (var address in addresses) { options.DeviceEndpointUris.Add(address); } }); } /// /// Sets the relative or absolute URLs associated to the introspection endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetIntrospectionEndpointUris([NotNull] params string[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } return SetIntrospectionEndpointUris(addresses.Select(address => new Uri(address, UriKind.RelativeOrAbsolute)).ToArray()); } /// /// Sets the relative or absolute URLs associated to the introspection endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetIntrospectionEndpointUris([NotNull] params Uri[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } if (addresses.Any(address => !address.IsWellFormedOriginalString())) { throw new ArgumentException("One of the specified addresses is not valid.", nameof(addresses)); } if (addresses.Any(address => !address.IsAbsoluteUri && !address.OriginalString.StartsWith("/", StringComparison.OrdinalIgnoreCase))) { throw new ArgumentException("Relative URLs must start with a '/'.", nameof(addresses)); } return Configure(options => { options.IntrospectionEndpointUris.Clear(); foreach (var address in addresses) { options.IntrospectionEndpointUris.Add(address); } }); } /// /// Sets the relative or absolute URLs associated to the logout endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetLogoutEndpointUris([NotNull] params string[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } return SetLogoutEndpointUris(addresses.Select(address => new Uri(address, UriKind.RelativeOrAbsolute)).ToArray()); } /// /// Sets the relative or absolute URLs associated to the logout endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetLogoutEndpointUris([NotNull] params Uri[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } if (addresses.Any(address => !address.IsWellFormedOriginalString())) { throw new ArgumentException("One of the specified addresses is not valid.", nameof(addresses)); } if (addresses.Any(address => !address.IsAbsoluteUri && !address.OriginalString.StartsWith("/", StringComparison.OrdinalIgnoreCase))) { throw new ArgumentException("Relative URLs must start with a '/'.", nameof(addresses)); } return Configure(options => { options.LogoutEndpointUris.Clear(); foreach (var address in addresses) { options.LogoutEndpointUris.Add(address); } }); } /// /// Sets the relative or absolute URLs associated to the revocation endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetRevocationEndpointUris([NotNull] params string[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } return SetRevocationEndpointUris(addresses.Select(address => new Uri(address, UriKind.RelativeOrAbsolute)).ToArray()); } /// /// Sets the relative or absolute URLs associated to the revocation endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetRevocationEndpointUris([NotNull] params Uri[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } if (addresses.Any(address => !address.IsWellFormedOriginalString())) { throw new ArgumentException("One of the specified addresses is not valid.", nameof(addresses)); } if (addresses.Any(address => !address.IsAbsoluteUri && !address.OriginalString.StartsWith("/", StringComparison.OrdinalIgnoreCase))) { throw new ArgumentException("Relative URLs must start with a '/'.", nameof(addresses)); } return Configure(options => { options.RevocationEndpointUris.Clear(); foreach (var address in addresses) { options.RevocationEndpointUris.Add(address); } }); } /// /// Sets the relative or absolute URLs associated to the token endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetTokenEndpointUris([NotNull] params string[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } return SetTokenEndpointUris(addresses.Select(address => new Uri(address, UriKind.RelativeOrAbsolute)).ToArray()); } /// /// Sets the relative or absolute URLs associated to the token endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetTokenEndpointUris([NotNull] params Uri[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } if (addresses.Any(address => !address.IsWellFormedOriginalString())) { throw new ArgumentException("One of the specified addresses is not valid.", nameof(addresses)); } if (addresses.Any(address => !address.IsAbsoluteUri && !address.OriginalString.StartsWith("/", StringComparison.OrdinalIgnoreCase))) { throw new ArgumentException("Relative URLs must start with a '/'.", nameof(addresses)); } return Configure(options => { options.TokenEndpointUris.Clear(); foreach (var address in addresses) { options.TokenEndpointUris.Add(address); } }); } /// /// Sets the relative or absolute URLs associated to the userinfo endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetUserinfoEndpointUris([NotNull] params string[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } return SetUserinfoEndpointUris(addresses.Select(address => new Uri(address, UriKind.RelativeOrAbsolute)).ToArray()); } /// /// Sets the relative or absolute URLs associated to the userinfo endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned as part of the discovery document. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetUserinfoEndpointUris([NotNull] params Uri[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } if (addresses.Any(address => !address.IsWellFormedOriginalString())) { throw new ArgumentException("One of the specified addresses is not valid.", nameof(addresses)); } if (addresses.Any(address => !address.IsAbsoluteUri && !address.OriginalString.StartsWith("/", StringComparison.OrdinalIgnoreCase))) { throw new ArgumentException("Relative URLs must start with a '/'.", nameof(addresses)); } return Configure(options => { options.UserinfoEndpointUris.Clear(); foreach (var address in addresses) { options.UserinfoEndpointUris.Add(address); } }); } /// /// Sets the relative or absolute URLs associated to the verification endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned by the device endpoint. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetVerificationEndpointUris([NotNull] params string[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } return SetVerificationEndpointUris(addresses.Select(address => new Uri(address, UriKind.RelativeOrAbsolute)).ToArray()); } /// /// Sets the relative or absolute URLs associated to the verification endpoint. /// If an empty array is specified, the endpoint will be considered disabled. /// Note: only the first address will be returned by the device endpoint. /// /// The addresses associated to the endpoint. /// The . public OpenIddictServerBuilder SetVerificationEndpointUris([NotNull] params Uri[] addresses) { if (addresses == null) { throw new ArgumentNullException(nameof(addresses)); } if (addresses.Any(address => !address.IsWellFormedOriginalString())) { throw new ArgumentException("One of the specified addresses is not valid.", nameof(addresses)); } if (addresses.Any(address => !address.IsAbsoluteUri && !address.OriginalString.StartsWith("/", StringComparison.OrdinalIgnoreCase))) { throw new ArgumentException("Relative URLs must start with a '/'.", nameof(addresses)); } return Configure(options => { options.VerificationEndpointUris.Clear(); foreach (var address in addresses) { options.VerificationEndpointUris.Add(address); } }); } /// /// Disables JWT access token encryption (this option doesn't affect Data Protection tokens). /// Disabling encryption is NOT recommended and SHOULD only be done when issuing tokens /// to third-party resource servers/APIs you don't control and don't fully trust. /// /// The . public OpenIddictServerBuilder DisableAccessTokenEncryption() => Configure(options => options.DisableAccessTokenEncryption = true); /// /// Disables authorization storage so that ad-hoc authorizations are /// not created when an authorization code or refresh token is issued /// and can't be revoked to prevent associated tokens from being used. /// Using this option is generally NOT recommended. /// /// The . public OpenIddictServerBuilder DisableAuthorizationStorage() => Configure(options => options.DisableAuthorizationStorage = true); /// /// Disables sliding expiration. When using this option, refresh tokens /// are issued with a fixed expiration date: when they expire, a complete /// authorization flow must be started to retrieve a new refresh token. /// /// The . public OpenIddictServerBuilder DisableSlidingExpiration() => Configure(options => options.UseSlidingExpiration = false); /// /// Disables token storage, so that authorization code and /// refresh tokens are never stored and cannot be revoked. /// Using this option is generally NOT recommended. /// /// The . public OpenIddictServerBuilder DisableTokenStorage() => Configure(options => options.DisableTokenStorage = true); /// /// Allows processing authorization and token requests that specify scopes that have not /// been registered using or the scope manager. /// /// The . public OpenIddictServerBuilder DisableScopeValidation() => Configure(options => options.DisableScopeValidation = true); /// /// Enables the degraded mode. When the degraded mode is enabled, all the security checks that /// depend on the OpenIddict core managers are disabled. This option MUST be enabled with extreme /// caution and custom handlers MUST be registered to properly validate OpenID Connect requests. /// /// The . [EditorBrowsable(EditorBrowsableState.Advanced)] public OpenIddictServerBuilder EnableDegradedMode() => Configure(options => options.EnableDegradedMode = true); /// /// Disables endpoint permissions enforcement. Calling this method is NOT recommended, /// unless all the clients are first-party applications you own, control and fully trust. /// /// The . public OpenIddictServerBuilder IgnoreEndpointPermissions() => Configure(options => options.IgnoreEndpointPermissions = true); /// /// Disables grant type permissions enforcement. Calling this method is NOT recommended, /// unless all the clients are first-party applications you own, control and fully trust. /// /// The . public OpenIddictServerBuilder IgnoreGrantTypePermissions() => Configure(options => options.IgnoreGrantTypePermissions = true); /// /// Disables scope permissions enforcement. Calling this method is NOT recommended, /// unless all the clients are first-party applications you own, control and fully trust. /// /// The . public OpenIddictServerBuilder IgnoreScopePermissions() => Configure(options => options.IgnoreScopePermissions = true); /// /// Registers the specified claims as supported claims so /// they can be returned as part of the discovery document. /// /// The supported claims. /// The . public OpenIddictServerBuilder RegisterClaims([NotNull] params string[] claims) { if (claims == null) { throw new ArgumentNullException(nameof(claims)); } if (claims.Any(claim => string.IsNullOrEmpty(claim))) { throw new ArgumentException("Claims cannot be null or empty.", nameof(claims)); } return Configure(options => options.Claims.UnionWith(claims)); } /// /// Registers the specified scopes as supported scopes so /// they can be returned as part of the discovery document. /// /// The supported scopes. /// The . public OpenIddictServerBuilder RegisterScopes([NotNull] params string[] scopes) { if (scopes == null) { throw new ArgumentNullException(nameof(scopes)); } if (scopes.Any(scope => string.IsNullOrEmpty(scope))) { throw new ArgumentException("Scopes cannot be null or empty.", nameof(scopes)); } return Configure(options => options.Scopes.UnionWith(scopes)); } /// /// 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. /// While discouraged, null can be specified to issue tokens that never expire. /// /// The access token lifetime. /// The . public OpenIddictServerBuilder SetAccessTokenLifetime([CanBeNull] TimeSpan? lifetime) => Configure(options => options.AccessTokenLifetime = lifetime); /// /// Sets the authorization code lifetime, after which client applications /// are unable to send a grant_type=authorization_code token request. /// Using short-lived authorization codes is strongly recommended. /// While discouraged, null can be specified to issue codes that never expire. /// /// The authorization code lifetime. /// The . public OpenIddictServerBuilder SetAuthorizationCodeLifetime([CanBeNull] TimeSpan? lifetime) => Configure(options => options.AuthorizationCodeLifetime = lifetime); /// /// Sets the device code lifetime, after which client applications are unable to /// send a grant_type=urn:ietf:params:oauth:grant-type:device_code token request. /// Using short-lived device codes is strongly recommended. /// While discouraged, null can be specified to issue codes that never expire. /// /// The authorization code lifetime. /// The . public OpenIddictServerBuilder SetDeviceCodeLifetime([CanBeNull] TimeSpan? lifetime) => Configure(options => options.DeviceCodeLifetime = lifetime); /// /// Sets the identity token lifetime, after which client /// applications should refuse processing identity tokens. /// While discouraged, null can be specified to issue tokens that never expire. /// /// The identity token lifetime. /// The . public OpenIddictServerBuilder SetIdentityTokenLifetime([CanBeNull] TimeSpan? lifetime) => Configure(options => options.IdentityTokenLifetime = lifetime); /// /// Sets the refresh token lifetime, after which client applications must get /// a new authorization from the user. When sliding expiration is enabled, /// a new refresh token is always issued to the client application, /// which prolongs the validity period of the refresh token. /// While discouraged, null can be specified to issue tokens that never expire. /// /// The refresh token lifetime. /// The . public OpenIddictServerBuilder SetRefreshTokenLifetime([CanBeNull] TimeSpan? lifetime) => Configure(options => options.RefreshTokenLifetime = lifetime); /// /// Sets the user code lifetime, after which they'll no longer be considered valid. /// Using short-lived device codes is strongly recommended. /// While discouraged, null can be specified to issue codes that never expire. /// /// The authorization code lifetime. /// The . public OpenIddictServerBuilder SetUserCodeLifetime([CanBeNull] TimeSpan? lifetime) => Configure(options => options.UserCodeLifetime = lifetime); /// /// Sets the issuer address, which is used as the base address /// for the endpoint URIs returned from the discovery endpoint. /// /// The issuer address. /// The . public OpenIddictServerBuilder SetIssuer([NotNull] Uri address) { if (address == null) { throw new ArgumentNullException(nameof(address)); } return Configure(options => options.Issuer = address); } /// /// Updates the token validation parameters using the specified delegate. /// /// The configuration delegate. /// The . public OpenIddictServerBuilder SetTokenValidationParameters([NotNull] Action configuration) { if (configuration == null) { throw new ArgumentNullException(nameof(configuration)); } return Configure(options => configuration(options.TokenValidationParameters)); } /// /// Configures OpenIddict to use reference tokens, so that access 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 access tokens or when immediate revocation is desired. /// /// The . public OpenIddictServerBuilder UseReferenceAccessTokens() => Configure(options => options.UseReferenceAccessTokens = true); /// /// Configures OpenIddict to use rolling refresh tokens. When this option is enabled, /// a new refresh token is always issued for each refresh token request (and the previous /// one is automatically revoked unless token revocation was explicitly disabled). /// /// The . public OpenIddictServerBuilder UseRollingTokens() => Configure(options => options.UseRollingTokens = true); /// /// Determines whether the specified object is equal to the current object. /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. [EditorBrowsable(EditorBrowsableState.Never)] public override bool Equals([CanBeNull] object obj) => base.Equals(obj); /// /// Serves as the default hash function. /// /// A hash code for the current object. [EditorBrowsable(EditorBrowsableState.Never)] public override int GetHashCode() => base.GetHashCode(); /// /// Returns a string that represents the current object. /// /// A string that represents the current object. [EditorBrowsable(EditorBrowsableState.Never)] public override string ToString() => base.ToString(); } }