Skip to content

Commit

Permalink
Update the OpenIddict validation ASP.NET Core and OWIN hosts to allow…
Browse files Browse the repository at this point in the history
… controlling access token extraction
  • Loading branch information
kevinchalet committed May 14, 2024
1 parent 5d0e410 commit d657bdf
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,30 @@ public OpenIddictValidationAspNetCoreBuilder Configure(Action<OpenIddictValidati
return this;
}

/// <summary>
/// Prevents OpenIddict from extracting access tokens from the standard "Authorization" header.
/// </summary>
/// <remarks>
/// Disabling access token extraction from the "Authorization" header is NOT recommended.
/// </remarks>
/// <returns>The <see cref="OpenIddictValidationAspNetCoreBuilder"/> instance.</returns>
public OpenIddictValidationAspNetCoreBuilder DisableAccessTokenExtractionFromAuthorizationHeader()
=> Configure(options => options.DisableAccessTokenExtractionFromAuthorizationHeader = true);

/// <summary>
/// Prevents OpenIddict from extracting access tokens from the standard "access_token" body form parameter.
/// </summary>
/// <returns>The <see cref="OpenIddictValidationAspNetCoreBuilder"/> instance.</returns>
public OpenIddictValidationAspNetCoreBuilder DisableAccessTokenExtractionFromBodyForm()
=> Configure(options => options.DisableAccessTokenExtractionFromBodyForm = true);

/// <summary>
/// Prevents OpenIddict from extracting access tokens from the standard "access_token" query string parameter.
/// </summary>
/// <returns>The <see cref="OpenIddictValidationAspNetCoreBuilder"/> instance.</returns>
public OpenIddictValidationAspNetCoreBuilder DisableAccessTokenExtractionFromQueryString()
=> Configure(options => options.DisableAccessTokenExtractionFromQueryString = true);

/// <summary>
/// Sets the realm returned to the caller as part of the WWW-Authenticate header.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ public static OpenIddictValidationAspNetCoreBuilder UseAspNetCore(this OpenIddic
builder.Services.TryAdd(OpenIddictValidationAspNetCoreHandlers.DefaultHandlers.Select(descriptor => descriptor.ServiceDescriptor));

// Register the built-in filters used by the default OpenIddict ASP.NET Core validation event handlers.
builder.Services.TryAddSingleton<RequireAccessTokenExtractionFromAuthorizationHeaderEnabled>();
builder.Services.TryAddSingleton<RequireAccessTokenExtractionFromBodyFormEnabled>();
builder.Services.TryAddSingleton<RequireAccessTokenExtractionFromQueryStringEnabled>();
builder.Services.TryAddSingleton<RequireHttpRequest>();

// Register the option initializer used by the OpenIddict ASP.NET Core validation integration services.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

using System.ComponentModel;
using Microsoft.AspNetCore;
using Microsoft.Extensions.Options;

namespace OpenIddict.Validation.AspNetCore;

Expand All @@ -15,6 +16,75 @@ namespace OpenIddict.Validation.AspNetCore;
[EditorBrowsable(EditorBrowsableState.Advanced)]
public static class OpenIddictValidationAspNetCoreHandlerFilters
{
/// <summary>
/// Represents a filter that excludes the associated handlers if
/// access token extraction from the Authorization header was disabled.
/// </summary>
public sealed class RequireAccessTokenExtractionFromAuthorizationHeaderEnabled : IOpenIddictValidationHandlerFilter<BaseContext>
{
private readonly IOptionsMonitor<OpenIddictValidationAspNetCoreOptions> _options;

public RequireAccessTokenExtractionFromAuthorizationHeaderEnabled(IOptionsMonitor<OpenIddictValidationAspNetCoreOptions> options)
=> _options = options ?? throw new ArgumentNullException(nameof(options));

/// <inheritdoc/>
public ValueTask<bool> IsActiveAsync(BaseContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}

return new(!_options.CurrentValue.DisableAccessTokenExtractionFromAuthorizationHeader);
}
}

/// <summary>
/// Represents a filter that excludes the associated handlers if access token
/// extraction from the "access_token" body form parameter was disabled.
/// </summary>
public sealed class RequireAccessTokenExtractionFromBodyFormEnabled : IOpenIddictValidationHandlerFilter<BaseContext>
{
private readonly IOptionsMonitor<OpenIddictValidationAspNetCoreOptions> _options;

public RequireAccessTokenExtractionFromBodyFormEnabled(IOptionsMonitor<OpenIddictValidationAspNetCoreOptions> options)
=> _options = options ?? throw new ArgumentNullException(nameof(options));

/// <inheritdoc/>
public ValueTask<bool> IsActiveAsync(BaseContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}

return new(!_options.CurrentValue.DisableAccessTokenExtractionFromBodyForm);
}
}

/// <summary>
/// Represents a filter that excludes the associated handlers if access token
/// extraction from the "access_token" query string parameter was disabled.
/// </summary>
public sealed class RequireAccessTokenExtractionFromQueryStringEnabled : IOpenIddictValidationHandlerFilter<BaseContext>
{
private readonly IOptionsMonitor<OpenIddictValidationAspNetCoreOptions> _options;

public RequireAccessTokenExtractionFromQueryStringEnabled(IOptionsMonitor<OpenIddictValidationAspNetCoreOptions> options)
=> _options = options ?? throw new ArgumentNullException(nameof(options));

/// <inheritdoc/>
public ValueTask<bool> IsActiveAsync(BaseContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}

return new(!_options.CurrentValue.DisableAccessTokenExtractionFromQueryString);
}
}

/// <summary>
/// Represents a filter that excludes the associated handlers if no ASP.NET Core request can be found.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ public sealed class ExtractAccessTokenFromAuthorizationHeader : IOpenIddictValid
= OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireHttpRequest>()
.AddFilter<RequireAccessTokenExtracted>()
.AddFilter<RequireAccessTokenExtractionFromAuthorizationHeaderEnabled>()
.UseSingletonHandler<ExtractAccessTokenFromAuthorizationHeader>()
.SetOrder(EvaluateValidatedTokens.Descriptor.Order + 500)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
Expand Down Expand Up @@ -233,6 +234,7 @@ public sealed class ExtractAccessTokenFromBodyForm : IOpenIddictValidationHandle
= OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireHttpRequest>()
.AddFilter<RequireAccessTokenExtracted>()
.AddFilter<RequireAccessTokenExtractionFromBodyFormEnabled>()
.UseSingletonHandler<ExtractAccessTokenFromBodyForm>()
.SetOrder(ExtractAccessTokenFromAuthorizationHeader.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
Expand Down Expand Up @@ -288,6 +290,7 @@ public sealed class ExtractAccessTokenFromQueryString : IOpenIddictValidationHan
= OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireHttpRequest>()
.AddFilter<RequireAccessTokenExtracted>()
.AddFilter<RequireAccessTokenExtractionFromQueryStringEnabled>()
.UseSingletonHandler<ExtractAccessTokenFromQueryString>()
.SetOrder(ExtractAccessTokenFromBodyForm.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,27 @@ namespace OpenIddict.Validation.AspNetCore;
/// </summary>
public sealed class OpenIddictValidationAspNetCoreOptions : AuthenticationSchemeOptions
{
/// <summary>
/// Gets or sets a boolean indicating whether the built-in logic extracting
/// access tokens from the standard "Authorization" header should be disabled.
/// </summary>
/// <remarks>
/// Disabling access token extraction from the "Authorization" header is NOT recommended.
/// </remarks>
public bool DisableAccessTokenExtractionFromAuthorizationHeader { get; set; }

/// <summary>
/// Gets or sets a boolean indicating whether the built-in logic extracting access
/// tokens from the standard "access_token" body form parameter should be disabled.
/// </summary>
public bool DisableAccessTokenExtractionFromBodyForm { get; set; }

/// <summary>
/// Gets or sets a boolean indicating whether the built-in logic extracting access
/// tokens from the standard "access_token" query string parameter should be disabled.
/// </summary>
public bool DisableAccessTokenExtractionFromQueryString { get; set; }

/// <summary>
/// Gets or sets the optional "realm" value returned to the caller as part of the WWW-Authenticate header.
/// </summary>
Expand Down
24 changes: 24 additions & 0 deletions src/OpenIddict.Validation.Owin/OpenIddictValidationOwinBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,30 @@ public OpenIddictValidationOwinBuilder Configure(Action<OpenIddictValidationOwin
return this;
}

/// <summary>
/// Prevents OpenIddict from extracting access tokens from the standard "Authorization" header.
/// </summary>
/// <remarks>
/// Disabling access token extraction from the "Authorization" header is NOT recommended.
/// </remarks>
/// <returns>The <see cref="OpenIddictValidationOwinBuilder"/> instance.</returns>
public OpenIddictValidationOwinBuilder DisableAccessTokenExtractionFromAuthorizationHeader()
=> Configure(options => options.DisableAccessTokenExtractionFromAuthorizationHeader = true);

/// <summary>
/// Prevents OpenIddict from extracting access tokens from the standard "access_token" body form parameter.
/// </summary>
/// <returns>The <see cref="OpenIddictValidationOwinBuilder"/> instance.</returns>
public OpenIddictValidationOwinBuilder DisableAccessTokenExtractionFromBodyForm()
=> Configure(options => options.DisableAccessTokenExtractionFromBodyForm = true);

/// <summary>
/// Prevents OpenIddict from extracting access tokens from the standard "access_token" query string parameter.
/// </summary>
/// <returns>The <see cref="OpenIddictValidationOwinBuilder"/> instance.</returns>
public OpenIddictValidationOwinBuilder DisableAccessTokenExtractionFromQueryString()
=> Configure(options => options.DisableAccessTokenExtractionFromQueryString = true);

/// <summary>
/// Configures the OpenIddict validation OWIN integration to use active authentication.
/// When using active authentication, the principal resolved from the access token is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ public static OpenIddictValidationOwinBuilder UseOwin(this OpenIddictValidationB
builder.Services.TryAdd(OpenIddictValidationOwinHandlers.DefaultHandlers.Select(descriptor => descriptor.ServiceDescriptor));

// Register the built-in filters used by the default OpenIddict OWIN validation event handlers.
builder.Services.TryAddSingleton<RequireAccessTokenExtractionFromAuthorizationHeaderEnabled>();
builder.Services.TryAddSingleton<RequireAccessTokenExtractionFromBodyFormEnabled>();
builder.Services.TryAddSingleton<RequireAccessTokenExtractionFromQueryStringEnabled>();
builder.Services.TryAddSingleton<RequireOwinRequest>();

// Register the option initializers used by the OpenIddict OWIN validation integration services.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* the license and the contributors participating to this project.
*/

using Microsoft.Extensions.Options;
using Owin;

namespace OpenIddict.Validation.Owin;
Expand All @@ -13,6 +14,75 @@ namespace OpenIddict.Validation.Owin;
/// </summary>
public static class OpenIddictValidationOwinHandlerFilters
{
/// <summary>
/// Represents a filter that excludes the associated handlers if
/// access token extraction from the Authorization header was disabled.
/// </summary>
public sealed class RequireAccessTokenExtractionFromAuthorizationHeaderEnabled : IOpenIddictValidationHandlerFilter<BaseContext>
{
private readonly IOptionsMonitor<OpenIddictValidationOwinOptions> _options;

public RequireAccessTokenExtractionFromAuthorizationHeaderEnabled(IOptionsMonitor<OpenIddictValidationOwinOptions> options)
=> _options = options ?? throw new ArgumentNullException(nameof(options));

/// <inheritdoc/>
public ValueTask<bool> IsActiveAsync(BaseContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}

return new(!_options.CurrentValue.DisableAccessTokenExtractionFromAuthorizationHeader);
}
}

/// <summary>
/// Represents a filter that excludes the associated handlers if access token
/// extraction from the "access_token" body form parameter was disabled.
/// </summary>
public sealed class RequireAccessTokenExtractionFromBodyFormEnabled : IOpenIddictValidationHandlerFilter<BaseContext>
{
private readonly IOptionsMonitor<OpenIddictValidationOwinOptions> _options;

public RequireAccessTokenExtractionFromBodyFormEnabled(IOptionsMonitor<OpenIddictValidationOwinOptions> options)
=> _options = options ?? throw new ArgumentNullException(nameof(options));

/// <inheritdoc/>
public ValueTask<bool> IsActiveAsync(BaseContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}

return new(!_options.CurrentValue.DisableAccessTokenExtractionFromBodyForm);
}
}

/// <summary>
/// Represents a filter that excludes the associated handlers if access token
/// extraction from the "access_token" query string parameter was disabled.
/// </summary>
public sealed class RequireAccessTokenExtractionFromQueryStringEnabled : IOpenIddictValidationHandlerFilter<BaseContext>
{
private readonly IOptionsMonitor<OpenIddictValidationOwinOptions> _options;

public RequireAccessTokenExtractionFromQueryStringEnabled(IOptionsMonitor<OpenIddictValidationOwinOptions> options)
=> _options = options ?? throw new ArgumentNullException(nameof(options));

/// <inheritdoc/>
public ValueTask<bool> IsActiveAsync(BaseContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}

return new(!_options.CurrentValue.DisableAccessTokenExtractionFromQueryString);
}
}

/// <summary>
/// Represents a filter that excludes the associated handlers if no OWIN request can be found.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public static partial class OpenIddictValidationOwinHandlers
/*
* Authentication processing:
*/
ValidateHostHeader.Descriptor,
ExtractAccessTokenFromAuthorizationHeader.Descriptor,
ExtractAccessTokenFromBodyForm.Descriptor,
ExtractAccessTokenFromQueryString.Descriptor,
Expand Down Expand Up @@ -181,6 +182,7 @@ public sealed class ExtractAccessTokenFromAuthorizationHeader : IOpenIddictValid
= OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireOwinRequest>()
.AddFilter<RequireAccessTokenExtracted>()
.AddFilter<RequireAccessTokenExtractionFromAuthorizationHeaderEnabled>()
.UseSingletonHandler<ExtractAccessTokenFromAuthorizationHeader>()
.SetOrder(EvaluateValidatedTokens.Descriptor.Order + 500)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
Expand Down Expand Up @@ -232,6 +234,7 @@ public sealed class ExtractAccessTokenFromBodyForm : IOpenIddictValidationHandle
= OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireOwinRequest>()
.AddFilter<RequireAccessTokenExtracted>()
.AddFilter<RequireAccessTokenExtractionFromBodyFormEnabled>()
.UseSingletonHandler<ExtractAccessTokenFromBodyForm>()
.SetOrder(ExtractAccessTokenFromAuthorizationHeader.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
Expand Down Expand Up @@ -288,6 +291,7 @@ public sealed class ExtractAccessTokenFromQueryString : IOpenIddictValidationHan
= OpenIddictValidationHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireOwinRequest>()
.AddFilter<RequireAccessTokenExtracted>()
.AddFilter<RequireAccessTokenExtractionFromQueryStringEnabled>()
.UseSingletonHandler<ExtractAccessTokenFromQueryString>()
.SetOrder(ExtractAccessTokenFromBodyForm.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
Expand Down
21 changes: 21 additions & 0 deletions src/OpenIddict.Validation.Owin/OpenIddictValidationOwinOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,27 @@ public OpenIddictValidationOwinOptions()
: base(OpenIddictValidationOwinDefaults.AuthenticationType)
=> AuthenticationMode = AuthenticationMode.Passive;

/// <summary>
/// Gets or sets a boolean indicating whether the built-in logic extracting
/// access tokens from the standard "Authorization" header should be disabled.
/// </summary>
/// <remarks>
/// Disabling access token extraction from the "Authorization" header is NOT recommended.
/// </remarks>
public bool DisableAccessTokenExtractionFromAuthorizationHeader { get; set; }

/// <summary>
/// Gets or sets a boolean indicating whether the built-in logic extracting access
/// tokens from the standard "access_token" body form parameter should be disabled.
/// </summary>
public bool DisableAccessTokenExtractionFromBodyForm { get; set; }

/// <summary>
/// Gets or sets a boolean indicating whether the built-in logic extracting access
/// tokens from the standard "access_token" query string parameter should be disabled.
/// </summary>
public bool DisableAccessTokenExtractionFromQueryString { get; set; }

/// <summary>
/// Gets or sets the optional "realm" value returned to the caller as part of the WWW-Authenticate header.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public void PostConfigure(string? name, OpenIddictValidationOptions options)
throw new ArgumentNullException(nameof(options));
}

if (options.ValidationType != OpenIddictValidationType.Direct)
if (options.ValidationType is not OpenIddictValidationType.Direct)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0170));
}
Expand Down

0 comments on commit d657bdf

Please sign in to comment.