Browse Source

Update the OWIN hosts to automatically set SuppressFormsAuthenticationRedirect to true on challenges

pull/1472/head
Kévin Chalet 4 years ago
parent
commit
69e9a7e7c3
  1. 1
      src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.Authentication.cs
  2. 69
      src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs
  3. 1
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Authentication.cs
  4. 2
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Device.cs
  5. 2
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Discovery.cs
  6. 1
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Exchange.cs
  7. 1
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Introspection.cs
  8. 1
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Revocation.cs
  9. 1
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Session.cs
  10. 1
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Userinfo.cs
  11. 69
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs
  12. 73
      src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlers.cs

1
src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.Authentication.cs

@ -34,6 +34,7 @@ public static partial class OpenIddictClientOwinHandlers
*/
AttachHttpResponseCode<ApplyRedirectionResponseContext>.Descriptor,
AttachOwinResponseChallenge<ApplyRedirectionResponseContext>.Descriptor,
SuppressFormsAuthenticationRedirect<ApplyRedirectionResponseContext>.Descriptor,
AttachCacheControlHeader<ApplyRedirectionResponseContext>.Descriptor,
ProcessPassthroughErrorResponse<ApplyRedirectionResponseContext, RequireRedirectionEndpointPassthroughEnabled>.Descriptor,
ProcessLocalErrorResponse<ApplyRedirectionResponseContext>.Descriptor);

69
src/OpenIddict.Client.Owin/OpenIddictClientOwinHandlers.cs

@ -716,6 +716,73 @@ public static partial class OpenIddictClientOwinHandlers
}
}
/// <summary>
/// Contains the logic responsible for suppressing the redirection applied by FormsAuthenticationModule, if necessary.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
/// </summary>
public class SuppressFormsAuthenticationRedirect<TContext> : IOpenIddictClientHandler<TContext> where TContext : BaseRequestContext
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireOwinRequest>()
.UseSingletonHandler<SuppressFormsAuthenticationRedirect<TContext>>()
.SetOrder(AttachOwinResponseChallenge<TContext>.Descriptor.Order + 1_000)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(TContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
// This handler only applies to OWIN requests. If The OWIN request cannot be resolved,
// this may indicate that the request was incorrectly processed by another server stack.
var response = context.Transaction.GetOwinRequest()?.Context.Response ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0120));
// Similarly to the automatic authentication mode used by OWIN authentication middleware,
// the ASP.NET FormsAuthentication module aggressively intercepts 401 responses even if
// the request has already been fully handled by another component (like OpenIddict).
// To prevent that, this handler is responsible for suppressing the redirection enforced
// by FormsAuthenticationModule when the status code was set to 401 (the only status code
// used by the FormsAuthenticationModule) and the OWIN application is hosted on SystemWeb.
if (response.StatusCode is 401)
{
TrySuppressFormsAuthenticationRedirect(response.Environment);
}
return default;
static void TrySuppressFormsAuthenticationRedirect(IDictionary<string, object> environment)
{
// Note: the OWIN host cannot depend on the OWIN SystemWeb package but a direct access
// to the underlying ASP.NET 4.x context is required to be able to disable the redirection
// enforced by the FormsAuthentication module. To work around that, the HttpContextBase
// instance is resolved from the OWIN environment and SuppressFormsAuthenticationRedirect
// is set to true using a dynamic runtime resolution (that uses reflection under the hood).
if (environment.TryGetValue("System.Web.HttpContextBase", out dynamic context))
{
try
{
// Note: the SuppressFormsAuthenticationRedirect property was introduced in ASP.NET 4.5
// and thus should always be present, as OpenIddict requires targeting ASP.NET >= 4.6.1.
context.Response.SuppressFormsAuthenticationRedirect = true;
}
catch
{
}
}
}
}
}
/// <summary>
/// Contains the logic responsible for attaching the appropriate HTTP response cache headers.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
@ -729,7 +796,7 @@ public static partial class OpenIddictClientOwinHandlers
= OpenIddictClientHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireOwinRequest>()
.UseSingletonHandler<AttachCacheControlHeader<TContext>>()
.SetOrder(AttachOwinResponseChallenge<TContext>.Descriptor.Order + 1_000)
.SetOrder(SuppressFormsAuthenticationRedirect<TContext>.Descriptor.Order + 1_000)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();

1
src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Authentication.cs

@ -45,6 +45,7 @@ public static partial class OpenIddictServerOwinHandlers
RemoveCachedRequest.Descriptor,
AttachHttpResponseCode<ApplyAuthorizationResponseContext>.Descriptor,
AttachOwinResponseChallenge<ApplyAuthorizationResponseContext>.Descriptor,
SuppressFormsAuthenticationRedirect<ApplyAuthorizationResponseContext>.Descriptor,
AttachCacheControlHeader<ApplyAuthorizationResponseContext>.Descriptor,
ProcessFormPostResponse.Descriptor,
ProcessQueryResponse.Descriptor,

2
src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Device.cs

@ -26,6 +26,7 @@ public static partial class OpenIddictServerOwinHandlers
*/
AttachHttpResponseCode<ApplyDeviceResponseContext>.Descriptor,
AttachOwinResponseChallenge<ApplyDeviceResponseContext>.Descriptor,
SuppressFormsAuthenticationRedirect<ApplyDeviceResponseContext>.Descriptor,
AttachCacheControlHeader<ApplyDeviceResponseContext>.Descriptor,
AttachWwwAuthenticateHeader<ApplyDeviceResponseContext>.Descriptor,
ProcessJsonResponse<ApplyDeviceResponseContext>.Descriptor,
@ -45,6 +46,7 @@ public static partial class OpenIddictServerOwinHandlers
*/
AttachHttpResponseCode<ApplyVerificationResponseContext>.Descriptor,
AttachOwinResponseChallenge<ApplyVerificationResponseContext>.Descriptor,
SuppressFormsAuthenticationRedirect<ApplyVerificationResponseContext>.Descriptor,
AttachCacheControlHeader<ApplyVerificationResponseContext>.Descriptor,
ProcessHostRedirectionResponse.Descriptor,
ProcessPassthroughErrorResponse<ApplyVerificationResponseContext, RequireVerificationEndpointPassthroughEnabled>.Descriptor,

2
src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Discovery.cs

@ -23,6 +23,7 @@ public static partial class OpenIddictServerOwinHandlers
*/
AttachHttpResponseCode<ApplyConfigurationResponseContext>.Descriptor,
AttachOwinResponseChallenge<ApplyConfigurationResponseContext>.Descriptor,
SuppressFormsAuthenticationRedirect<ApplyConfigurationResponseContext>.Descriptor,
AttachWwwAuthenticateHeader<ApplyConfigurationResponseContext>.Descriptor,
ProcessJsonResponse<ApplyConfigurationResponseContext>.Descriptor,
@ -36,6 +37,7 @@ public static partial class OpenIddictServerOwinHandlers
*/
AttachHttpResponseCode<ApplyCryptographyResponseContext>.Descriptor,
AttachOwinResponseChallenge<ApplyCryptographyResponseContext>.Descriptor,
SuppressFormsAuthenticationRedirect<ApplyCryptographyResponseContext>.Descriptor,
AttachWwwAuthenticateHeader<ApplyCryptographyResponseContext>.Descriptor,
ProcessJsonResponse<ApplyCryptographyResponseContext>.Descriptor);
}

1
src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Exchange.cs

@ -29,6 +29,7 @@ public static partial class OpenIddictServerOwinHandlers
*/
AttachHttpResponseCode<ApplyTokenResponseContext>.Descriptor,
AttachOwinResponseChallenge<ApplyTokenResponseContext>.Descriptor,
SuppressFormsAuthenticationRedirect<ApplyTokenResponseContext>.Descriptor,
AttachCacheControlHeader<ApplyTokenResponseContext>.Descriptor,
AttachWwwAuthenticateHeader<ApplyTokenResponseContext>.Descriptor,
ProcessJsonResponse<ApplyTokenResponseContext>.Descriptor);

1
src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Introspection.cs

@ -24,6 +24,7 @@ public static partial class OpenIddictServerOwinHandlers
*/
AttachHttpResponseCode<ApplyIntrospectionResponseContext>.Descriptor,
AttachOwinResponseChallenge<ApplyIntrospectionResponseContext>.Descriptor,
SuppressFormsAuthenticationRedirect<ApplyIntrospectionResponseContext>.Descriptor,
AttachWwwAuthenticateHeader<ApplyIntrospectionResponseContext>.Descriptor,
ProcessJsonResponse<ApplyIntrospectionResponseContext>.Descriptor);
}

1
src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Revocation.cs

@ -24,6 +24,7 @@ public static partial class OpenIddictServerOwinHandlers
*/
AttachHttpResponseCode<ApplyRevocationResponseContext>.Descriptor,
AttachOwinResponseChallenge<ApplyRevocationResponseContext>.Descriptor,
SuppressFormsAuthenticationRedirect<ApplyRevocationResponseContext>.Descriptor,
AttachCacheControlHeader<ApplyRevocationResponseContext>.Descriptor,
AttachWwwAuthenticateHeader<ApplyRevocationResponseContext>.Descriptor,
ProcessJsonResponse<ApplyRevocationResponseContext>.Descriptor);

1
src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Session.cs

@ -43,6 +43,7 @@ public static partial class OpenIddictServerOwinHandlers
RemoveCachedRequest.Descriptor,
AttachHttpResponseCode<ApplyLogoutResponseContext>.Descriptor,
AttachOwinResponseChallenge<ApplyLogoutResponseContext>.Descriptor,
SuppressFormsAuthenticationRedirect<ApplyLogoutResponseContext>.Descriptor,
AttachCacheControlHeader<ApplyLogoutResponseContext>.Descriptor,
ProcessHostRedirectionResponse.Descriptor,
ProcessPassthroughErrorResponse<ApplyLogoutResponseContext, RequireLogoutEndpointPassthroughEnabled>.Descriptor,

1
src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Userinfo.cs

@ -29,6 +29,7 @@ public static partial class OpenIddictServerOwinHandlers
*/
AttachHttpResponseCode<ApplyUserinfoResponseContext>.Descriptor,
AttachOwinResponseChallenge<ApplyUserinfoResponseContext>.Descriptor,
SuppressFormsAuthenticationRedirect<ApplyUserinfoResponseContext>.Descriptor,
AttachWwwAuthenticateHeader<ApplyUserinfoResponseContext>.Descriptor,
ProcessChallengeErrorResponse<ApplyUserinfoResponseContext>.Descriptor,
ProcessJsonResponse<ApplyUserinfoResponseContext>.Descriptor);

69
src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs

@ -1046,6 +1046,73 @@ public static partial class OpenIddictServerOwinHandlers
}
}
/// <summary>
/// Contains the logic responsible for suppressing the redirection applied by FormsAuthenticationModule, if necessary.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
/// </summary>
public class SuppressFormsAuthenticationRedirect<TContext> : IOpenIddictServerHandler<TContext> where TContext : BaseRequestContext
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireOwinRequest>()
.UseSingletonHandler<SuppressFormsAuthenticationRedirect<TContext>>()
.SetOrder(AttachOwinResponseChallenge<TContext>.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(TContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
// This handler only applies to OWIN requests. If The OWIN request cannot be resolved,
// this may indicate that the request was incorrectly processed by another server stack.
var response = context.Transaction.GetOwinRequest()?.Context.Response ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0120));
// Similarly to the automatic authentication mode used by OWIN authentication middleware,
// the ASP.NET FormsAuthentication module aggressively intercepts 401 responses even if
// the request has already been fully handled by another component (like OpenIddict).
// To prevent that, this handler is responsible for suppressing the redirection enforced
// by FormsAuthenticationModule when the status code was set to 401 (the only status code
// used by the FormsAuthenticationModule) and the OWIN application is hosted on SystemWeb.
if (response.StatusCode is 401)
{
TrySuppressFormsAuthenticationRedirect(response.Environment);
}
return default;
static void TrySuppressFormsAuthenticationRedirect(IDictionary<string, object> environment)
{
// Note: the OWIN host cannot depend on the OWIN SystemWeb package but a direct access
// to the underlying ASP.NET 4.x context is required to be able to disable the redirection
// enforced by the FormsAuthentication module. To work around that, the HttpContextBase
// instance is resolved from the OWIN environment and SuppressFormsAuthenticationRedirect
// is set to true using a dynamic runtime resolution (that uses reflection under the hood).
if (environment.TryGetValue("System.Web.HttpContextBase", out dynamic context))
{
try
{
// Note: the SuppressFormsAuthenticationRedirect property was introduced in ASP.NET 4.5
// and thus should always be present, as OpenIddict requires targeting ASP.NET >= 4.6.1.
context.Response.SuppressFormsAuthenticationRedirect = true;
}
catch
{
}
}
}
}
}
/// <summary>
/// Contains the logic responsible for attaching the appropriate HTTP response cache headers.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
@ -1059,7 +1126,7 @@ public static partial class OpenIddictServerOwinHandlers
= OpenIddictServerHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireOwinRequest>()
.UseSingletonHandler<AttachCacheControlHeader<TContext>>()
.SetOrder(AttachOwinResponseChallenge<TContext>.Descriptor.Order + 1_000)
.SetOrder(SuppressFormsAuthenticationRedirect<TContext>.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();

73
src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlers.cs

@ -8,8 +8,6 @@ using System.Collections.Immutable;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Owin;
@ -44,12 +42,14 @@ public static partial class OpenIddictValidationOwinHandlers
*/
AttachHttpResponseCode<ProcessChallengeContext>.Descriptor,
AttachOwinResponseChallenge<ProcessChallengeContext>.Descriptor,
SuppressFormsAuthenticationRedirect<ProcessChallengeContext>.Descriptor,
AttachCacheControlHeader<ProcessChallengeContext>.Descriptor,
AttachWwwAuthenticateHeader<ProcessChallengeContext>.Descriptor,
ProcessChallengeErrorResponse<ProcessChallengeContext>.Descriptor,
AttachHttpResponseCode<ProcessErrorContext>.Descriptor,
AttachOwinResponseChallenge<ProcessErrorContext>.Descriptor,
SuppressFormsAuthenticationRedirect<ProcessErrorContext>.Descriptor,
AttachCacheControlHeader<ProcessErrorContext>.Descriptor,
AttachWwwAuthenticateHeader<ProcessErrorContext>.Descriptor,
ProcessChallengeErrorResponse<ProcessErrorContext>.Descriptor,
@ -424,6 +424,73 @@ public static partial class OpenIddictValidationOwinHandlers
}
}
/// <summary>
/// Contains the logic responsible for suppressing the redirection applied by FormsAuthenticationModule, if necessary.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
/// </summary>
public class SuppressFormsAuthenticationRedirect<TContext> : IOpenIddictValidationHandler<TContext> where TContext : BaseRequestContext
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictValidationHandlerDescriptor Descriptor { get; }
= OpenIddictValidationHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireOwinRequest>()
.UseSingletonHandler<SuppressFormsAuthenticationRedirect<TContext>>()
.SetOrder(AttachOwinResponseChallenge<TContext>.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(TContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
// This handler only applies to OWIN requests. If The OWIN request cannot be resolved,
// this may indicate that the request was incorrectly processed by another server stack.
var response = context.Transaction.GetOwinRequest()?.Context.Response ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0120));
// Similarly to the automatic authentication mode used by OWIN authentication middleware,
// the ASP.NET FormsAuthentication module aggressively intercepts 401 responses even if
// the request has already been fully handled by another component (like OpenIddict).
// To prevent that, this handler is responsible for suppressing the redirection enforced
// by FormsAuthenticationModule when the status code was set to 401 (the only status code
// used by the FormsAuthenticationModule) and the OWIN application is hosted on SystemWeb.
if (response.StatusCode is 401)
{
TrySuppressFormsAuthenticationRedirect(response.Environment);
}
return default;
static void TrySuppressFormsAuthenticationRedirect(IDictionary<string, object> environment)
{
// Note: the OWIN host cannot depend on the OWIN SystemWeb package but a direct access
// to the underlying ASP.NET 4.x context is required to be able to disable the redirection
// enforced by the FormsAuthentication module. To work around that, the HttpContextBase
// instance is resolved from the OWIN environment and SuppressFormsAuthenticationRedirect
// is set to true using a dynamic runtime resolution (that uses reflection under the hood).
if (environment.TryGetValue("System.Web.HttpContextBase", out dynamic context))
{
try
{
// Note: the SuppressFormsAuthenticationRedirect property was introduced in ASP.NET 4.5
// and thus should always be present, as OpenIddict requires targeting ASP.NET >= 4.6.1.
context.Response.SuppressFormsAuthenticationRedirect = true;
}
catch
{
}
}
}
}
}
/// <summary>
/// Contains the logic responsible for attaching the appropriate HTTP response cache headers.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
@ -437,7 +504,7 @@ public static partial class OpenIddictValidationOwinHandlers
= OpenIddictValidationHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireOwinRequest>()
.UseSingletonHandler<AttachCacheControlHeader<TContext>>()
.SetOrder(AttachOwinResponseChallenge<TContext>.Descriptor.Order + 1_000)
.SetOrder(SuppressFormsAuthenticationRedirect<TContext>.Descriptor.Order + 1_000)
.SetType(OpenIddictValidationHandlerType.BuiltIn)
.Build();

Loading…
Cancel
Save