Browse Source

Introduce a new built-in RequireEmbeddedWebServerEnabled filter

pull/2109/head
Kévin Chalet 2 years ago
parent
commit
67c993a0de
  1. 1
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationExtensions.cs
  2. 22
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlerFilters.cs
  3. 52
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.cs

1
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationExtensions.cs

@ -59,6 +59,7 @@ public static class OpenIddictClientSystemIntegrationExtensions
// Register the built-in filters used by the default OpenIddict client system integration event handlers. // Register the built-in filters used by the default OpenIddict client system integration event handlers.
builder.Services.TryAddSingleton<RequireAuthenticationNonce>(); builder.Services.TryAddSingleton<RequireAuthenticationNonce>();
builder.Services.TryAddSingleton<RequireEmbeddedWebServerEnabled>();
builder.Services.TryAddSingleton<RequireHttpListenerContext>(); builder.Services.TryAddSingleton<RequireHttpListenerContext>();
builder.Services.TryAddSingleton<RequireInteractiveSession>(); builder.Services.TryAddSingleton<RequireInteractiveSession>();
builder.Services.TryAddSingleton<RequireProtocolActivation>(); builder.Services.TryAddSingleton<RequireProtocolActivation>();

22
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlerFilters.cs

@ -34,6 +34,28 @@ public static class OpenIddictClientSystemIntegrationHandlerFilters
} }
} }
/// <summary>
/// Represents a filter that excludes the associated handlers if the embedded web server was not enabled.
/// </summary>
public sealed class RequireEmbeddedWebServerEnabled : IOpenIddictClientHandlerFilter<BaseContext>
{
private readonly IOptionsMonitor<OpenIddictClientSystemIntegrationOptions> _options;
public RequireEmbeddedWebServerEnabled(IOptionsMonitor<OpenIddictClientSystemIntegrationOptions> 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.EnableEmbeddedWebServer is true);
}
}
/// <summary> /// <summary>
/// Represents a filter that excludes the associated handlers if no HTTP listener context can be found. /// Represents a filter that excludes the associated handlers if no HTTP listener context can be found.
/// </summary> /// </summary>

52
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.cs

@ -1723,6 +1723,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessChallengeContext>() = OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessChallengeContext>()
.AddFilter<RequireInteractiveSession>() .AddFilter<RequireInteractiveSession>()
.AddFilter<RequireInteractiveGrantType>() .AddFilter<RequireInteractiveGrantType>()
.AddFilter<RequireEmbeddedWebServerEnabled>()
// Note: only apply the dynamic port replacement logic if the callback request // Note: only apply the dynamic port replacement logic if the callback request
// is going to be received by the system browser to ensure it doesn't apply to // is going to be received by the system browser to ensure it doesn't apply to
// challenge demands handled via a web authentication broker. // challenge demands handled via a web authentication broker.
@ -1743,10 +1744,11 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
// If the redirect_uri uses a loopback host/IP as the authority and doesn't include a non-default port, // If the redirect_uri uses a loopback host/IP as the authority and doesn't include a non-default port,
// determine whether the embedded web server is running: if so, override the port in the redirect_uri // determine whether the embedded web server is running: if so, override the port in the redirect_uri
// by the port used by the embedded web server (guaranteed to be running if a value is returned). // by the port used by the embedded web server (guaranteed to be running if a value is returned).
if (!string.IsNullOrEmpty(context.RedirectUri) && if (!string.IsNullOrEmpty(context.RedirectUri) &&
Uri.TryCreate(context.RedirectUri, UriKind.Absolute, out Uri? uri) && Uri.TryCreate(context.RedirectUri, UriKind.Absolute, out Uri? uri) &&
string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) && string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) &&
uri.IsLoopback && uri.IsDefaultPort && uri.IsLoopback &&
uri.IsDefaultPort &&
await _listener.GetEmbeddedServerPortAsync(context.CancellationToken) is int port) await _listener.GetEmbeddedServerPortAsync(context.CancellationToken) is int port)
{ {
var builder = new UriBuilder(context.RedirectUri) var builder = new UriBuilder(context.RedirectUri)
@ -1848,9 +1850,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
// contain a value that prevents response_mode=query from being used (token/id_token). // contain a value that prevents response_mode=query from being used (token/id_token).
({ Count: > 0 } client, { Count: > 0 } server) when ({ Count: > 0 } client, { Count: > 0 } server) when
IsAuthenticationMode(OpenIddictClientSystemIntegrationAuthenticationMode.SystemBrowser) && IsAuthenticationMode(OpenIddictClientSystemIntegrationAuthenticationMode.SystemBrowser) &&
Uri.TryCreate(context.RedirectUri, UriKind.Absolute, out Uri? uri) && !await IsEmbeddedWebServerRedirectUriAsync() &&
!string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) &&
uri.Port != await _listener.GetEmbeddedServerPortAsync(context.CancellationToken) &&
client.Contains(ResponseModes.Fragment) && server.Contains(ResponseModes.Fragment) && client.Contains(ResponseModes.Fragment) && server.Contains(ResponseModes.Fragment) &&
(types.Contains(ResponseTypes.IdToken) || types.Contains(ResponseTypes.Token)) (types.Contains(ResponseTypes.IdToken) || types.Contains(ResponseTypes.Token))
=> ResponseModes.Fragment, => ResponseModes.Fragment,
@ -1861,10 +1861,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
// response_mode=query from being used (token/id_token). // response_mode=query from being used (token/id_token).
({ Count: > 0 } client, { Count: 0 }) when ({ Count: > 0 } client, { Count: 0 }) when
IsAuthenticationMode(OpenIddictClientSystemIntegrationAuthenticationMode.SystemBrowser) && IsAuthenticationMode(OpenIddictClientSystemIntegrationAuthenticationMode.SystemBrowser) &&
Uri.TryCreate(context.RedirectUri, UriKind.Absolute, out Uri? uri) && !await IsEmbeddedWebServerRedirectUriAsync() &&
!string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) && client.Contains(ResponseModes.Fragment) &&
uri.Port != await _listener.GetEmbeddedServerPortAsync(context.CancellationToken) &&
client.Contains(ResponseModes.Fragment) &&
(types.Contains(ResponseTypes.IdToken) || types.Contains(ResponseTypes.Token)) (types.Contains(ResponseTypes.IdToken) || types.Contains(ResponseTypes.Token))
=> ResponseModes.Fragment, => ResponseModes.Fragment,
@ -1873,10 +1871,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
// types contain a value that prevents response_mode=query from being used (token/id_token). // types contain a value that prevents response_mode=query from being used (token/id_token).
({ Count: > 0 } client, { Count: > 0 } server) when ({ Count: > 0 } client, { Count: > 0 } server) when
IsAuthenticationMode(OpenIddictClientSystemIntegrationAuthenticationMode.SystemBrowser) && IsAuthenticationMode(OpenIddictClientSystemIntegrationAuthenticationMode.SystemBrowser) &&
Uri.TryCreate(context.RedirectUri, UriKind.Absolute, out Uri? uri) && await IsEmbeddedWebServerRedirectUriAsync() &&
string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) &&
uri.IsLoopback &&
uri.Port == await _listener.GetEmbeddedServerPortAsync(context.CancellationToken) &&
client.Contains(ResponseModes.FormPost) && server.Contains(ResponseModes.FormPost) && client.Contains(ResponseModes.FormPost) && server.Contains(ResponseModes.FormPost) &&
(types.Contains(ResponseTypes.IdToken) || types.Contains(ResponseTypes.Token)) (types.Contains(ResponseTypes.IdToken) || types.Contains(ResponseTypes.Token))
=> ResponseModes.FormPost, => ResponseModes.FormPost,
@ -1887,10 +1882,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
// a value that prevents response_mode=query from being used (token/id_token). // a value that prevents response_mode=query from being used (token/id_token).
({ Count: > 0 } client, { Count: 0 }) when ({ Count: > 0 } client, { Count: 0 }) when
IsAuthenticationMode(OpenIddictClientSystemIntegrationAuthenticationMode.SystemBrowser) && IsAuthenticationMode(OpenIddictClientSystemIntegrationAuthenticationMode.SystemBrowser) &&
Uri.TryCreate(context.RedirectUri, UriKind.Absolute, out Uri? uri) && await IsEmbeddedWebServerRedirectUriAsync() &&
string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) &&
uri.IsLoopback &&
uri.Port == await _listener.GetEmbeddedServerPortAsync(context.CancellationToken) &&
client.Contains(ResponseModes.FormPost) && client.Contains(ResponseModes.FormPost) &&
(types.Contains(ResponseTypes.IdToken) || types.Contains(ResponseTypes.Token)) (types.Contains(ResponseTypes.IdToken) || types.Contains(ResponseTypes.Token))
=> ResponseModes.FormPost, => ResponseModes.FormPost,
@ -1918,9 +1910,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
// server, if both the client and the server support response_mode=fragment, use it. // server, if both the client and the server support response_mode=fragment, use it.
({ Count: > 0 } client, { Count: > 0 } server) when ({ Count: > 0 } client, { Count: > 0 } server) when
IsAuthenticationMode(OpenIddictClientSystemIntegrationAuthenticationMode.SystemBrowser) && IsAuthenticationMode(OpenIddictClientSystemIntegrationAuthenticationMode.SystemBrowser) &&
Uri.TryCreate(context.RedirectUri, UriKind.Absolute, out Uri? uri) && !await IsEmbeddedWebServerRedirectUriAsync() &&
!string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) &&
uri.Port != await _listener.GetEmbeddedServerPortAsync(context.CancellationToken) &&
client.Contains(ResponseModes.Fragment) && server.Contains(ResponseModes.Fragment) client.Contains(ResponseModes.Fragment) && server.Contains(ResponseModes.Fragment)
=> ResponseModes.Fragment, => ResponseModes.Fragment,
@ -1928,10 +1918,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
// server, if both the client and the server support response_mode=form_post, use it. // server, if both the client and the server support response_mode=form_post, use it.
({ Count: > 0 } client, { Count: > 0 } server) when ({ Count: > 0 } client, { Count: > 0 } server) when
IsAuthenticationMode(OpenIddictClientSystemIntegrationAuthenticationMode.SystemBrowser) && IsAuthenticationMode(OpenIddictClientSystemIntegrationAuthenticationMode.SystemBrowser) &&
Uri.TryCreate(context.RedirectUri, UriKind.Absolute, out Uri? uri) && await IsEmbeddedWebServerRedirectUriAsync() &&
string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) &&
uri.IsLoopback &&
uri.Port == await _listener.GetEmbeddedServerPortAsync(context.CancellationToken) &&
client.Contains(ResponseModes.FormPost) && server.Contains(ResponseModes.FormPost) client.Contains(ResponseModes.FormPost) && server.Contains(ResponseModes.FormPost)
=> ResponseModes.FormPost, => ResponseModes.FormPost,
@ -1952,6 +1939,13 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
return mode == _options.CurrentValue.AuthenticationMode; return mode == _options.CurrentValue.AuthenticationMode;
} }
async ValueTask<bool> IsEmbeddedWebServerRedirectUriAsync()
=> _options.CurrentValue.EnableEmbeddedWebServer is true &&
Uri.TryCreate(context.RedirectUri, UriKind.Absolute, out Uri? uri) &&
string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) &&
uri.IsLoopback &&
uri.Port == await _listener.GetEmbeddedServerPortAsync(context.CancellationToken);
} }
} }
@ -2099,6 +2093,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
public static OpenIddictClientHandlerDescriptor Descriptor { get; } public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessSignOutContext>() = OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessSignOutContext>()
.AddFilter<RequireInteractiveSession>() .AddFilter<RequireInteractiveSession>()
.AddFilter<RequireEmbeddedWebServerEnabled>()
// Note: only apply the dynamic port replacement logic if the callback request // Note: only apply the dynamic port replacement logic if the callback request
// is going to be received by the system browser to ensure it doesn't apply to // is going to be received by the system browser to ensure it doesn't apply to
// sign-out demands handled via a web authentication broker are not affected. // sign-out demands handled via a web authentication broker are not affected.
@ -2119,10 +2114,11 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
// If the post_logout_redirect_uri uses a loopback host/IP as the authority and doesn't include a non-default port, // If the post_logout_redirect_uri uses a loopback host/IP as the authority and doesn't include a non-default port,
// determine whether the embedded web server is running: if so, override the port in the post_logout_redirect_uri // determine whether the embedded web server is running: if so, override the port in the post_logout_redirect_uri
// by the port used by the embedded web server (guaranteed to be running if a value is returned). // by the port used by the embedded web server (guaranteed to be running if a value is returned).
if (!string.IsNullOrEmpty(context.PostLogoutRedirectUri) && if (!string.IsNullOrEmpty(context.PostLogoutRedirectUri) &&
Uri.TryCreate(context.PostLogoutRedirectUri, UriKind.Absolute, out Uri? uri) && Uri.TryCreate(context.PostLogoutRedirectUri, UriKind.Absolute, out Uri? uri) &&
string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) && string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) &&
uri.IsLoopback && uri.IsDefaultPort && uri.IsLoopback &&
uri.IsDefaultPort &&
await _listener.GetEmbeddedServerPortAsync(context.CancellationToken) is int port) await _listener.GetEmbeddedServerPortAsync(context.CancellationToken) is int port)
{ {
var builder = new UriBuilder(context.PostLogoutRedirectUri) var builder = new UriBuilder(context.PostLogoutRedirectUri)

Loading…
Cancel
Save