You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
465 lines
20 KiB
465 lines
20 KiB
/*
|
|
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|
* the license and the contributors participating to this project.
|
|
*/
|
|
|
|
using System.Collections.Immutable;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace OpenIddict.Client;
|
|
|
|
public static partial class OpenIddictClientHandlers
|
|
{
|
|
public static class Authentication
|
|
{
|
|
public static ImmutableArray<OpenIddictClientHandlerDescriptor> DefaultHandlers { get; } = ImmutableArray.Create(
|
|
/*
|
|
* Authorization request top-level processing:
|
|
*/
|
|
PrepareAuthorizationRequest.Descriptor,
|
|
ApplyAuthorizationRequest.Descriptor,
|
|
|
|
/*
|
|
* Authorization request preparation:
|
|
*/
|
|
NormalizeResponseModeParameter.Descriptor,
|
|
|
|
/*
|
|
* Authorization request processing:
|
|
*/
|
|
AttachAuthorizationEndpoint.Descriptor,
|
|
|
|
/*
|
|
* Redirection request top-level processing:
|
|
*/
|
|
ExtractRedirectionRequest.Descriptor,
|
|
ValidateRedirectionRequest.Descriptor,
|
|
HandleRedirectionRequest.Descriptor,
|
|
ApplyRedirectionResponse<ProcessErrorContext>.Descriptor,
|
|
ApplyRedirectionResponse<ProcessRequestContext>.Descriptor,
|
|
|
|
/*
|
|
* Redirection request validation:
|
|
*/
|
|
ValidateTokens.Descriptor);
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of preparing authorization requests and invoking the corresponding event handlers.
|
|
/// </summary>
|
|
public class PrepareAuthorizationRequest : IOpenIddictClientHandler<ProcessChallengeContext>
|
|
{
|
|
private readonly IOpenIddictClientDispatcher _dispatcher;
|
|
|
|
public PrepareAuthorizationRequest(IOpenIddictClientDispatcher dispatcher!!)
|
|
=> _dispatcher = dispatcher;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessChallengeContext>()
|
|
.AddFilter<RequireAuthorizationCodeOrImplicitGrantType>()
|
|
.UseScopedHandler<PrepareAuthorizationRequest>()
|
|
.SetOrder(int.MaxValue - 100_000)
|
|
.Build();
|
|
|
|
/// <inheritdoc/>
|
|
public async ValueTask HandleAsync(ProcessChallengeContext context!!)
|
|
{
|
|
var notification = new PrepareAuthorizationRequestContext(context.Transaction);
|
|
await _dispatcher.DispatchAsync(notification);
|
|
|
|
if (notification.IsRequestHandled)
|
|
{
|
|
context.HandleRequest();
|
|
return;
|
|
}
|
|
|
|
else if (notification.IsRequestSkipped)
|
|
{
|
|
context.SkipRequest();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of applying authorization requests and invoking the corresponding event handlers.
|
|
/// </summary>
|
|
public class ApplyAuthorizationRequest : IOpenIddictClientHandler<ProcessChallengeContext>
|
|
{
|
|
private readonly IOpenIddictClientDispatcher _dispatcher;
|
|
|
|
public ApplyAuthorizationRequest(IOpenIddictClientDispatcher dispatcher!!)
|
|
=> _dispatcher = dispatcher;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessChallengeContext>()
|
|
.AddFilter<RequireAuthorizationCodeOrImplicitGrantType>()
|
|
.UseScopedHandler<ApplyAuthorizationRequest>()
|
|
.SetOrder(PrepareAuthorizationRequest.Descriptor.Order + 1_000)
|
|
.Build();
|
|
|
|
/// <inheritdoc/>
|
|
public async ValueTask HandleAsync(ProcessChallengeContext context!!)
|
|
{
|
|
var notification = new ApplyAuthorizationRequestContext(context.Transaction);
|
|
await _dispatcher.DispatchAsync(notification);
|
|
|
|
if (notification.IsRequestHandled)
|
|
{
|
|
context.HandleRequest();
|
|
return;
|
|
}
|
|
|
|
else if (notification.IsRequestSkipped)
|
|
{
|
|
context.SkipRequest();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of attaching the address of the authorization request to the request.
|
|
/// </summary>
|
|
public class AttachAuthorizationEndpoint : IOpenIddictClientHandler<ApplyAuthorizationRequestContext>
|
|
{
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictClientHandlerDescriptor.CreateBuilder<ApplyAuthorizationRequestContext>()
|
|
.UseSingletonHandler<AttachAuthorizationEndpoint>()
|
|
.SetOrder(int.MinValue + 100_000)
|
|
.Build();
|
|
|
|
/// <inheritdoc/>
|
|
public async ValueTask HandleAsync(ApplyAuthorizationRequestContext context!!)
|
|
{
|
|
var configuration = await context.Registration.ConfigurationManager.GetConfigurationAsync(default) ??
|
|
throw new InvalidOperationException(SR.GetResourceString(SR.ID0140));
|
|
|
|
// Ensure the issuer resolved from the configuration matches the expected value.
|
|
if (configuration.Issuer != context.Issuer)
|
|
{
|
|
throw new InvalidOperationException(SR.GetResourceString(SR.ID0307));
|
|
}
|
|
|
|
// Ensure the authorization endpoint is present and is a valid absolute URL.
|
|
if (configuration.AuthorizationEndpoint is not { IsAbsoluteUri: true } ||
|
|
!configuration.AuthorizationEndpoint.IsWellFormedOriginalString())
|
|
{
|
|
throw new InvalidOperationException(SR.FormatID0301(Metadata.AuthorizationEndpoint));
|
|
}
|
|
|
|
context.AuthorizationEndpoint = configuration.AuthorizationEndpoint.AbsoluteUri;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of extracting redirection requests and invoking the corresponding event handlers.
|
|
/// </summary>
|
|
public class ExtractRedirectionRequest : IOpenIddictClientHandler<ProcessRequestContext>
|
|
{
|
|
private readonly IOpenIddictClientDispatcher _dispatcher;
|
|
|
|
public ExtractRedirectionRequest(IOpenIddictClientDispatcher dispatcher!!)
|
|
=> _dispatcher = dispatcher;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessRequestContext>()
|
|
.AddFilter<RequireRedirectionRequest>()
|
|
.UseScopedHandler<ExtractRedirectionRequest>()
|
|
.SetOrder(100_000)
|
|
.Build();
|
|
|
|
/// <inheritdoc/>
|
|
public async ValueTask HandleAsync(ProcessRequestContext context!!)
|
|
{
|
|
var notification = new ExtractRedirectionRequestContext(context.Transaction);
|
|
await _dispatcher.DispatchAsync(notification);
|
|
|
|
if (notification.IsRequestHandled)
|
|
{
|
|
context.HandleRequest();
|
|
return;
|
|
}
|
|
|
|
else if (notification.IsRequestSkipped)
|
|
{
|
|
context.SkipRequest();
|
|
return;
|
|
}
|
|
|
|
else if (notification.IsRejected)
|
|
{
|
|
context.Reject(
|
|
error: notification.Error ?? Errors.InvalidRequest,
|
|
description: notification.ErrorDescription,
|
|
uri: notification.ErrorUri);
|
|
return;
|
|
}
|
|
|
|
if (notification.Request is null)
|
|
{
|
|
throw new InvalidOperationException(SR.GetResourceString(SR.ID0302));
|
|
}
|
|
|
|
context.Logger.LogInformation(SR.GetResourceString(SR.ID6178), notification.Request);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of validating redirection requests and invoking the corresponding event handlers.
|
|
/// </summary>
|
|
public class ValidateRedirectionRequest : IOpenIddictClientHandler<ProcessRequestContext>
|
|
{
|
|
private readonly IOpenIddictClientDispatcher _dispatcher;
|
|
|
|
public ValidateRedirectionRequest(IOpenIddictClientDispatcher dispatcher!!)
|
|
=> _dispatcher = dispatcher;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessRequestContext>()
|
|
.AddFilter<RequireRedirectionRequest>()
|
|
.UseScopedHandler<ValidateRedirectionRequest>()
|
|
.SetOrder(ExtractRedirectionRequest.Descriptor.Order + 1_000)
|
|
.SetType(OpenIddictClientHandlerType.BuiltIn)
|
|
.Build();
|
|
|
|
/// <inheritdoc/>
|
|
public async ValueTask HandleAsync(ProcessRequestContext context!!)
|
|
{
|
|
var notification = new ValidateRedirectionRequestContext(context.Transaction);
|
|
await _dispatcher.DispatchAsync(notification);
|
|
|
|
if (notification.IsRequestHandled)
|
|
{
|
|
context.HandleRequest();
|
|
return;
|
|
}
|
|
|
|
else if (notification.IsRequestSkipped)
|
|
{
|
|
context.SkipRequest();
|
|
return;
|
|
}
|
|
|
|
else if (notification.IsRejected)
|
|
{
|
|
context.Reject(
|
|
error: notification.Error ?? Errors.InvalidRequest,
|
|
description: notification.ErrorDescription,
|
|
uri: notification.ErrorUri);
|
|
return;
|
|
}
|
|
|
|
context.Logger.LogInformation(SR.GetResourceString(SR.ID6179));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of handling redirection requests and invoking the corresponding event handlers.
|
|
/// </summary>
|
|
public class HandleRedirectionRequest : IOpenIddictClientHandler<ProcessRequestContext>
|
|
{
|
|
private readonly IOpenIddictClientDispatcher _dispatcher;
|
|
|
|
public HandleRedirectionRequest(IOpenIddictClientDispatcher dispatcher!!)
|
|
=> _dispatcher = dispatcher;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessRequestContext>()
|
|
.AddFilter<RequireRedirectionRequest>()
|
|
.UseScopedHandler<HandleRedirectionRequest>()
|
|
.SetOrder(ValidateRedirectionRequest.Descriptor.Order + 1_000)
|
|
.Build();
|
|
|
|
/// <inheritdoc/>
|
|
public async ValueTask HandleAsync(ProcessRequestContext context!!)
|
|
{
|
|
var notification = new HandleRedirectionRequestContext(context.Transaction);
|
|
await _dispatcher.DispatchAsync(notification);
|
|
|
|
if (notification.IsRequestHandled)
|
|
{
|
|
context.HandleRequest();
|
|
return;
|
|
}
|
|
|
|
else if (notification.IsRequestSkipped)
|
|
{
|
|
context.SkipRequest();
|
|
return;
|
|
}
|
|
|
|
else if (notification.IsRejected)
|
|
{
|
|
context.Reject(
|
|
error: notification.Error ?? Errors.InvalidRequest,
|
|
description: notification.ErrorDescription,
|
|
uri: notification.ErrorUri);
|
|
return;
|
|
}
|
|
|
|
context.Logger.LogInformation(SR.GetResourceString(SR.ID6180));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of processing redirection responses and invoking the corresponding event handlers.
|
|
/// </summary>
|
|
public class ApplyRedirectionResponse<TContext> : IOpenIddictClientHandler<TContext> where TContext : BaseRequestContext
|
|
{
|
|
private readonly IOpenIddictClientDispatcher _dispatcher;
|
|
|
|
public ApplyRedirectionResponse(IOpenIddictClientDispatcher dispatcher!!)
|
|
=> _dispatcher = dispatcher;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictClientHandlerDescriptor.CreateBuilder<TContext>()
|
|
.AddFilter<RequireRedirectionRequest>()
|
|
.UseScopedHandler<ApplyRedirectionResponse<TContext>>()
|
|
.SetOrder(int.MaxValue - 100_000)
|
|
.SetType(OpenIddictClientHandlerType.BuiltIn)
|
|
.Build();
|
|
|
|
/// <inheritdoc/>
|
|
public async ValueTask HandleAsync(TContext context!!)
|
|
{
|
|
var notification = new ApplyRedirectionResponseContext(context.Transaction);
|
|
await _dispatcher.DispatchAsync(notification);
|
|
|
|
if (notification.IsRequestHandled)
|
|
{
|
|
context.HandleRequest();
|
|
return;
|
|
}
|
|
|
|
else if (notification.IsRequestSkipped)
|
|
{
|
|
context.SkipRequest();
|
|
return;
|
|
}
|
|
|
|
throw new InvalidOperationException(SR.GetResourceString(SR.ID0303));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of removing the response mode parameter from the
|
|
/// request if it corresponds to the default mode for the selected response type.
|
|
/// </summary>
|
|
public class NormalizeResponseModeParameter : IOpenIddictClientHandler<PrepareAuthorizationRequestContext>
|
|
{
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictClientHandlerDescriptor.CreateBuilder<PrepareAuthorizationRequestContext>()
|
|
.UseSingletonHandler<NormalizeResponseModeParameter>()
|
|
.SetOrder(int.MinValue + 100_000)
|
|
.Build();
|
|
|
|
/// <inheritdoc/>
|
|
public ValueTask HandleAsync(PrepareAuthorizationRequestContext context!!)
|
|
{
|
|
|
|
// When the response mode corresponds to the default mode assigned to the selected
|
|
// response type, the specification explicitly recommends omitting the response mode.
|
|
// As such, this handler is expected to remove the mode parameter in the following cases:
|
|
// - Authorization code flow: response_mode=query.
|
|
// - Hybrid flow: response_mode=fragment.
|
|
// - Implicit flow: response_mode=fragment.
|
|
//
|
|
// For more information, read
|
|
// https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes.
|
|
//
|
|
if (!string.IsNullOrEmpty(context.Request.ResponseMode) &&
|
|
(context.Request.IsAuthorizationCodeFlow() && context.Request.IsQueryResponseMode()) ||
|
|
(context.Request.IsHybridFlow() && context.Request.IsFragmentResponseMode()) ||
|
|
(context.Request.IsImplicitFlow() && context.Request.IsFragmentResponseMode()))
|
|
{
|
|
context.Request.ResponseMode = null;
|
|
}
|
|
|
|
return default;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of rejecting redirection requests that don't
|
|
/// specify a valid access token, authorization code, identity token or state token.
|
|
/// </summary>
|
|
public class ValidateTokens : IOpenIddictClientHandler<ValidateRedirectionRequestContext>
|
|
{
|
|
private readonly IOpenIddictClientDispatcher _dispatcher;
|
|
|
|
public ValidateTokens(IOpenIddictClientDispatcher dispatcher!!)
|
|
=> _dispatcher = dispatcher;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictClientHandlerDescriptor.CreateBuilder<ValidateRedirectionRequestContext>()
|
|
.UseScopedHandler<ValidateTokens>()
|
|
.SetOrder(int.MinValue + 100_000)
|
|
.SetType(OpenIddictClientHandlerType.BuiltIn)
|
|
.Build();
|
|
|
|
/// <inheritdoc/>
|
|
public async ValueTask HandleAsync(ValidateRedirectionRequestContext context!!)
|
|
{
|
|
var notification = new ProcessAuthenticationContext(context.Transaction);
|
|
await _dispatcher.DispatchAsync(notification);
|
|
|
|
// Store the context object in the transaction so it can be later retrieved by handlers
|
|
// that want to access the authentication result without triggering a new authentication flow.
|
|
context.Transaction.SetProperty(typeof(ProcessAuthenticationContext).FullName!, notification);
|
|
|
|
if (notification.IsRequestHandled)
|
|
{
|
|
context.HandleRequest();
|
|
return;
|
|
}
|
|
|
|
else if (notification.IsRequestSkipped)
|
|
{
|
|
context.SkipRequest();
|
|
return;
|
|
}
|
|
|
|
else if (notification.IsRejected)
|
|
{
|
|
context.Reject(
|
|
error: notification.Error ?? Errors.InvalidRequest,
|
|
description: notification.ErrorDescription,
|
|
uri: notification.ErrorUri);
|
|
return;
|
|
}
|
|
|
|
// Attach the security principals extracted from the tokens to the validation context.
|
|
context.Principal = notification.FrontchannelIdentityTokenPrincipal;
|
|
context.StateTokenPrincipal = notification.StateTokenPrincipal;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|