Browse Source

Share ProcessStatusCodePagesErrorResponse/ProcessPassthroughErrorResponse/ProcessLocalErrorResponse between authentication and logout handlers

pull/838/head
Kévin Chalet 6 years ago
parent
commit
2628b5a88a
  1. 220
      src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Authentication.cs
  2. 272
      src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Session.cs
  3. 263
      src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs
  4. 149
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Authentication.cs
  5. 198
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.Session.cs
  6. 194
      src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs
  7. 2
      src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs
  8. 4
      src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs
  9. 2
      src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs
  10. 2
      src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs
  11. 2
      src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs
  12. 2
      src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs
  13. 2
      src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs

220
src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Authentication.cs

@ -13,13 +13,11 @@ using System.Text.Encodings.Web;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
using Newtonsoft.Json.Bson;
using Newtonsoft.Json.Linq;
@ -55,9 +53,9 @@ namespace OpenIddict.Server.AspNetCore
ProcessFormPostResponse.Descriptor,
ProcessQueryResponse.Descriptor,
ProcessFragmentResponse.Descriptor,
ProcessStatusCodePagesErrorResponse.Descriptor,
ProcessPassthroughErrorResponse.Descriptor,
ProcessLocalErrorResponse.Descriptor);
ProcessStatusCodePagesErrorResponse<ApplyAuthorizationResponseContext>.Descriptor,
ProcessPassthroughErrorResponse<ApplyAuthorizationResponseContext, RequireAuthorizationEndpointPassthroughEnabled>.Descriptor,
ProcessLocalErrorResponse<ApplyAuthorizationResponseContext>.Descriptor);
/// <summary>
/// Contains the logic responsible of restoring cached requests from the request_id, if specified.
@ -480,7 +478,7 @@ namespace OpenIddict.Server.AspNetCore
= OpenIddictServerHandlerDescriptor.CreateBuilder<ApplyAuthorizationResponseContext>()
.AddFilter<RequireHttpRequest>()
.UseSingletonHandler<ProcessFragmentResponse>()
.SetOrder(ProcessLocalErrorResponse.Descriptor.Order - 1_000)
.SetOrder(ProcessLocalErrorResponse<ApplyAuthorizationResponseContext>.Descriptor.Order - 1_000)
.Build();
/// <summary>
@ -547,216 +545,6 @@ namespace OpenIddict.Server.AspNetCore
}
}
}
/// <summary>
/// Contains the logic responsible of processing authorization responses that must be handled by another
/// middleware in the pipeline at a later stage (e.g an ASP.NET Core MVC action or a NancyFX module).
/// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core.
/// </summary>
public class ProcessPassthroughErrorResponse : IOpenIddictServerHandler<ApplyAuthorizationResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ApplyAuthorizationResponseContext>()
.AddFilter<RequireHttpRequest>()
.AddFilter<RequireErrorPassthroughEnabled>()
.AddFilter<RequireAuthorizationEndpointPassthroughEnabled>()
.UseSingletonHandler<ProcessPassthroughErrorResponse>()
.SetOrder(ProcessStatusCodePagesErrorResponse.Descriptor.Order - 1_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public ValueTask HandleAsync([NotNull] ApplyAuthorizationResponseContext context)
{
if (context == 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 == null)
{
throw new InvalidOperationException("The ASP.NET Core HTTP request cannot be resolved.");
}
if (string.IsNullOrEmpty(context.Response.Error) || !string.IsNullOrEmpty(context.RedirectUri))
{
return default;
}
// Apply a 400 status code by default.
response.StatusCode = 400;
context.SkipRequest();
return default;
}
}
/// <summary>
/// Contains the logic responsible of processing authorization responses handled by the status code pages middleware.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core.
/// </summary>
public class ProcessStatusCodePagesErrorResponse : IOpenIddictServerHandler<ApplyAuthorizationResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ApplyAuthorizationResponseContext>()
.AddFilter<RequireHttpRequest>()
.AddFilter<RequireStatusCodePagesIntegrationEnabled>()
.UseSingletonHandler<ProcessStatusCodePagesErrorResponse>()
.SetOrder(ProcessLocalErrorResponse.Descriptor.Order - 1_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public ValueTask HandleAsync([NotNull] ApplyAuthorizationResponseContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Response == null)
{
throw new InvalidOperationException("This handler cannot be invoked without a response attached.");
}
// 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 == null)
{
throw new InvalidOperationException("The ASP.NET Core HTTP request cannot be resolved.");
}
if (string.IsNullOrEmpty(context.Error) || !string.IsNullOrEmpty(context.RedirectUri))
{
return default;
}
// Determine if the status code pages middleware has been enabled for this request.
// If it was not registered or enabled, let the default OpenIddict server handlers render
// a default error page instead of delegating the rendering to the status code middleware.
var feature = response.HttpContext.Features.Get<IStatusCodePagesFeature>();
if (feature == null || !feature.Enabled)
{
return default;
}
// Replace the default status code to return a 400 response.
response.StatusCode = 400;
// Mark the request as fully handled to prevent the other OpenIddict server handlers
// from displaying the default error page and to allow the status code pages middleware
// to rewrite the response using the logic defined by the developer when registering it.
context.HandleRequest();
return default;
}
}
/// <summary>
/// Contains the logic responsible of processing authorization responses that must be returned as plain-text.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core.
/// </summary>
public class ProcessLocalErrorResponse : IOpenIddictServerHandler<ApplyAuthorizationResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ApplyAuthorizationResponseContext>()
.AddFilter<RequireHttpRequest>()
.UseSingletonHandler<ProcessLocalErrorResponse>()
.SetOrder(int.MaxValue - 100_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public async ValueTask HandleAsync([NotNull] ApplyAuthorizationResponseContext context)
{
if (context == 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 == null)
{
throw new InvalidOperationException("The ASP.NET Core HTTP request cannot be resolved.");
}
if (string.IsNullOrEmpty(context.Response.Error) || !string.IsNullOrEmpty(context.RedirectUri))
{
return;
}
// Don't return the state originally sent by the client application.
context.Response.State = null;
// Apply a 400 status code by default.
response.StatusCode = 400;
context.Logger.LogInformation("The authorization response was successfully returned " +
"as a plain-text document: {Response}.", context.Response);
using (var buffer = new MemoryStream())
using (var writer = new StreamWriter(buffer))
{
foreach (var parameter in context.Response.GetParameters())
{
// Ignore null or empty parameters, including JSON
// objects that can't be represented as strings.
var value = (string) parameter.Value;
if (string.IsNullOrEmpty(value))
{
continue;
}
writer.WriteLine("{0}:{1}", parameter.Key, value);
}
writer.Flush();
response.ContentLength = buffer.Length;
response.ContentType = "text/plain;charset=UTF-8";
response.Headers[HeaderNames.CacheControl] = "no-cache";
response.Headers[HeaderNames.Pragma] = "no-cache";
response.Headers[HeaderNames.Expires] = "Thu, 01 Jan 1970 00:00:00 GMT";
buffer.Seek(offset: 0, loc: SeekOrigin.Begin);
await buffer.CopyToAsync(response.Body, 4096, response.HttpContext.RequestAborted);
}
context.HandleRequest();
}
}
}
}
}

272
src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.Session.cs

@ -12,13 +12,11 @@ using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
using Newtonsoft.Json.Bson;
using Newtonsoft.Json.Linq;
@ -51,11 +49,11 @@ namespace OpenIddict.Server.AspNetCore
* Logout response processing:
*/
RemoveCachedRequest.Descriptor,
ProcessEmptyResponse.Descriptor,
ProcessQueryResponse.Descriptor,
ProcessStatusCodePagesErrorResponse.Descriptor,
ProcessPassthroughErrorResponse.Descriptor,
ProcessLocalErrorResponse.Descriptor);
ProcessStatusCodePagesErrorResponse<ApplyLogoutResponseContext>.Descriptor,
ProcessPassthroughErrorResponse<ApplyLogoutResponseContext, RequireLogoutEndpointPassthroughEnabled>.Descriptor,
ProcessLocalErrorResponse<ApplyLogoutResponseContext>.Descriptor,
ProcessEmptyResponse<ApplyLogoutResponseContext>.Descriptor);
/// <summary>
/// Contains the logic responsible of restoring cached requests from the request_id, if specified.
@ -301,56 +299,6 @@ namespace OpenIddict.Server.AspNetCore
}
}
/// <summary>
/// Contains the logic responsible of processing logout responses that don't specify a post_logout_redirect_uri.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core.
/// </summary>
public class ProcessEmptyResponse : IOpenIddictServerHandler<ApplyLogoutResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ApplyLogoutResponseContext>()
.AddFilter<RequireHttpRequest>()
.UseSingletonHandler<ProcessQueryResponse>()
.SetOrder(ProcessPassthroughErrorResponse.Descriptor.Order - 1_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public ValueTask HandleAsync([NotNull] ApplyLogoutResponseContext context)
{
if (context == 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 == null)
{
throw new InvalidOperationException("The ASP.NET Core HTTP request cannot be resolved.");
}
if (!string.IsNullOrEmpty(context.PostLogoutRedirectUri))
{
return default;
}
context.Logger.LogInformation("The logout response was successfully returned: {Response}.", response);
context.HandleRequest();
return default;
}
}
/// <summary>
/// Contains the logic responsible of processing logout responses.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core.
@ -364,7 +312,7 @@ namespace OpenIddict.Server.AspNetCore
= OpenIddictServerHandlerDescriptor.CreateBuilder<ApplyLogoutResponseContext>()
.AddFilter<RequireHttpRequest>()
.UseSingletonHandler<ProcessQueryResponse>()
.SetOrder(ProcessEmptyResponse.Descriptor.Order - 1_000)
.SetOrder(ProcessStatusCodePagesErrorResponse<ApplyLogoutResponseContext>.Descriptor.Order - 1_000)
.Build();
/// <summary>
@ -413,216 +361,6 @@ namespace OpenIddict.Server.AspNetCore
return default;
}
}
/// <summary>
/// Contains the logic responsible of processing logout responses that must be handled by another
/// middleware in the pipeline at a later stage (e.g an ASP.NET Core MVC action or a NancyFX module).
/// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core.
/// </summary>
public class ProcessPassthroughErrorResponse : IOpenIddictServerHandler<ApplyLogoutResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ApplyLogoutResponseContext>()
.AddFilter<RequireHttpRequest>()
.AddFilter<RequireErrorPassthroughEnabled>()
.AddFilter<RequireLogoutEndpointPassthroughEnabled>()
.UseSingletonHandler<ProcessPassthroughErrorResponse>()
.SetOrder(ProcessStatusCodePagesErrorResponse.Descriptor.Order - 1_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public ValueTask HandleAsync([NotNull] ApplyLogoutResponseContext context)
{
if (context == 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 == null)
{
throw new InvalidOperationException("The ASP.NET Core HTTP request cannot be resolved.");
}
if (string.IsNullOrEmpty(context.Response.Error) || !string.IsNullOrEmpty(context.PostLogoutRedirectUri))
{
return default;
}
// Apply a 400 status code by default.
response.StatusCode = 400;
context.SkipRequest();
return default;
}
}
/// <summary>
/// Contains the logic responsible of processing logout responses handled by the status code pages middleware.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core.
/// </summary>
public class ProcessStatusCodePagesErrorResponse : IOpenIddictServerHandler<ApplyLogoutResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ApplyLogoutResponseContext>()
.AddFilter<RequireHttpRequest>()
.AddFilter<RequireStatusCodePagesIntegrationEnabled>()
.UseSingletonHandler<ProcessStatusCodePagesErrorResponse>()
.SetOrder(ProcessLocalErrorResponse.Descriptor.Order - 1_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public ValueTask HandleAsync([NotNull] ApplyLogoutResponseContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Response == null)
{
throw new InvalidOperationException("This handler cannot be invoked without a response attached.");
}
// 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 == null)
{
throw new InvalidOperationException("The ASP.NET Core HTTP request cannot be resolved.");
}
if (string.IsNullOrEmpty(context.Error) || !string.IsNullOrEmpty(context.PostLogoutRedirectUri))
{
return default;
}
// Determine if the status code pages middleware has been enabled for this request.
// If it was not registered or enabled, let the default OpenIddict server handlers render
// a default error page instead of delegating the rendering to the status code middleware.
var feature = response.HttpContext.Features.Get<IStatusCodePagesFeature>();
if (feature == null || !feature.Enabled)
{
return default;
}
// Replace the default status code to return a 400 response.
response.StatusCode = 400;
// Mark the request as fully handled to prevent the other OpenIddict server handlers
// from displaying the default error page and to allow the status code pages middleware
// to rewrite the response using the logic defined by the developer when registering it.
context.HandleRequest();
return default;
}
}
/// <summary>
/// Contains the logic responsible of processing logout responses that must be returned as plain-text.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core.
/// </summary>
public class ProcessLocalErrorResponse : IOpenIddictServerHandler<ApplyLogoutResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ApplyLogoutResponseContext>()
.AddFilter<RequireHttpRequest>()
.UseSingletonHandler<ProcessLocalErrorResponse>()
.SetOrder(int.MaxValue - 100_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public async ValueTask HandleAsync([NotNull] ApplyLogoutResponseContext context)
{
if (context == 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 == null)
{
throw new InvalidOperationException("The ASP.NET Core HTTP request cannot be resolved.");
}
if (string.IsNullOrEmpty(context.Response.Error) || !string.IsNullOrEmpty(context.PostLogoutRedirectUri))
{
return;
}
// Don't return the state originally sent by the client application.
context.Response.State = null;
// Apply a 400 status code by default.
response.StatusCode = 400;
context.Logger.LogInformation("The logout response was successfully returned " +
"as a plain-text document: {Response}.", context.Response);
using (var buffer = new MemoryStream())
using (var writer = new StreamWriter(buffer))
{
foreach (var parameter in context.Response.GetParameters())
{
// Ignore null or empty parameters, including JSON
// objects that can't be represented as strings.
var value = (string) parameter.Value;
if (string.IsNullOrEmpty(value))
{
continue;
}
writer.WriteLine("{0}:{1}", parameter.Key, value);
}
writer.Flush();
response.ContentLength = buffer.Length;
response.ContentType = "text/plain;charset=UTF-8";
response.Headers[HeaderNames.CacheControl] = "no-cache";
response.Headers[HeaderNames.Pragma] = "no-cache";
response.Headers[HeaderNames.Expires] = "Thu, 01 Jan 1970 00:00:00 GMT";
buffer.Seek(offset: 0, loc: SeekOrigin.Begin);
await buffer.CopyToAsync(response.Body, 4096, response.HttpContext.RequestAborted);
}
context.HandleRequest();
}
}
}
}
}

263
src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs

@ -13,6 +13,7 @@ using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
@ -696,7 +697,7 @@ namespace OpenIddict.Server.AspNetCore
= OpenIddictServerHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireHttpRequest>()
.UseSingletonHandler<ProcessJsonResponse<TContext>>()
.SetOrder(int.MinValue + 100_000)
.SetOrder(ProcessPassthroughErrorResponse<TContext, IOpenIddictServerHandlerFilter<TContext>>.Descriptor.Order - 1_000)
.Build();
/// <summary>
@ -787,5 +788,265 @@ namespace OpenIddict.Server.AspNetCore
context.HandleRequest();
}
}
/// <summary>
/// Contains the logic responsible of processing OpenID Connect responses that must be handled by another
/// middleware in the pipeline at a later stage (e.g an ASP.NET Core MVC action or a NancyFX module).
/// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core.
/// </summary>
public class ProcessPassthroughErrorResponse<TContext, TFilter> : IOpenIddictServerHandler<TContext>
where TContext : BaseRequestContext
where TFilter : IOpenIddictServerHandlerFilter<TContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireHttpRequest>()
.AddFilter<RequireErrorPassthroughEnabled>()
.AddFilter<TFilter>()
.UseSingletonHandler<ProcessPassthroughErrorResponse<TContext, TFilter>>()
.SetOrder(ProcessStatusCodePagesErrorResponse<TContext>.Descriptor.Order - 1_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public ValueTask HandleAsync([NotNull] TContext context)
{
if (context == 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 == null)
{
throw new InvalidOperationException("The ASP.NET Core HTTP request cannot be resolved.");
}
if (string.IsNullOrEmpty(context.Response.Error))
{
return default;
}
// Apply a 400 status code by default.
response.StatusCode = 400;
context.SkipRequest();
return default;
}
}
/// <summary>
/// Contains the logic responsible of processing OpenID Connect responses handled by the status code pages middleware.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core.
/// </summary>
public class ProcessStatusCodePagesErrorResponse<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<RequireHttpRequest>()
.AddFilter<RequireStatusCodePagesIntegrationEnabled>()
.UseSingletonHandler<ProcessStatusCodePagesErrorResponse<TContext>>()
.SetOrder(ProcessLocalErrorResponse<TContext>.Descriptor.Order - 1_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public ValueTask HandleAsync([NotNull] TContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Response == null)
{
throw new InvalidOperationException("This handler cannot be invoked without a response attached.");
}
// 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 == null)
{
throw new InvalidOperationException("The ASP.NET Core HTTP request cannot be resolved.");
}
if (string.IsNullOrEmpty(context.Response.Error))
{
return default;
}
// Determine if the status code pages middleware has been enabled for this request.
// If it was not registered or enabled, let the default OpenIddict server handlers render
// a default error page instead of delegating the rendering to the status code middleware.
var feature = response.HttpContext.Features.Get<IStatusCodePagesFeature>();
if (feature == null || !feature.Enabled)
{
return default;
}
// Replace the default status code to return a 400 response.
response.StatusCode = 400;
// Mark the request as fully handled to prevent the other OpenIddict server handlers
// from displaying the default error page and to allow the status code pages middleware
// to rewrite the response using the logic defined by the developer when registering it.
context.HandleRequest();
return default;
}
}
/// <summary>
/// Contains the logic responsible of processing context responses that must be returned as plain-text.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core.
/// </summary>
public class ProcessLocalErrorResponse<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<RequireHttpRequest>()
.UseSingletonHandler<ProcessLocalErrorResponse<TContext>>()
.SetOrder(ProcessEmptyResponse<TContext>.Descriptor.Order - 1_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public async ValueTask HandleAsync([NotNull] TContext context)
{
if (context == 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 == null)
{
throw new InvalidOperationException("The ASP.NET Core HTTP request cannot be resolved.");
}
if (string.IsNullOrEmpty(context.Response.Error))
{
return;
}
// Don't return the state originally sent by the client application.
context.Response.State = null;
// Apply a 400 status code by default.
response.StatusCode = 400;
context.Logger.LogInformation("The authorization response was successfully returned " +
"as a plain-text document: {Response}.", context.Response);
using (var buffer = new MemoryStream())
using (var writer = new StreamWriter(buffer))
{
foreach (var parameter in context.Response.GetParameters())
{
// Ignore null or empty parameters, including JSON
// objects that can't be represented as strings.
var value = (string) parameter.Value;
if (string.IsNullOrEmpty(value))
{
continue;
}
writer.WriteLine("{0}:{1}", parameter.Key, value);
}
writer.Flush();
response.ContentLength = buffer.Length;
response.ContentType = "text/plain;charset=UTF-8";
response.Headers[HeaderNames.CacheControl] = "no-cache";
response.Headers[HeaderNames.Pragma] = "no-cache";
response.Headers[HeaderNames.Expires] = "Thu, 01 Jan 1970 00:00:00 GMT";
buffer.Seek(offset: 0, loc: SeekOrigin.Begin);
await buffer.CopyToAsync(response.Body, 4096, response.HttpContext.RequestAborted);
}
context.HandleRequest();
}
}
/// <summary>
/// Contains the logic responsible of processing OpenID Connect responses that don't specify any parameter.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core.
/// </summary>
public class ProcessEmptyResponse<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<RequireHttpRequest>()
.UseSingletonHandler<ProcessEmptyResponse<TContext>>()
.SetOrder(int.MaxValue - 100_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public ValueTask HandleAsync([NotNull] TContext context)
{
if (context == 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 == null)
{
throw new InvalidOperationException("The ASP.NET Core HTTP request cannot be resolved.");
}
context.Logger.LogInformation("The response was successfully returned as an empty 200 response.");
context.HandleRequest();
return default;
}
}
}
}

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

@ -53,8 +53,8 @@ namespace OpenIddict.Server.Owin
ProcessFormPostResponse.Descriptor,
ProcessQueryResponse.Descriptor,
ProcessFragmentResponse.Descriptor,
ProcessPassthroughErrorResponse.Descriptor,
ProcessLocalErrorResponse.Descriptor);
ProcessPassthroughErrorResponse<ApplyAuthorizationResponseContext, RequireAuthorizationEndpointPassthroughEnabled>.Descriptor,
ProcessLocalErrorResponse<ApplyAuthorizationResponseContext>.Descriptor);
/// <summary>
/// Contains the logic responsible of restoring cached requests from the request_id, if specified.
@ -473,7 +473,7 @@ namespace OpenIddict.Server.Owin
= OpenIddictServerHandlerDescriptor.CreateBuilder<ApplyAuthorizationResponseContext>()
.AddFilter<RequireOwinRequest>()
.UseSingletonHandler<ProcessFragmentResponse>()
.SetOrder(ProcessLocalErrorResponse.Descriptor.Order - 1_000)
.SetOrder(ProcessLocalErrorResponse<ApplyAuthorizationResponseContext>.Descriptor.Order - 1_000)
.Build();
/// <summary>
@ -540,149 +540,6 @@ namespace OpenIddict.Server.Owin
}
}
}
/// <summary>
/// Contains the logic responsible of processing authorization responses that must be handled by another
/// middleware in the pipeline at a later stage (e.g an ASP.NET MVC action or a NancyFX module).
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
/// </summary>
public class ProcessPassthroughErrorResponse : IOpenIddictServerHandler<ApplyAuthorizationResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ApplyAuthorizationResponseContext>()
.AddFilter<RequireOwinRequest>()
.AddFilter<RequireErrorPassthroughEnabled>()
.AddFilter<RequireAuthorizationEndpointPassthroughEnabled>()
.UseSingletonHandler<ProcessPassthroughErrorResponse>()
.SetOrder(ProcessLocalErrorResponse.Descriptor.Order - 1_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public ValueTask HandleAsync([NotNull] ApplyAuthorizationResponseContext context)
{
if (context == 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;
if (response == null)
{
throw new InvalidOperationException("The OWIN request cannot be resolved.");
}
if (string.IsNullOrEmpty(context.Response.Error) || !string.IsNullOrEmpty(context.RedirectUri))
{
return default;
}
// Don't return the state originally sent by the client application.
context.Response.State = null;
// Apply a 400 status code by default.
response.StatusCode = 400;
context.SkipRequest();
return default;
}
}
/// <summary>
/// Contains the logic responsible of processing authorization responses that must be returned as plain-text.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
/// </summary>
public class ProcessLocalErrorResponse : IOpenIddictServerHandler<ApplyAuthorizationResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ApplyAuthorizationResponseContext>()
.AddFilter<RequireOwinRequest>()
.UseSingletonHandler<ProcessLocalErrorResponse>()
.SetOrder(int.MaxValue - 100_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public async ValueTask HandleAsync([NotNull] ApplyAuthorizationResponseContext context)
{
if (context == 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;
if (response == null)
{
throw new InvalidOperationException("The OWIN request cannot be resolved.");
}
if (string.IsNullOrEmpty(context.Response.Error) || !string.IsNullOrEmpty(context.RedirectUri))
{
return;
}
// Don't return the state originally sent by the client application.
context.Response.State = null;
// Apply a 400 status code by default.
response.StatusCode = 400;
context.Logger.LogInformation("The authorization response was successfully returned " +
"as a plain-text document: {Response}.", context.Response);
using (var buffer = new MemoryStream())
using (var writer = new StreamWriter(buffer))
{
foreach (var parameter in context.Response.GetParameters())
{
// Ignore null or empty parameters, including JSON
// objects that can't be represented as strings.
var value = (string) parameter.Value;
if (string.IsNullOrEmpty(value))
{
continue;
}
writer.WriteLine("{0}:{1}", parameter.Key, value);
}
writer.Flush();
response.ContentLength = buffer.Length;
response.ContentType = "text/plain;charset=UTF-8";
response.Headers["Cache-Control"] = "no-cache";
response.Headers["Pragma"] = "no-cache";
response.Headers["Expires"] = "Thu, 01 Jan 1970 00:00:00 GMT";
buffer.Seek(offset: 0, loc: SeekOrigin.Begin);
await buffer.CopyToAsync(response.Body, 4096, response.Context.Request.CallCancelled);
}
context.HandleRequest();
}
}
}
}
}

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

@ -49,10 +49,10 @@ namespace OpenIddict.Server.Owin
* Logout response processing:
*/
RemoveCachedRequest.Descriptor,
ProcessEmptyResponse.Descriptor,
ProcessQueryResponse.Descriptor,
ProcessPassthroughErrorResponse.Descriptor,
ProcessLocalErrorResponse.Descriptor);
ProcessPassthroughErrorResponse<ApplyLogoutResponseContext, RequireLogoutEndpointPassthroughEnabled>.Descriptor,
ProcessLocalErrorResponse<ApplyLogoutResponseContext>.Descriptor,
ProcessEmptyResponse<ApplyLogoutResponseContext>.Descriptor);
/// <summary>
/// Contains the logic responsible of restoring cached requests from the request_id, if specified.
@ -293,56 +293,6 @@ namespace OpenIddict.Server.Owin
}
}
/// <summary>
/// Contains the logic responsible of processing logout responses that don't specify a post_logout_redirect_uri.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
/// </summary>
public class ProcessEmptyResponse : IOpenIddictServerHandler<ApplyLogoutResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ApplyLogoutResponseContext>()
.AddFilter<RequireOwinRequest>()
.UseSingletonHandler<ProcessQueryResponse>()
.SetOrder(ProcessPassthroughErrorResponse.Descriptor.Order - 1_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public ValueTask HandleAsync([NotNull] ApplyLogoutResponseContext context)
{
if (context == 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;
if (response == null)
{
throw new InvalidOperationException("The OWIN request cannot be resolved.");
}
if (!string.IsNullOrEmpty(context.PostLogoutRedirectUri))
{
return default;
}
context.Logger.LogInformation("The logout response was successfully returned: {Response}.", response);
context.HandleRequest();
return default;
}
}
/// <summary>
/// Contains the logic responsible of processing logout responses.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
@ -356,7 +306,7 @@ namespace OpenIddict.Server.Owin
= OpenIddictServerHandlerDescriptor.CreateBuilder<ApplyLogoutResponseContext>()
.AddFilter<RequireOwinRequest>()
.UseSingletonHandler<ProcessQueryResponse>()
.SetOrder(ProcessEmptyResponse.Descriptor.Order - 1_000)
.SetOrder(ProcessPassthroughErrorResponse<ApplyLogoutResponseContext, IOpenIddictServerHandlerFilter<ApplyLogoutResponseContext>>.Descriptor.Order - 1_000)
.Build();
/// <summary>
@ -405,146 +355,6 @@ namespace OpenIddict.Server.Owin
return default;
}
}
/// <summary>
/// Contains the logic responsible of processing logout responses that must be handled by another
/// middleware in the pipeline at a later stage (e.g an OWIN MVC action or a NancyFX module).
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
/// </summary>
public class ProcessPassthroughErrorResponse : IOpenIddictServerHandler<ApplyLogoutResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ApplyLogoutResponseContext>()
.AddFilter<RequireOwinRequest>()
.AddFilter<RequireErrorPassthroughEnabled>()
.AddFilter<RequireLogoutEndpointPassthroughEnabled>()
.UseSingletonHandler<ProcessPassthroughErrorResponse>()
.SetOrder(ProcessLocalErrorResponse.Descriptor.Order - 1_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public ValueTask HandleAsync([NotNull] ApplyLogoutResponseContext context)
{
if (context == 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;
if (response == null)
{
throw new InvalidOperationException("The OWIN request cannot be resolved.");
}
if (string.IsNullOrEmpty(context.Response.Error) || !string.IsNullOrEmpty(context.PostLogoutRedirectUri))
{
return default;
}
// Apply a 400 status code by default.
response.StatusCode = 400;
context.SkipRequest();
return default;
}
}
/// <summary>
/// Contains the logic responsible of processing logout responses that must be returned as plain-text.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
/// </summary>
public class ProcessLocalErrorResponse : IOpenIddictServerHandler<ApplyLogoutResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ApplyLogoutResponseContext>()
.AddFilter<RequireOwinRequest>()
.UseSingletonHandler<ProcessLocalErrorResponse>()
.SetOrder(int.MaxValue - 100_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public async ValueTask HandleAsync([NotNull] ApplyLogoutResponseContext context)
{
if (context == 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;
if (response == null)
{
throw new InvalidOperationException("The OWIN request cannot be resolved.");
}
if (string.IsNullOrEmpty(context.Response.Error) || !string.IsNullOrEmpty(context.PostLogoutRedirectUri))
{
return;
}
// Don't return the state originally sent by the client application.
context.Response.State = null;
// Apply a 400 status code by default.
response.StatusCode = 400;
context.Logger.LogInformation("The logout response was successfully returned " +
"as a plain-text document: {Response}.", context.Response);
using (var buffer = new MemoryStream())
using (var writer = new StreamWriter(buffer))
{
foreach (var parameter in context.Response.GetParameters())
{
// Ignore null or empty parameters, including JSON
// objects that can't be represented as strings.
var value = (string) parameter.Value;
if (string.IsNullOrEmpty(value))
{
continue;
}
writer.WriteLine("{0}:{1}", parameter.Key, value);
}
writer.Flush();
response.ContentLength = buffer.Length;
response.ContentType = "text/plain;charset=UTF-8";
response.Headers["Cache-Control"] = "no-cache";
response.Headers["Pragma"] = "no-cache";
response.Headers["Expires"] = "Thu, 01 Jan 1970 00:00:00 GMT";
buffer.Seek(offset: 0, loc: SeekOrigin.Begin);
await buffer.CopyToAsync(response.Body, 4096, response.Context.Request.CallCancelled);
}
context.HandleRequest();
}
}
}
}
}

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

@ -697,7 +697,7 @@ namespace OpenIddict.Server.Owin
= OpenIddictServerHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireOwinRequest>()
.UseSingletonHandler<ProcessJsonResponse<TContext>>()
.SetOrder(int.MinValue + 100_000)
.SetOrder(ProcessPassthroughErrorResponse<TContext, IOpenIddictServerHandlerFilter<TContext>>.Descriptor.Order - 1_000)
.Build();
/// <summary>
@ -756,5 +756,197 @@ namespace OpenIddict.Server.Owin
context.HandleRequest();
}
}
/// <summary>
/// Contains the logic responsible of processing OpenID Connect responses that must be handled by another
/// middleware in the pipeline at a later stage (e.g an ASP.NET MVC action or a NancyFX module).
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
/// </summary>
public class ProcessPassthroughErrorResponse<TContext, TFilter> : IOpenIddictServerHandler<TContext>
where TContext : BaseRequestContext
where TFilter : IOpenIddictServerHandlerFilter<TContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireOwinRequest>()
.AddFilter<RequireErrorPassthroughEnabled>()
.AddFilter<TFilter>()
.UseSingletonHandler<ProcessPassthroughErrorResponse<TContext, TFilter>>()
.SetOrder(ProcessLocalErrorResponse<TContext>.Descriptor.Order - 1_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public ValueTask HandleAsync([NotNull] TContext context)
{
if (context == 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;
if (response == null)
{
throw new InvalidOperationException("The OWIN request cannot be resolved.");
}
if (string.IsNullOrEmpty(context.Response.Error))
{
return default;
}
// Don't return the state originally sent by the client application.
context.Response.State = null;
// Apply a 400 status code by default.
response.StatusCode = 400;
context.SkipRequest();
return default;
}
}
/// <summary>
/// Contains the logic responsible of processing OpenID Connect responses that must be returned as plain-text.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
/// </summary>
public class ProcessLocalErrorResponse<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<ProcessLocalErrorResponse<TContext>>()
.SetOrder(ProcessEmptyResponse<TContext>.Descriptor.Order - 1_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public async ValueTask HandleAsync([NotNull] TContext context)
{
if (context == 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;
if (response == null)
{
throw new InvalidOperationException("The OWIN request cannot be resolved.");
}
if (string.IsNullOrEmpty(context.Response.Error))
{
return;
}
// Don't return the state originally sent by the client application.
context.Response.State = null;
// Apply a 400 status code by default.
response.StatusCode = 400;
context.Logger.LogInformation("The authorization response was successfully returned " +
"as a plain-text document: {Response}.", context.Response);
using (var buffer = new MemoryStream())
using (var writer = new StreamWriter(buffer))
{
foreach (var parameter in context.Response.GetParameters())
{
// Ignore null or empty parameters, including JSON
// objects that can't be represented as strings.
var value = (string) parameter.Value;
if (string.IsNullOrEmpty(value))
{
continue;
}
writer.WriteLine("{0}:{1}", parameter.Key, value);
}
writer.Flush();
response.ContentLength = buffer.Length;
response.ContentType = "text/plain;charset=UTF-8";
response.Headers["Cache-Control"] = "no-cache";
response.Headers["Pragma"] = "no-cache";
response.Headers["Expires"] = "Thu, 01 Jan 1970 00:00:00 GMT";
buffer.Seek(offset: 0, loc: SeekOrigin.Begin);
await buffer.CopyToAsync(response.Body, 4096, response.Context.Request.CallCancelled);
}
context.HandleRequest();
}
}
/// <summary>
/// Contains the logic responsible of processing OpenID Connect responses that don't specify any parameter.
/// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN.
/// </summary>
public class ProcessEmptyResponse<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<ProcessEmptyResponse<TContext>>()
.SetOrder(int.MaxValue - 100_000)
.Build();
/// <summary>
/// Processes the event.
/// </summary>
/// <param name="context">The context associated with the event to process.</param>
/// <returns>
/// A <see cref="ValueTask"/> that can be used to monitor the asynchronous operation.
/// </returns>
public ValueTask HandleAsync([NotNull] TContext context)
{
if (context == 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;
if (response == null)
{
throw new InvalidOperationException("The OWIN request cannot be resolved.");
}
context.Logger.LogInformation("The response was successfully returned as an empty 200 response.");
context.HandleRequest();
return default;
}
}
}
}

2
src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs

@ -367,7 +367,7 @@ namespace OpenIddict.Server
}
throw new InvalidOperationException(new StringBuilder()
.Append("The authorization response was not correctly applied. To apply authorization response, ")
.Append("The authorization response was not correctly applied. To apply authorization responses, ")
.Append("create a class implementing 'IOpenIddictServerHandler<ApplyAuthorizationResponseContext>' ")
.AppendLine("and register it using 'services.AddOpenIddict().AddServer().AddEventHandler()'.")
.ToString());

4
src/OpenIddict.Server/OpenIddictServerHandlers.Discovery.cs

@ -355,7 +355,7 @@ namespace OpenIddict.Server
}
throw new InvalidOperationException(new StringBuilder()
.Append("The configuration response was not correctly applied. To apply configuration response, ")
.Append("The configuration response was not correctly applied. To apply configuration responses, ")
.Append("create a class implementing 'IOpenIddictServerHandler<ApplyConfigurationResponseContext>' ")
.AppendLine("and register it using 'services.AddOpenIddict().AddServer().AddEventHandler()'.")
.ToString());
@ -1204,7 +1204,7 @@ namespace OpenIddict.Server
}
throw new InvalidOperationException(new StringBuilder()
.Append("The cryptography response was not correctly applied. To apply cryptography response, ")
.Append("The cryptography response was not correctly applied. To apply cryptography responses, ")
.Append("create a class implementing 'IOpenIddictServerHandler<ApplyCryptographyResponseContext>' ")
.AppendLine("and register it using 'services.AddOpenIddict().AddServer().AddEventHandler()'.")
.ToString());

2
src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs

@ -357,7 +357,7 @@ namespace OpenIddict.Server
}
throw new InvalidOperationException(new StringBuilder()
.Append("The token response was not correctly applied. To apply token response, ")
.Append("The token response was not correctly applied. To apply token responses, ")
.Append("create a class implementing 'IOpenIddictServerHandler<ApplyTokenResponseContext>' ")
.AppendLine("and register it using 'services.AddOpenIddict().AddServer().AddEventHandler()'.")
.ToString());

2
src/OpenIddict.Server/OpenIddictServerHandlers.Introspection.cs

@ -372,7 +372,7 @@ namespace OpenIddict.Server
}
throw new InvalidOperationException(new StringBuilder()
.Append("The introspection response was not correctly applied. To apply introspection response, ")
.Append("The introspection response was not correctly applied. To apply introspection responses, ")
.Append("create a class implementing 'IOpenIddictServerHandler<ApplyIntrospectionResponseContext>' ")
.AppendLine("and register it using 'services.AddOpenIddict().AddServer().AddEventHandler()'.")
.ToString());

2
src/OpenIddict.Server/OpenIddictServerHandlers.Revocation.cs

@ -320,7 +320,7 @@ namespace OpenIddict.Server
}
throw new InvalidOperationException(new StringBuilder()
.Append("The revocation response was not correctly applied. To apply revocation response, ")
.Append("The revocation response was not correctly applied. To apply revocation responses, ")
.Append("create a class implementing 'IOpenIddictServerHandler<ApplyRevocationResponseContext>' ")
.AppendLine("and register it using 'services.AddOpenIddict().AddServer().AddEventHandler()'.")
.ToString());

2
src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs

@ -343,7 +343,7 @@ namespace OpenIddict.Server
}
throw new InvalidOperationException(new StringBuilder()
.Append("The revocation response was not correctly applied. To apply revocation response, ")
.Append("The revocation response was not correctly applied. To apply revocation responses, ")
.Append("create a class implementing 'IOpenIddictServerHandler<ApplyRevocationResponseContext>' ")
.AppendLine("and register it using 'services.AddOpenIddict().AddServer().AddEventHandler()'.")
.ToString());

2
src/OpenIddict.Server/OpenIddictServerHandlers.Userinfo.cs

@ -347,7 +347,7 @@ namespace OpenIddict.Server
}
throw new InvalidOperationException(new StringBuilder()
.Append("The userinfo response was not correctly applied. To apply userinfo response, ")
.Append("The userinfo response was not correctly applied. To apply userinfo responses, ")
.Append("create a class implementing 'IOpenIddictServerHandler<ApplyUserinfoResponseContext>' ")
.AppendLine("and register it using 'services.AddOpenIddict().AddServer().AddEventHandler()'.")
.ToString());

Loading…
Cancel
Save