/// <summary> /// Sets a property in the server transaction using the specified name and value. /// </summary> /// <typeparam name="TProperty">The type of the property.</typeparam> /// <param name="transaction">The server transaction.</param> /// <param name="name">The property name.</param> /// <param name="value">The property value.</param> /// <returns>The server transaction, so that calls can be easily chained.</returns> public static OpenIddictServerTransaction SetProperty <TProperty>( this OpenIddictServerTransaction transaction, string name, TProperty?value) where TProperty : class { if (transaction == null) { throw new ArgumentNullException(nameof(transaction)); } if (string.IsNullOrEmpty(name)) { throw new ArgumentException(SR.GetResourceString(SR.ID1105), nameof(name)); } if (value == null) { transaction.Properties.Remove(name); } else { transaction.Properties[name] = value; } return(transaction); }
/// <summary> /// Initializes a new OpenIddict message. /// </summary> /// <param name="parameters">The message parameters.</param> /// <remarks>Parameters with a null or empty key are always ignored.</remarks> public OpenIddictMessage(JsonElement parameters) { if (parameters.ValueKind != JsonValueKind.Object) { throw new ArgumentException(SR.GetResourceString(SR.ID0189), nameof(parameters)); } foreach (var parameter in parameters.EnumerateObject()) { // Ignore parameters whose name is null or empty. if (string.IsNullOrEmpty(parameter.Name)) { continue; } // While generally discouraged, JSON objects can contain multiple properties with // the same name. In this case, the last occurrence replaces the previous ones. if (HasParameter(parameter.Name)) { RemoveParameter(parameter.Name); } AddParameter(parameter.Name, parameter.Value); } }
/// <summary> /// Resolves the <see cref="IServiceProvider"/> instance from the OWIN context /// and creates a new instance of the <see cref="OpenIddictValidationOwinMiddleware"/> class, /// which is used to register <see cref="OpenIddictValidationOwinHandler"/> in the pipeline. /// </summary> /// <param name="context">The <see cref="IOwinContext"/>.</param> /// <returns> /// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation. /// </returns> public override Task Invoke(IOwinContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } var provider = context.Get <IServiceProvider>(typeof(IServiceProvider).FullName); if (provider is null) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0168)); } // Note: the Microsoft.Extensions.DependencyInjection container doesn't support resolving services // with arbitrary parameters, which prevents the validation OWIN middleware from being resolved directly // from the DI container, as the next middleware in the pipeline cannot be specified as a parameter. // To work around this limitation, the validation OWIN middleware is manually instantiated and invoked. var middleware = new OpenIddictValidationOwinMiddleware( next: Next, options: GetRequiredService <IOptionsMonitor <OpenIddictValidationOwinOptions> >(provider), dispatcher: GetRequiredService <IOpenIddictValidationDispatcher>(provider), factory: GetRequiredService <IOpenIddictValidationFactory>(provider)); return(middleware.Invoke(context));
/// <inheritdoc/> public async ValueTask HandleAsync(PrepareIntrospectionRequestContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } Debug.Assert(context.Request is not null, SR.GetResourceString(SR.ID4008)); // This handler only applies to System.Net.Http requests. If the HTTP request cannot be resolved, // this may indicate that the request was incorrectly processed by another client stack. var request = context.Transaction.GetHttpRequestMessage(); if (request is null) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0173)); } // If no client identifier was attached to the request, skip the following logic. if (string.IsNullOrEmpty(context.Request.ClientId)) { return; } var configuration = await context.Options.ConfigurationManager.GetConfigurationAsync(default) ??
/// <inheritdoc/> public void Configure(QuartzOptions options) { if (options is null) { throw new ArgumentNullException(nameof(options)); } options.AddJob <OpenIddictQuartzJob>(builder => { builder.StoreDurably() .WithIdentity(OpenIddictQuartzJob.Identity) .WithDescription(SR.GetResourceString(SR.ID8000)); }); options.AddTrigger(builder => { // Note: this trigger uses a quite long interval (1 hour), which means it may be potentially // never reached if the application is shut down or recycled. As such, this trigger is set up // to fire 2 minutes after the application starts to ensure it's executed at least once. builder.ForJob(OpenIddictQuartzJob.Identity) .WithSimpleSchedule(options => options.WithIntervalInHours(1).RepeatForever()) .WithDescription(SR.GetResourceString(SR.ID8001)) .StartAt(DateBuilder.FutureDate(2, IntervalUnit.Minute)); }); }
public void SetParameter_ThrowsAnExceptionForNullOrEmptyName(string name) { // Arrange var message = new OpenIddictMessage(); // Act and assert var exception = Assert.Throws <ArgumentException>(() => message.SetParameter(name, null)); Assert.Equal("name", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0190), exception.Message); }
public void Constructor_ThrowsAnExceptionForInvalidJsonElement() { // Arrange, act and assert var exception = Assert.Throws <ArgumentException>(delegate { return(new OpenIddictMessage(JsonSerializer.Deserialize <JsonElement>("[0,1,2,3]"))); }); Assert.Equal("parameters", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0189), exception.Message); }
public void GetUnnamedParameter_ThrowsAnExceptionForNegativeIndex() { // Arrange var parameter = new OpenIddictParameter(); // Act var exception = Assert.Throws <ArgumentOutOfRangeException>(() => parameter.GetUnnamedParameter(-1)); // Assert Assert.Equal("index", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0193), exception.Message); }
public void SetMaximumRefireCount_ThrowsAnExceptionForNegativeCount() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws <ArgumentOutOfRangeException>(() => builder.SetMaximumRefireCount(-1)); Assert.Equal("count", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID1278), exception.Message); }
public void SetMinimumTokenLifespan_ThrowsAnExceptionForNegativeLifespan() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws <ArgumentOutOfRangeException>(() => builder.SetMinimumTokenLifespan(TimeSpan.FromSeconds(-1))); Assert.Equal("lifespan", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID1279), exception.Message); }
/// <summary> /// Ensures that the authentication configuration is in a consistent and valid state. /// </summary> /// <param name="name">The name of the options instance to configure, if applicable.</param> /// <param name="options">The options instance to initialize.</param> public void PostConfigure(string name, AuthenticationOptions options) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (!TryValidate(options.SchemeMap, options.DefaultSignInScheme) || !TryValidate(options.SchemeMap, options.DefaultSignOutScheme)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID1164)); }
public void PostConfigure(string name, OpenIddictServerOwinOptions options) { if (options is null) { throw new ArgumentNullException(nameof(options)); } if (options.AuthenticationMode == AuthenticationMode.Active) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0119)); } }
public void TryGetNamedParameter_ThrowsAnExceptionForNullOrEmptyName(string name) { // Arrange var parameter = new OpenIddictParameter(); // Act var exception = Assert.Throws <ArgumentException>(() => parameter.TryGetNamedParameter(name, out _)); // Assert Assert.Equal("name", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0192), exception.Message); }
public void SetTokensCollectionName_ThrowsAnExceptionForNullOrEmptyCollectionName(string name) { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws <ArgumentException>(() => builder.SetTokensCollectionName(name)); Assert.Equal("name", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0261), exception.Message); }
/// <summary> /// Deserializes an <see cref="OpenIddictMessage"/> instance. /// </summary> /// <param name="reader">The JSON reader.</param> /// <param name="typeToConvert">The type of the deserialized instance.</param> /// <param name="options">The JSON serializer options.</param> /// <returns>The deserialized <see cref="OpenIddictMessage"/> instance.</returns> public override OpenIddictMessage Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (typeToConvert is null) { throw new ArgumentNullException(nameof(typeToConvert)); } using var document = JsonDocument.ParseValue(ref reader); return(typeToConvert == typeof(OpenIddictMessage) ? new OpenIddictMessage(document.RootElement.Clone()) : typeToConvert == typeof(OpenIddictRequest) ? new OpenIddictRequest(document.RootElement.Clone()) : typeToConvert == typeof(OpenIddictResponse) ? new OpenIddictResponse(document.RootElement.Clone()) : throw new ArgumentException(SR.GetResourceString(SR.ID0176), nameof(typeToConvert))); }
public void Constructor_ThrowsAnExceptionForNullOrEmptyParameterNames(string name) { // Arrange, act and assert var exception = Assert.Throws <ArgumentException>(delegate { return(new OpenIddictMessage(new[] { new KeyValuePair <string, OpenIddictParameter>(name, "Fabrikam") })); }); Assert.Equal("name", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0190), exception.Message); }
/// <summary> /// Finds all the base types that matches the specified generic type definition. /// </summary> /// <param name="type">The type to introspect.</param> /// <param name="definition">The generic type definition.</param> /// <returns>A <see cref="Type"/> instance if the base type was found, <c>null</c> otherwise.</returns> public static IEnumerable <Type> FindGenericBaseTypes(Type type, Type definition) { if (type is null) { throw new ArgumentNullException(nameof(type)); } if (definition is null) { throw new ArgumentNullException(nameof(definition)); } if (!definition.IsGenericTypeDefinition) { throw new ArgumentException(SR.GetResourceString(SR.ID0263), nameof(definition)); } if (definition.IsInterface) { foreach (var contract in type.GetInterfaces()) { if (!contract.IsGenericType && !contract.IsConstructedGenericType) { continue; } if (contract.GetGenericTypeDefinition() == definition) { yield return(contract); } } } else { for (var candidate = type; candidate is not null; candidate = candidate.BaseType) { if (!candidate.IsGenericType && !candidate.IsConstructedGenericType) { continue; } if (candidate.GetGenericTypeDefinition() == definition) { yield return(candidate); } } } }
public void Constructor_ThrowsAnExceptionForDuplicateParameters() { // Arrange, act and assert var exception = Assert.Throws <ArgumentException>(delegate { return(new OpenIddictMessage(new[] { new KeyValuePair <string, OpenIddictParameter>("parameter", "Fabrikam"), new KeyValuePair <string, OpenIddictParameter>("parameter", "Contoso") })); }); Assert.Equal("name", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID0191), exception.Message); }
public void UseDbContext_ThrowsAnExceptionForInvalidType() { // Arrange var services = CreateServices(); var builder = CreateBuilder(services); // Act and assert var exception = Assert.Throws <ArgumentException>(delegate { return(builder.UseDbContext(typeof(object))); }); Assert.Equal("type", exception.ParamName); Assert.StartsWith(SR.GetResourceString(SR.ID1231), exception.Message); }
public void Read_ThrowsAnExceptionForUnsupportedType(Type type) { // Arrange var converter = new OpenIddictConverter(); // Act and assert var exception = Assert.Throws <ArgumentException>(delegate { var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(@"{""name"":""value""}")); return(converter.Read(ref reader, type, options: null)); }); Assert.StartsWith(SR.GetResourceString(SR.ID0176), exception.Message); Assert.Equal("typeToConvert", exception.ParamName); }
/// <summary> /// Registers the OpenIddict server Quartz.NET integration in the DI container. /// </summary> /// <param name="builder">The services builder used by OpenIddict to register new services.</param> /// <remarks>This extension can be safely called multiple times.</remarks> /// <returns>The <see cref="OpenIddictServerQuartzBuilder"/>.</returns> public static OpenIddictServerQuartzBuilder UseQuartz(this OpenIddictServerBuilder builder) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } // Warning: the AddQuartz() method is deliberately not used as it's not idempotent. // Calling it at this point may override user-defined services (e.g Quartz DI support). builder.Services.TryAddTransient <OpenIddictServerQuartzJob>(); // To ensure this method can be safely called multiple times, the job details // of the OpenIddict server job are only added if no existing IJobDetail instance // pointing to OpenIddictServerQuartzJob was already registered in the DI container. if (!builder.Services.Any(descriptor => descriptor.ServiceType == typeof(IJobDetail) && descriptor.ImplementationInstance is IJobDetail job && job.Key.Equals(OpenIddictServerQuartzJob.Identity))) { builder.Services.AddSingleton( JobBuilder.Create <OpenIddictServerQuartzJob>() .StoreDurably() .WithIdentity(OpenIddictServerQuartzJob.Identity) .WithDescription(SR.GetResourceString(SR.ID9000)) .Build()); } // To ensure this method can be safely called multiple times, the trigger details // of the OpenIddict server job are only added if no existing ITrigger instance // pointing to OpenIddictServerQuartzJob was already registered in the DI container. if (!builder.Services.Any(descriptor => descriptor.ServiceType == typeof(ITrigger) && descriptor.ImplementationInstance is ITrigger trigger && trigger.JobKey.Equals(OpenIddictServerQuartzJob.Identity))) { // Note: this trigger uses a quite long interval (1 hour), which means it may be // potentially never reached if the application is shut down or recycled. As such, // this trigger is set up to fire immediately to ensure it's executed at least once. builder.Services.AddSingleton( TriggerBuilder.Create() .ForJob(OpenIddictServerQuartzJob.Identity) .WithSimpleSchedule(options => options.WithIntervalInHours(1).RepeatForever()) .WithDescription(SR.GetResourceString(SR.ID9001)) .StartNow() .Build()); } return(new OpenIddictServerQuartzBuilder(builder.Services)); }
/// <summary> /// Adds the type of a handler filter to the filters list. /// </summary> /// <param name="type">The event handler filter type.</param> /// <returns>The builder instance, so that calls can be easily chained.</returns> public Builder <TContext> AddFilter(Type type) { if (type is null) { throw new ArgumentNullException(nameof(type)); } if (!typeof(IOpenIddictServerHandlerFilter <>).MakeGenericType(typeof(TContext)).IsAssignableFrom(type)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0104)); } _filters.Add(type); return(this); }
/// <summary> /// Registers the OpenIddict server handler in the global authentication options. /// </summary> /// <param name="options">The options instance to initialize.</param> public void Configure(AuthenticationOptions options) { if (options is null) { throw new ArgumentNullException(nameof(options)); } // If a handler was already registered and the type doesn't correspond to the OpenIddict handler, throw an exception. if (options.SchemeMap.TryGetValue(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, out var builder) && builder.HandlerType != typeof(OpenIddictServerAspNetCoreHandler)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0108)); } options.AddScheme <OpenIddictServerAspNetCoreHandler>( OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, displayName: null); }
/// <summary> /// Sets the service descriptor. /// </summary> /// <param name="descriptor">The service descriptor.</param> /// <returns>The builder instance, so that calls can be easily chained.</returns> public Builder <TContext> SetServiceDescriptor(ServiceDescriptor descriptor) { if (descriptor is null) { throw new ArgumentNullException(nameof(descriptor)); } var type = descriptor.ServiceType; if (!typeof(IOpenIddictServerHandler <>).MakeGenericType(typeof(TContext)).IsAssignableFrom(type)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0104)); } _descriptor = descriptor; return(this); }
/// <inheritdoc/> public ValueTask HandleAsync(HandleConfigurationResponseContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } // The issuer returned in the discovery document must exactly match the URL used to access it. // See https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation. var issuer = (string?)context.Response[Metadata.Issuer]; if (string.IsNullOrEmpty(issuer)) { context.Reject( error: Errors.ServerError, description: SR.GetResourceString(SR.ID2096)); return(default);
static string?GetKeyIdentifier(SecurityKey key) { // When no key identifier can be retrieved from the security keys, a value is automatically // inferred from the hexadecimal representation of the certificate thumbprint (SHA-1) // when the key is bound to a X.509 certificate or from the public part of the signing key. if (key is X509SecurityKey x509SecurityKey) { return(x509SecurityKey.Certificate.Thumbprint); } if (key is RsaSecurityKey rsaSecurityKey) { // Note: if the RSA parameters are not attached to the signing key, // extract them by calling ExportParameters on the RSA instance. var parameters = rsaSecurityKey.Parameters; if (parameters.Modulus is null) { parameters = rsaSecurityKey.Rsa.ExportParameters(includePrivateParameters: false); Debug.Assert(parameters.Modulus is not null, SR.GetResourceString(SR.ID4003)); } // Only use the 40 first chars of the base64url-encoded modulus. var identifier = Base64UrlEncoder.Encode(parameters.Modulus); return(identifier.Substring(0, Math.Min(identifier.Length, 40)).ToUpperInvariant()); } #if SUPPORTS_ECDSA if (key is ECDsaSecurityKey ecsdaSecurityKey) { // Extract the ECDSA parameters from the signing credentials. var parameters = ecsdaSecurityKey.ECDsa.ExportParameters(includePrivateParameters: false); Debug.Assert(parameters.Q.X is not null, SR.GetResourceString(SR.ID4004)); // Only use the 40 first chars of the base64url-encoded X coordinate. var identifier = Base64UrlEncoder.Encode(parameters.Q.X); return(identifier.Substring(0, Math.Min(identifier.Length, 40)).ToUpperInvariant()); } #endif return(null); }
/// <inheritdoc/> public async ValueTask HandleAsync(PrepareIntrospectionRequestContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } Debug.Assert(context.Request != null, SR.GetResourceString(SR.ID5008)); // This handler only applies to System.Net.Http requests. If the HTTP request cannot be resolved, // this may indicate that the request was incorrectly processed by another client stack. var request = context.Transaction.GetHttpRequestMessage(); if (request == null) { throw new InvalidOperationException(SR.GetResourceString(SR.ID1172)); } var configuration = await context.Options.ConfigurationManager.GetConfigurationAsync(default) ??
/// <inheritdoc/> public ValueTask HandleAsync(ApplyLogoutResponseContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } // This handler only applies to ASP.NET Core requests. If the HTTP context cannot be resolved, // this may indicate that the request was incorrectly processed by another server stack. var response = context.Transaction.GetHttpRequest()?.HttpContext.Response; if (response is null) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0114)); } if (string.IsNullOrEmpty(context.PostLogoutRedirectUri)) { return(default);
/// <summary> /// Imports the properties set on the specified descriptor. /// </summary> /// <param name="descriptor">The existing descriptor properties are copied from.</param> /// <remarks>All the properties previously set on this instance are automatically replaced.</remarks> /// <returns>The builder instance, so that calls can be easily chained.</returns> public Builder <TContext> Import(OpenIddictServerHandlerDescriptor descriptor) { if (descriptor is null) { throw new ArgumentNullException(nameof(descriptor)); } if (descriptor.ContextType != typeof(TContext)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0284)); } _descriptor = descriptor.ServiceDescriptor; _filters.Clear(); _filters.AddRange(descriptor.FilterTypes); _order = descriptor.Order; _type = descriptor.Type; return(this); }
/// <summary> /// Retrieves a property value from the server transaction using the specified name. /// </summary> /// <typeparam name="TProperty">The type of the property.</typeparam> /// <param name="transaction">The server transaction.</param> /// <param name="name">The property name.</param> /// <returns>The property value or <c>null</c> if it couldn't be found.</returns> public static TProperty?GetProperty <TProperty>( this OpenIddictServerTransaction transaction, string name) where TProperty : class { if (transaction == null) { throw new ArgumentNullException(nameof(transaction)); } if (string.IsNullOrEmpty(name)) { throw new ArgumentException(SR.GetResourceString(SR.ID1105), nameof(name)); } if (transaction.Properties.TryGetValue(name, out var property) && property is TProperty result) { return(result); } return(null); }