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.
1225 lines
58 KiB
1225 lines
58 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;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Security.Claims;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using JetBrains.Annotations;
|
|
using Microsoft.Extensions.Logging;
|
|
using OpenIddict.Abstractions;
|
|
using static OpenIddict.Abstractions.OpenIddictConstants;
|
|
using static OpenIddict.Server.OpenIddictServerEvents;
|
|
using static OpenIddict.Server.OpenIddictServerHandlerFilters;
|
|
|
|
namespace OpenIddict.Server
|
|
{
|
|
public static partial class OpenIddictServerHandlers
|
|
{
|
|
public static class Device
|
|
{
|
|
public static ImmutableArray<OpenIddictServerHandlerDescriptor> DefaultHandlers { get; } = ImmutableArray.Create(
|
|
/*
|
|
* Device request top-level processing:
|
|
*/
|
|
ExtractDeviceRequest.Descriptor,
|
|
ValidateDeviceRequest.Descriptor,
|
|
HandleDeviceRequest.Descriptor,
|
|
ApplyDeviceResponse<ProcessChallengeContext>.Descriptor,
|
|
ApplyDeviceResponse<ProcessErrorContext>.Descriptor,
|
|
ApplyDeviceResponse<ProcessRequestContext>.Descriptor,
|
|
ApplyDeviceResponse<ProcessSigninContext>.Descriptor,
|
|
|
|
/*
|
|
* Device request validation:
|
|
*/
|
|
ValidateClientIdParameter.Descriptor,
|
|
ValidateScopes.Descriptor,
|
|
ValidateClientId.Descriptor,
|
|
ValidateClientType.Descriptor,
|
|
ValidateClientSecret.Descriptor,
|
|
ValidateEndpointPermissions.Descriptor,
|
|
ValidateScopePermissions.Descriptor,
|
|
|
|
/*
|
|
* Verification request top-level processing:
|
|
*/
|
|
ExtractVerificationRequest.Descriptor,
|
|
ValidateVerificationRequest.Descriptor,
|
|
HandleVerificationRequest.Descriptor,
|
|
ApplyVerificationResponse<ProcessChallengeContext>.Descriptor,
|
|
ApplyVerificationResponse<ProcessErrorContext>.Descriptor,
|
|
ApplyVerificationResponse<ProcessRequestContext>.Descriptor,
|
|
ApplyVerificationResponse<ProcessSigninContext>.Descriptor,
|
|
|
|
/*
|
|
* Verification request handling:
|
|
*/
|
|
AttachUserCodePrincipal.Descriptor);
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of extracting device requests and invoking the corresponding event handlers.
|
|
/// </summary>
|
|
public class ExtractDeviceRequest : IOpenIddictServerHandler<ProcessRequestContext>
|
|
{
|
|
private readonly IOpenIddictServerProvider _provider;
|
|
|
|
public ExtractDeviceRequest([NotNull] IOpenIddictServerProvider provider)
|
|
=> _provider = provider;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<ProcessRequestContext>()
|
|
.UseScopedHandler<ExtractDeviceRequest>()
|
|
.SetOrder(int.MinValue + 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] ProcessRequestContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
if (context.EndpointType != OpenIddictServerEndpointType.Device)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var notification = new ExtractDeviceRequestContext(context.Transaction);
|
|
await _provider.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 == null)
|
|
{
|
|
throw new InvalidOperationException(new StringBuilder()
|
|
.Append("The device request was not correctly extracted. To extract device requests, ")
|
|
.Append("create a class implementing 'IOpenIddictServerHandler<ExtractDeviceRequestContext>' ")
|
|
.AppendLine("and register it using 'services.AddOpenIddict().AddServer().AddEventHandler()'.")
|
|
.ToString());
|
|
}
|
|
|
|
context.Logger.LogInformation("The device request was successfully extracted: {Request}.", notification.Request);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of validating device requests and invoking the corresponding event handlers.
|
|
/// </summary>
|
|
public class ValidateDeviceRequest : IOpenIddictServerHandler<ProcessRequestContext>
|
|
{
|
|
private readonly IOpenIddictServerProvider _provider;
|
|
|
|
public ValidateDeviceRequest([NotNull] IOpenIddictServerProvider provider)
|
|
=> _provider = provider;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<ProcessRequestContext>()
|
|
.UseScopedHandler<ValidateDeviceRequest>()
|
|
.SetOrder(ExtractDeviceRequest.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] ProcessRequestContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
if (context.EndpointType != OpenIddictServerEndpointType.Device)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var notification = new ValidateDeviceRequestContext(context.Transaction);
|
|
await _provider.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("The device request was successfully validated.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of handling device requests and invoking the corresponding event handlers.
|
|
/// </summary>
|
|
public class HandleDeviceRequest : IOpenIddictServerHandler<ProcessRequestContext>
|
|
{
|
|
private readonly IOpenIddictServerProvider _provider;
|
|
|
|
public HandleDeviceRequest([NotNull] IOpenIddictServerProvider provider)
|
|
=> _provider = provider;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<ProcessRequestContext>()
|
|
.UseScopedHandler<HandleDeviceRequest>()
|
|
.SetOrder(ValidateDeviceRequest.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] ProcessRequestContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
if (context.EndpointType != OpenIddictServerEndpointType.Device)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var notification = new HandleDeviceRequestContext(context.Transaction);
|
|
await _provider.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;
|
|
}
|
|
|
|
var @event = new ProcessSigninContext(context.Transaction)
|
|
{
|
|
Principal = notification.Principal,
|
|
Response = new OpenIddictResponse()
|
|
};
|
|
|
|
if (@event.Principal == null)
|
|
{
|
|
// Note: no authentication type is deliberately specified to represent an unauthenticated identity.
|
|
var principal = new ClaimsPrincipal(new ClaimsIdentity());
|
|
principal.SetScopes(context.Request.GetScopes());
|
|
|
|
@event.Principal = principal;
|
|
}
|
|
|
|
await _provider.DispatchAsync(@event);
|
|
|
|
if (@event.IsRequestHandled)
|
|
{
|
|
context.HandleRequest();
|
|
return;
|
|
}
|
|
|
|
else if (@event.IsRequestSkipped)
|
|
{
|
|
context.SkipRequest();
|
|
return;
|
|
}
|
|
|
|
else if (@event.IsRejected)
|
|
{
|
|
context.Reject(
|
|
error: @event.Error ?? Errors.InvalidGrant,
|
|
description: @event.ErrorDescription,
|
|
uri: @event.ErrorUri);
|
|
return;
|
|
}
|
|
|
|
throw new InvalidOperationException(new StringBuilder()
|
|
.Append("The device request was not handled. To handle device requests, ")
|
|
.Append("create a class implementing 'IOpenIddictServerHandler<HandleDeviceRequestContext>' ")
|
|
.AppendLine("and register it using 'services.AddOpenIddict().AddServer().AddEventHandler()'.")
|
|
.Append("Alternatively, enable the pass-through mode to handle them at a later stage.")
|
|
.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of processing sign-in responses and invoking the corresponding event handlers.
|
|
/// </summary>
|
|
public class ApplyDeviceResponse<TContext> : IOpenIddictServerHandler<TContext> where TContext : BaseRequestContext
|
|
{
|
|
private readonly IOpenIddictServerProvider _provider;
|
|
|
|
public ApplyDeviceResponse([NotNull] IOpenIddictServerProvider provider)
|
|
=> _provider = provider;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<TContext>()
|
|
.UseScopedHandler<ApplyDeviceResponse<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 async ValueTask HandleAsync([NotNull] TContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
if (context.EndpointType != OpenIddictServerEndpointType.Device)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var notification = new ApplyDeviceResponseContext(context.Transaction);
|
|
await _provider.DispatchAsync(notification);
|
|
|
|
if (notification.IsRequestHandled)
|
|
{
|
|
context.HandleRequest();
|
|
return;
|
|
}
|
|
|
|
else if (notification.IsRequestSkipped)
|
|
{
|
|
context.SkipRequest();
|
|
return;
|
|
}
|
|
|
|
throw new InvalidOperationException(new StringBuilder()
|
|
.Append("The device response was not correctly applied. To apply device responses, ")
|
|
.Append("create a class implementing 'IOpenIddictServerHandler<ApplyDeviceResponseContext>' ")
|
|
.AppendLine("and register it using 'services.AddOpenIddict().AddServer().AddEventHandler()'.")
|
|
.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of rejecting device requests that don't specify a client identifier.
|
|
/// </summary>
|
|
public class ValidateClientIdParameter : IOpenIddictServerHandler<ValidateDeviceRequestContext>
|
|
{
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateDeviceRequestContext>()
|
|
.UseSingletonHandler<ValidateClientIdParameter>()
|
|
.SetOrder(int.MinValue + 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] ValidateDeviceRequestContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
// client_id is a required parameter and MUST cause an error when missing.
|
|
// See https://tools.ietf.org/html/rfc8628#section-3.1 for more information.
|
|
if (string.IsNullOrEmpty(context.ClientId))
|
|
{
|
|
context.Logger.LogError("The device request was rejected because the mandatory 'client_id' was missing.");
|
|
|
|
context.Reject(
|
|
error: Errors.InvalidRequest,
|
|
description: "The mandatory 'client_id' parameter is missing.");
|
|
|
|
return default;
|
|
}
|
|
|
|
return default;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of rejecting authorization requests that use unregistered scopes.
|
|
/// Note: this handler is not used when the degraded mode is enabled or when scope validation is disabled.
|
|
/// </summary>
|
|
public class ValidateScopes : IOpenIddictServerHandler<ValidateDeviceRequestContext>
|
|
{
|
|
private readonly IOpenIddictScopeManager _scopeManager;
|
|
|
|
public ValidateScopes() => throw new InvalidOperationException(new StringBuilder()
|
|
.AppendLine("The core services must be registered when enabling the OpenIddict server feature.")
|
|
.Append("To register the OpenIddict core services, reference the 'OpenIddict.Core' package ")
|
|
.AppendLine("and call 'services.AddOpenIddict().AddCore()' from 'ConfigureServices'.")
|
|
.Append("Alternatively, you can disable the built-in database-based server features by enabling ")
|
|
.Append("the degraded mode with 'services.AddOpenIddict().AddServer().EnableDegradedMode()'.")
|
|
.ToString());
|
|
|
|
public ValidateScopes([NotNull] IOpenIddictScopeManager scopeManager)
|
|
=> _scopeManager = scopeManager;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateDeviceRequestContext>()
|
|
.AddFilter<RequireScopeValidationEnabled>()
|
|
.AddFilter<RequireDegradedModeDisabled>()
|
|
.UseScopedHandler<ValidateScopes>()
|
|
.SetOrder(ValidateClientIdParameter.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] ValidateDeviceRequestContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
// If all the specified scopes are registered in the options, avoid making a database lookup.
|
|
var scopes = new HashSet<string>(context.Request.GetScopes(), StringComparer.Ordinal);
|
|
scopes.ExceptWith(context.Options.Scopes);
|
|
|
|
if (scopes.Count != 0)
|
|
{
|
|
await foreach (var scope in _scopeManager.FindByNamesAsync(scopes.ToImmutableArray()))
|
|
{
|
|
scopes.Remove(await _scopeManager.GetNameAsync(scope));
|
|
}
|
|
}
|
|
|
|
// If at least one scope was not recognized, return an error.
|
|
if (scopes.Count != 0)
|
|
{
|
|
context.Logger.LogError("The device request was rejected because " +
|
|
"invalid scopes were specified: {Scopes}.", scopes);
|
|
|
|
context.Reject(
|
|
error: Errors.InvalidScope,
|
|
description: "The specified 'scope' parameter is not valid.");
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of rejecting device requests that use an invalid client_id.
|
|
/// Note: this handler is not used when the degraded mode is enabled.
|
|
/// </summary>
|
|
public class ValidateClientId : IOpenIddictServerHandler<ValidateDeviceRequestContext>
|
|
{
|
|
private readonly IOpenIddictApplicationManager _applicationManager;
|
|
|
|
public ValidateClientId() => throw new InvalidOperationException(new StringBuilder()
|
|
.AppendLine("The core services must be registered when enabling the OpenIddict server feature.")
|
|
.Append("To register the OpenIddict core services, reference the 'OpenIddict.Core' package ")
|
|
.AppendLine("and call 'services.AddOpenIddict().AddCore()' from 'ConfigureServices'.")
|
|
.Append("Alternatively, you can disable the built-in database-based server features by enabling ")
|
|
.Append("the degraded mode with 'services.AddOpenIddict().AddServer().EnableDegradedMode()'.")
|
|
.ToString());
|
|
|
|
public ValidateClientId([NotNull] IOpenIddictApplicationManager applicationManager)
|
|
=> _applicationManager = applicationManager;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateDeviceRequestContext>()
|
|
.AddFilter<RequireClientIdParameter>()
|
|
.AddFilter<RequireDegradedModeDisabled>()
|
|
.UseScopedHandler<ValidateClientId>()
|
|
.SetOrder(ValidateScopes.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] ValidateDeviceRequestContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
// Retrieve the application details corresponding to the requested client_id.
|
|
// If no entity can be found, this likely indicates that the client_id is invalid.
|
|
var application = await _applicationManager.FindByClientIdAsync(context.ClientId);
|
|
if (application == null)
|
|
{
|
|
context.Logger.LogError("The device request was rejected because the client " +
|
|
"application was not found: '{ClientId}'.", context.ClientId);
|
|
|
|
context.Reject(
|
|
error: Errors.InvalidClient,
|
|
description: "The specified 'client_id' parameter is invalid.");
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of rejecting device requests made by applications
|
|
/// whose client type is not compatible with the requested grant type.
|
|
/// Note: this handler is not used when the degraded mode is enabled.
|
|
/// </summary>
|
|
public class ValidateClientType : IOpenIddictServerHandler<ValidateDeviceRequestContext>
|
|
{
|
|
private readonly IOpenIddictApplicationManager _applicationManager;
|
|
|
|
public ValidateClientType() => throw new InvalidOperationException(new StringBuilder()
|
|
.AppendLine("The core services must be registered when enabling the OpenIddict server feature.")
|
|
.Append("To register the OpenIddict core services, reference the 'OpenIddict.Core' package ")
|
|
.AppendLine("and call 'services.AddOpenIddict().AddCore()' from 'ConfigureServices'.")
|
|
.Append("Alternatively, you can disable the built-in database-based server features by enabling ")
|
|
.Append("the degraded mode with 'services.AddOpenIddict().AddServer().EnableDegradedMode()'.")
|
|
.ToString());
|
|
|
|
public ValidateClientType([NotNull] IOpenIddictApplicationManager applicationManager)
|
|
=> _applicationManager = applicationManager;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateDeviceRequestContext>()
|
|
.AddFilter<RequireClientIdParameter>()
|
|
.AddFilter<RequireDegradedModeDisabled>()
|
|
.UseScopedHandler<ValidateClientType>()
|
|
.SetOrder(ValidateClientId.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] ValidateDeviceRequestContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
var application = await _applicationManager.FindByClientIdAsync(context.ClientId);
|
|
if (application == null)
|
|
{
|
|
throw new InvalidOperationException("The client application details cannot be found in the database.");
|
|
}
|
|
|
|
if (await _applicationManager.IsPublicAsync(application))
|
|
{
|
|
// Reject device requests containing a client_secret when the client is a public application.
|
|
if (!string.IsNullOrEmpty(context.ClientSecret))
|
|
{
|
|
context.Logger.LogError("The device request was rejected because the public application '{ClientId}' " +
|
|
"was not allowed to send a client secret.", context.ClientId);
|
|
|
|
context.Reject(
|
|
error: Errors.InvalidRequest,
|
|
description: "The 'client_secret' parameter is not valid for this client application.");
|
|
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Confidential and hybrid applications MUST authenticate to protect them from impersonation attacks.
|
|
if (string.IsNullOrEmpty(context.ClientSecret))
|
|
{
|
|
context.Logger.LogError("The device request was rejected because the confidential or hybrid application " +
|
|
"'{ClientId}' didn't specify a client secret.", context.ClientId);
|
|
|
|
context.Reject(
|
|
error: Errors.InvalidClient,
|
|
description: "The 'client_secret' parameter required for this client application is missing.");
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of rejecting device requests specifying an invalid client secret.
|
|
/// Note: this handler is not used when the degraded mode is enabled.
|
|
/// </summary>
|
|
public class ValidateClientSecret : IOpenIddictServerHandler<ValidateDeviceRequestContext>
|
|
{
|
|
private readonly IOpenIddictApplicationManager _applicationManager;
|
|
|
|
public ValidateClientSecret() => throw new InvalidOperationException(new StringBuilder()
|
|
.AppendLine("The core services must be registered when enabling the OpenIddict server feature.")
|
|
.Append("To register the OpenIddict core services, reference the 'OpenIddict.Core' package ")
|
|
.AppendLine("and call 'services.AddOpenIddict().AddCore()' from 'ConfigureServices'.")
|
|
.Append("Alternatively, you can disable the built-in database-based server features by enabling ")
|
|
.Append("the degraded mode with 'services.AddOpenIddict().AddServer().EnableDegradedMode()'.")
|
|
.ToString());
|
|
|
|
public ValidateClientSecret([NotNull] IOpenIddictApplicationManager applicationManager)
|
|
=> _applicationManager = applicationManager;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateDeviceRequestContext>()
|
|
.AddFilter<RequireClientIdParameter>()
|
|
.AddFilter<RequireDegradedModeDisabled>()
|
|
.UseScopedHandler<ValidateClientSecret>()
|
|
.SetOrder(ValidateClientType.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] ValidateDeviceRequestContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
var application = await _applicationManager.FindByClientIdAsync(context.ClientId);
|
|
if (application == null)
|
|
{
|
|
throw new InvalidOperationException("The client application details cannot be found in the database.");
|
|
}
|
|
|
|
// If the application is not a public client, validate the client secret.
|
|
if (!await _applicationManager.IsPublicAsync(application) &&
|
|
!await _applicationManager.ValidateClientSecretAsync(application, context.ClientSecret))
|
|
{
|
|
context.Logger.LogError("The device request was rejected because the confidential or hybrid application " +
|
|
"'{ClientId}' didn't specify valid client credentials.", context.ClientId);
|
|
|
|
context.Reject(
|
|
error: Errors.InvalidClient,
|
|
description: "The specified client credentials are invalid.");
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of rejecting device requests made by
|
|
/// applications that haven't been granted the device endpoint permission.
|
|
/// Note: this handler is not used when the degraded mode is enabled.
|
|
/// </summary>
|
|
public class ValidateEndpointPermissions : IOpenIddictServerHandler<ValidateDeviceRequestContext>
|
|
{
|
|
private readonly IOpenIddictApplicationManager _applicationManager;
|
|
|
|
public ValidateEndpointPermissions() => throw new InvalidOperationException(new StringBuilder()
|
|
.AppendLine("The core services must be registered when enabling the OpenIddict server feature.")
|
|
.Append("To register the OpenIddict core services, reference the 'OpenIddict.Core' package ")
|
|
.AppendLine("and call 'services.AddOpenIddict().AddCore()' from 'ConfigureServices'.")
|
|
.Append("Alternatively, you can disable the built-in database-based server features by enabling ")
|
|
.Append("the degraded mode with 'services.AddOpenIddict().AddServer().EnableDegradedMode()'.")
|
|
.ToString());
|
|
|
|
public ValidateEndpointPermissions([NotNull] IOpenIddictApplicationManager applicationManager)
|
|
=> _applicationManager = applicationManager;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateDeviceRequestContext>()
|
|
.AddFilter<RequireClientIdParameter>()
|
|
.AddFilter<RequireDegradedModeDisabled>()
|
|
.AddFilter<RequireEndpointPermissionsEnabled>()
|
|
.UseScopedHandler<ValidateEndpointPermissions>()
|
|
.SetOrder(ValidateClientSecret.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] ValidateDeviceRequestContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
var application = await _applicationManager.FindByClientIdAsync(context.ClientId);
|
|
if (application == null)
|
|
{
|
|
throw new InvalidOperationException("The client application details cannot be found in the database.");
|
|
}
|
|
|
|
// Reject the request if the application is not allowed to use the device endpoint.
|
|
if (!await _applicationManager.HasPermissionAsync(application, Permissions.Endpoints.Device))
|
|
{
|
|
context.Logger.LogError("The device request was rejected because the application '{ClientId}' " +
|
|
"was not allowed to use the device endpoint.", context.ClientId);
|
|
|
|
context.Reject(
|
|
error: Errors.UnauthorizedClient,
|
|
description: "This client application is not allowed to use the device endpoint.");
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of rejecting device requests made by applications
|
|
/// that haven't been granted the appropriate grant type permission.
|
|
/// Note: this handler is not used when the degraded mode is enabled.
|
|
/// </summary>
|
|
public class ValidateScopePermissions : IOpenIddictServerHandler<ValidateDeviceRequestContext>
|
|
{
|
|
private readonly IOpenIddictApplicationManager _applicationManager;
|
|
|
|
public ValidateScopePermissions() => throw new InvalidOperationException(new StringBuilder()
|
|
.AppendLine("The core services must be registered when enabling the OpenIddict server feature.")
|
|
.Append("To register the OpenIddict core services, reference the 'OpenIddict.Core' package ")
|
|
.AppendLine("and call 'services.AddOpenIddict().AddCore()' from 'ConfigureServices'.")
|
|
.Append("Alternatively, you can disable the built-in database-based server features by enabling ")
|
|
.Append("the degraded mode with 'services.AddOpenIddict().AddServer().EnableDegradedMode()'.")
|
|
.ToString());
|
|
|
|
public ValidateScopePermissions([NotNull] IOpenIddictApplicationManager applicationManager)
|
|
=> _applicationManager = applicationManager;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateDeviceRequestContext>()
|
|
.AddFilter<RequireClientIdParameter>()
|
|
.AddFilter<RequireDegradedModeDisabled>()
|
|
.AddFilter<RequireScopePermissionsEnabled>()
|
|
.UseScopedHandler<ValidateScopePermissions>()
|
|
.SetOrder(ValidateEndpointPermissions.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] ValidateDeviceRequestContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
var application = await _applicationManager.FindByClientIdAsync(context.ClientId);
|
|
if (application == null)
|
|
{
|
|
throw new InvalidOperationException("The client application details cannot be found in the database.");
|
|
}
|
|
|
|
foreach (var scope in context.Request.GetScopes())
|
|
{
|
|
// Avoid validating the "openid" and "offline_access" scopes as they represent protocol scopes.
|
|
if (string.Equals(scope, Scopes.OfflineAccess, StringComparison.Ordinal) ||
|
|
string.Equals(scope, Scopes.OpenId, StringComparison.Ordinal))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Reject the request if the application is not allowed to use the iterated scope.
|
|
if (!await _applicationManager.HasPermissionAsync(application, Permissions.Prefixes.Scope + scope))
|
|
{
|
|
context.Logger.LogError("The device request was rejected because the application '{ClientId}' " +
|
|
"was not allowed to use the scope {Scope}.", context.ClientId, scope);
|
|
|
|
context.Reject(
|
|
error: Errors.InvalidRequest,
|
|
description: "This client application is not allowed to use the specified scope.");
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of extracting verification requests and invoking the corresponding event handlers.
|
|
/// </summary>
|
|
public class ExtractVerificationRequest : IOpenIddictServerHandler<ProcessRequestContext>
|
|
{
|
|
private readonly IOpenIddictServerProvider _provider;
|
|
|
|
public ExtractVerificationRequest([NotNull] IOpenIddictServerProvider provider)
|
|
=> _provider = provider;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<ProcessRequestContext>()
|
|
.UseScopedHandler<ExtractVerificationRequest>()
|
|
.SetOrder(int.MinValue + 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] ProcessRequestContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
if (context.EndpointType != OpenIddictServerEndpointType.Verification)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var notification = new ExtractVerificationRequestContext(context.Transaction);
|
|
await _provider.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 == null)
|
|
{
|
|
throw new InvalidOperationException(new StringBuilder()
|
|
.Append("The verification request was not correctly extracted. To extract verification requests, ")
|
|
.Append("create a class implementing 'IOpenIddictServerHandler<ExtractVerificationRequestContext>' ")
|
|
.AppendLine("and register it using 'services.AddOpenIddict().AddServer().AddEventHandler()'.")
|
|
.ToString());
|
|
}
|
|
|
|
context.Logger.LogInformation("The verification request was successfully extracted: {Request}.", notification.Request);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of validating verification requests and invoking the corresponding event handlers.
|
|
/// </summary>
|
|
public class ValidateVerificationRequest : IOpenIddictServerHandler<ProcessRequestContext>
|
|
{
|
|
private readonly IOpenIddictServerProvider _provider;
|
|
|
|
public ValidateVerificationRequest([NotNull] IOpenIddictServerProvider provider)
|
|
=> _provider = provider;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<ProcessRequestContext>()
|
|
.UseScopedHandler<ValidateVerificationRequest>()
|
|
.SetOrder(ExtractVerificationRequest.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] ProcessRequestContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
if (context.EndpointType != OpenIddictServerEndpointType.Verification)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var notification = new ValidateVerificationRequestContext(context.Transaction);
|
|
await _provider.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("The verification request was successfully validated.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of handling verification requests and invoking the corresponding event handlers.
|
|
/// </summary>
|
|
public class HandleVerificationRequest : IOpenIddictServerHandler<ProcessRequestContext>
|
|
{
|
|
private readonly IOpenIddictServerProvider _provider;
|
|
|
|
public HandleVerificationRequest([NotNull] IOpenIddictServerProvider provider)
|
|
=> _provider = provider;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<ProcessRequestContext>()
|
|
.UseScopedHandler<HandleVerificationRequest>()
|
|
.SetOrder(ValidateVerificationRequest.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] ProcessRequestContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
if (context.EndpointType != OpenIddictServerEndpointType.Verification)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var notification = new HandleVerificationRequestContext(context.Transaction);
|
|
await _provider.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.Principal != null)
|
|
{
|
|
var @event = new ProcessSigninContext(context.Transaction)
|
|
{
|
|
Principal = notification.Principal,
|
|
Response = new OpenIddictResponse()
|
|
};
|
|
|
|
await _provider.DispatchAsync(@event);
|
|
|
|
if (@event.IsRequestHandled)
|
|
{
|
|
context.HandleRequest();
|
|
return;
|
|
}
|
|
|
|
else if (@event.IsRequestSkipped)
|
|
{
|
|
context.SkipRequest();
|
|
return;
|
|
}
|
|
|
|
else if (@event.IsRejected)
|
|
{
|
|
context.Reject(
|
|
error: @event.Error ?? Errors.InvalidGrant,
|
|
description: @event.ErrorDescription,
|
|
uri: @event.ErrorUri);
|
|
return;
|
|
}
|
|
}
|
|
|
|
throw new InvalidOperationException(new StringBuilder()
|
|
.Append("The verification request was not handled. To handle verification requests, ")
|
|
.Append("create a class implementing 'IOpenIddictServerHandler<HandleVerificationRequestContext>' ")
|
|
.AppendLine("and register it using 'services.AddOpenIddict().AddServer().AddEventHandler()'.")
|
|
.Append("Alternatively, enable the pass-through mode to handle them at a later stage.")
|
|
.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of processing sign-in responses and invoking the corresponding event handlers.
|
|
/// </summary>
|
|
public class ApplyVerificationResponse<TContext> : IOpenIddictServerHandler<TContext> where TContext : BaseRequestContext
|
|
{
|
|
private readonly IOpenIddictServerProvider _provider;
|
|
|
|
public ApplyVerificationResponse([NotNull] IOpenIddictServerProvider provider)
|
|
=> _provider = provider;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<TContext>()
|
|
.UseScopedHandler<ApplyVerificationResponse<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 async ValueTask HandleAsync([NotNull] TContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
if (context.EndpointType != OpenIddictServerEndpointType.Verification)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var notification = new ApplyVerificationResponseContext(context.Transaction);
|
|
await _provider.DispatchAsync(notification);
|
|
|
|
if (notification.IsRequestHandled)
|
|
{
|
|
context.HandleRequest();
|
|
return;
|
|
}
|
|
|
|
else if (notification.IsRequestSkipped)
|
|
{
|
|
context.SkipRequest();
|
|
return;
|
|
}
|
|
|
|
throw new InvalidOperationException(new StringBuilder()
|
|
.Append("The verification response was not correctly applied. To apply verification responses, ")
|
|
.Append("create a class implementing 'IOpenIddictServerHandler<ApplyVerificationResponseContext>' ")
|
|
.AppendLine("and register it using 'services.AddOpenIddict().AddServer().AddEventHandler()'.")
|
|
.ToString());
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Contains the logic responsible of attaching the claims principal resolved from the user code.
|
|
/// </summary>
|
|
public class AttachUserCodePrincipal : IOpenIddictServerHandler<HandleVerificationRequestContext>
|
|
{
|
|
private readonly IOpenIddictServerProvider _provider;
|
|
|
|
public AttachUserCodePrincipal([NotNull] IOpenIddictServerProvider provider)
|
|
=> _provider = provider;
|
|
|
|
/// <summary>
|
|
/// Gets the default descriptor definition assigned to this handler.
|
|
/// </summary>
|
|
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
|
|
= OpenIddictServerHandlerDescriptor.CreateBuilder<HandleVerificationRequestContext>()
|
|
.UseScopedHandler<AttachUserCodePrincipal>()
|
|
.SetOrder(int.MinValue + 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] HandleVerificationRequestContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
// Note: the user_code may not be present (e.g when the user typed
|
|
// the verification_uri manually without the user code appended).
|
|
// In this case, ignore the missing token so that a view can be
|
|
// rendered by the application to ask the user to enter the code.
|
|
if (string.IsNullOrEmpty(context.Request.UserCode))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var notification = new ProcessAuthenticationContext(context.Transaction);
|
|
await _provider.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)
|
|
{
|
|
// Note: authentication errors are deliberately not flowed up to the parent context.
|
|
return;
|
|
}
|
|
|
|
// Attach the security principal extracted from the token to the validation context.
|
|
context.Principal = notification.Principal;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|