Versatile OpenID Connect stack for ASP.NET Core and Microsoft.Owin (compatible with ASP.NET 4.6.1)
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.
 
 
 
 
 
 

1141 lines
50 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 System.Diagnostics;
using System.Security.Claims;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace OpenIddict.Server;
public static partial class OpenIddictServerHandlers
{
public static class Device
{
public static ImmutableArray<OpenIddictServerHandlerDescriptor> DefaultHandlers { get; } =
[
/*
* Device request top-level processing:
*/
ExtractDeviceAuthorizationRequest.Descriptor,
ValidateDeviceAuthorizationRequest.Descriptor,
HandleDeviceAuthorizationRequest.Descriptor,
ApplyDeviceAuthorizationResponse<ProcessChallengeContext>.Descriptor,
ApplyDeviceAuthorizationResponse<ProcessErrorContext>.Descriptor,
ApplyDeviceAuthorizationResponse<ProcessRequestContext>.Descriptor,
ApplyDeviceAuthorizationResponse<ProcessSignInContext>.Descriptor,
/*
* Device request validation:
*/
ValidateScopeParameter.Descriptor,
ValidateClientCredentialsParameters.Descriptor,
ValidateScopes.Descriptor,
ValidateDeviceAuthentication.Descriptor,
ValidateEndpointPermissions.Descriptor,
ValidateGrantTypePermissions.Descriptor,
ValidateScopePermissions.Descriptor,
/*
* Verification request top-level processing:
*/
ExtractEndUserVerificationRequest.Descriptor,
ValidateEndUserVerificationRequest.Descriptor,
HandleEndUserVerificationRequest.Descriptor,
ApplyEndUserVerificationResponse<ProcessChallengeContext>.Descriptor,
ApplyEndUserVerificationResponse<ProcessErrorContext>.Descriptor,
ApplyEndUserVerificationResponse<ProcessRequestContext>.Descriptor,
ApplyEndUserVerificationResponse<ProcessSignInContext>.Descriptor,
/*
* Verification request validation:
*/
ValidateVerificationAuthentication.Descriptor,
/*
* Verification request handling:
*/
AttachUserCodePrincipal.Descriptor
];
/// <summary>
/// Contains the logic responsible for extracting device requests and invoking the corresponding event handlers.
/// </summary>
public sealed class ExtractDeviceAuthorizationRequest : IOpenIddictServerHandler<ProcessRequestContext>
{
private readonly IOpenIddictServerDispatcher _dispatcher;
public ExtractDeviceAuthorizationRequest(IOpenIddictServerDispatcher dispatcher)
=> _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ProcessRequestContext>()
.AddFilter<RequireDeviceAuthorizationRequest>()
.UseScopedHandler<ExtractDeviceAuthorizationRequest>()
.SetOrder(100_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public async ValueTask HandleAsync(ProcessRequestContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
var notification = new ExtractDeviceAuthorizationRequestContext(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.ID0031));
}
context.Logger.LogInformation(6054, SR.GetResourceString(SR.ID6054), notification.Request);
}
}
/// <summary>
/// Contains the logic responsible for validating device requests and invoking the corresponding event handlers.
/// </summary>
public sealed class ValidateDeviceAuthorizationRequest : IOpenIddictServerHandler<ProcessRequestContext>
{
private readonly IOpenIddictServerDispatcher _dispatcher;
public ValidateDeviceAuthorizationRequest(IOpenIddictServerDispatcher dispatcher)
=> _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ProcessRequestContext>()
.AddFilter<RequireDeviceAuthorizationRequest>()
.UseScopedHandler<ValidateDeviceAuthorizationRequest>()
.SetOrder(ExtractDeviceAuthorizationRequest.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public async ValueTask HandleAsync(ProcessRequestContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
var notification = new ValidateDeviceAuthorizationRequestContext(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(6055, SR.GetResourceString(SR.ID6055));
}
}
/// <summary>
/// Contains the logic responsible for handling device requests and invoking the corresponding event handlers.
/// </summary>
public sealed class HandleDeviceAuthorizationRequest : IOpenIddictServerHandler<ProcessRequestContext>
{
private readonly IOpenIddictServerDispatcher _dispatcher;
public HandleDeviceAuthorizationRequest(IOpenIddictServerDispatcher dispatcher)
=> _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ProcessRequestContext>()
.AddFilter<RequireDeviceAuthorizationRequest>()
.UseScopedHandler<HandleDeviceAuthorizationRequest>()
.SetOrder(ValidateDeviceAuthorizationRequest.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public async ValueTask HandleAsync(ProcessRequestContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
var notification = new HandleDeviceAuthorizationRequestContext(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.Principal is null)
{
// Note: no authentication type is deliberately specified to represent an unauthenticated identity.
var principal = new ClaimsPrincipal(new ClaimsIdentity());
principal.SetScopes(notification.Request.GetScopes());
notification.Principal = principal;
}
var @event = new ProcessSignInContext(context.Transaction)
{
Principal = notification.Principal,
Response = new OpenIddictResponse()
};
if (notification.Parameters.Count is > 0)
{
foreach (var parameter in notification.Parameters)
{
@event.Parameters.Add(parameter.Key, parameter.Value);
}
}
await _dispatcher.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;
}
}
}
/// <summary>
/// Contains the logic responsible for processing sign-in responses and invoking the corresponding event handlers.
/// </summary>
public sealed class ApplyDeviceAuthorizationResponse<TContext> : IOpenIddictServerHandler<TContext> where TContext : BaseRequestContext
{
private readonly IOpenIddictServerDispatcher _dispatcher;
public ApplyDeviceAuthorizationResponse(IOpenIddictServerDispatcher dispatcher)
=> _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireDeviceAuthorizationRequest>()
.UseScopedHandler<ApplyDeviceAuthorizationResponse<TContext>>()
.SetOrder(500_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public async ValueTask HandleAsync(TContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
var notification = new ApplyDeviceAuthorizationResponseContext(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.ID0033));
}
}
/// <summary>
/// Contains the logic responsible for rejecting device requests that don't specify a valid scope parameter.
/// </summary>
public sealed class ValidateScopeParameter : IOpenIddictServerHandler<ValidateDeviceAuthorizationRequestContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateDeviceAuthorizationRequestContext>()
.UseSingletonHandler<ValidateScopeParameter>()
.SetOrder(int.MinValue + 100_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(ValidateDeviceAuthorizationRequestContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
// Reject device requests that specify scope=offline_access if the refresh token flow is not enabled.
if (context.Request.HasScope(Scopes.OfflineAccess) && !context.Options.GrantTypes.Contains(GrantTypes.RefreshToken))
{
context.Reject(
error: Errors.InvalidRequest,
description: SR.FormatID2035(Scopes.OfflineAccess),
uri: SR.FormatID8000(SR.ID2035));
return ValueTask.CompletedTask;
}
return ValueTask.CompletedTask;
}
}
/// <summary>
/// Contains the logic responsible for rejecting device requests that specify invalid client credentials parameters.
/// </summary>
public sealed class ValidateClientCredentialsParameters : IOpenIddictServerHandler<ValidateDeviceAuthorizationRequestContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateDeviceAuthorizationRequestContext>()
.UseSingletonHandler<ValidateClientCredentialsParameters>()
.SetOrder(ValidateScopeParameter.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(ValidateDeviceAuthorizationRequestContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
// Ensure a client_assertion_type is specified when a client_assertion was attached.
if (!string.IsNullOrEmpty(context.Request.ClientAssertion) &&
string.IsNullOrEmpty(context.Request.ClientAssertionType))
{
context.Reject(
error: Errors.InvalidRequest,
description: SR.FormatID2037(Parameters.ClientAssertionType, Parameters.ClientAssertion),
uri: SR.FormatID8000(SR.ID2037));
return ValueTask.CompletedTask;
}
// Ensure a client_assertion is specified when a client_assertion_type was attached.
if (string.IsNullOrEmpty(context.Request.ClientAssertion) &&
!string.IsNullOrEmpty(context.Request.ClientAssertionType))
{
context.Reject(
error: Errors.InvalidRequest,
description: SR.FormatID2037(Parameters.ClientAssertion, Parameters.ClientAssertionType),
uri: SR.FormatID8000(SR.ID2037));
return ValueTask.CompletedTask;
}
// Reject requests that use multiple client authentication methods.
//
// See https://tools.ietf.org/html/rfc6749#section-2.3 for more information.
if (!string.IsNullOrEmpty(context.Request.ClientAssertion) &&
!string.IsNullOrEmpty(context.Request.ClientSecret))
{
context.Logger.LogInformation(6140, SR.GetResourceString(SR.ID6140));
context.Reject(
error: Errors.InvalidRequest,
description: SR.GetResourceString(SR.ID2087),
uri: SR.FormatID8000(SR.ID2087));
return ValueTask.CompletedTask;
}
// Ensure the specified client_assertion_type is supported.
if (!string.IsNullOrEmpty(context.Request.ClientAssertionType) &&
!context.Options.ClientAssertionTypes.Contains(context.Request.ClientAssertionType))
{
context.Reject(
error: Errors.InvalidClient,
description: SR.FormatID2032(Parameters.ClientAssertionType),
uri: SR.FormatID8000(SR.ID2032));
return ValueTask.CompletedTask;
}
return ValueTask.CompletedTask;
}
}
/// <summary>
/// Contains the logic responsible for rejecting authorization requests that use unregistered scopes.
/// Note: this handler partially works with the degraded mode but is not used when scope validation is disabled.
/// </summary>
public sealed class ValidateScopes : IOpenIddictServerHandler<ValidateDeviceAuthorizationRequestContext>
{
private readonly IOpenIddictScopeManager? _scopeManager;
public ValidateScopes(IOpenIddictScopeManager? scopeManager = null)
=> _scopeManager = scopeManager;
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateDeviceAuthorizationRequestContext>()
.AddFilter<RequireScopeValidationEnabled>()
.UseScopedHandler(static provider =>
{
// Note: the scope manager is only resolved if the degraded mode was not enabled to ensure
// invalid core configuration exceptions are not thrown even if the managers were registered.
var options = provider.GetRequiredService<IOptionsMonitor<OpenIddictServerOptions>>().CurrentValue;
return options.EnableDegradedMode ?
new ValidateScopes() :
new ValidateScopes(provider.GetService<IOpenIddictScopeManager>() ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0016)));
})
.SetOrder(ValidateClientCredentialsParameters.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public async ValueTask HandleAsync(ValidateDeviceAuthorizationRequestContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
// If all the specified scopes are registered in the options, avoid making a database lookup.
var scopes = context.Request.GetScopes().ToHashSet(StringComparer.Ordinal);
scopes.ExceptWith(context.Options.Scopes);
// Note: the remaining scopes are only checked if the degraded mode was not enabled,
// as this requires using the scope manager, which is never used with the degraded mode,
// even if the service was registered and resolved from the dependency injection container.
if (scopes.Count is not 0 && !context.Options.EnableDegradedMode)
{
if (_scopeManager is null)
{
throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
}
await foreach (var scope in _scopeManager.FindByNamesAsync([.. scopes]))
{
var name = await _scopeManager.GetNameAsync(scope);
if (!string.IsNullOrEmpty(name))
{
scopes.Remove(name);
}
}
}
// If at least one scope was not recognized, return an error.
if (scopes.Count is not 0)
{
context.Logger.LogInformation(6057, SR.GetResourceString(SR.ID6057), scopes);
context.Reject(
error: Errors.InvalidScope,
description: SR.FormatID2052(Parameters.Scope),
uri: SR.FormatID8000(SR.ID2052));
return;
}
}
}
/// <summary>
/// Contains the logic responsible for applying the authentication logic to device requests.
/// </summary>
public sealed class ValidateDeviceAuthentication : IOpenIddictServerHandler<ValidateDeviceAuthorizationRequestContext>
{
private readonly IOpenIddictServerDispatcher _dispatcher;
public ValidateDeviceAuthentication(IOpenIddictServerDispatcher dispatcher)
=> _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateDeviceAuthorizationRequestContext>()
.UseScopedHandler<ValidateDeviceAuthentication>()
.SetOrder(ValidateScopes.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public async ValueTask HandleAsync(ValidateDeviceAuthorizationRequestContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(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;
}
}
}
/// <summary>
/// Contains the logic responsible for rejecting device requests made by
/// applications that haven't been granted the device authorization endpoint permission.
/// Note: this handler is not used when the degraded mode is enabled.
/// </summary>
public sealed class ValidateEndpointPermissions : IOpenIddictServerHandler<ValidateDeviceAuthorizationRequestContext>
{
private readonly IOpenIddictApplicationManager _applicationManager;
public ValidateEndpointPermissions() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
public ValidateEndpointPermissions(IOpenIddictApplicationManager applicationManager)
=> _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateDeviceAuthorizationRequestContext>()
.AddFilter<RequireClientIdParameter>()
.AddFilter<RequireDegradedModeDisabled>()
.AddFilter<RequireEndpointPermissionsEnabled>()
.UseScopedHandler<ValidateEndpointPermissions>()
.SetOrder(ValidateDeviceAuthentication.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public async ValueTask HandleAsync(ValidateDeviceAuthorizationRequestContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
var application = await _applicationManager.FindByClientIdAsync(context.ClientId) ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0032));
// Reject the request if the application is not allowed to use the device authorization endpoint.
//
// Note: the legacy "ept:device" permission is still allowed for backward compatibility.
if (!await _applicationManager.HasPermissionAsync(application, Permissions.Endpoints.DeviceAuthorization) &&
!await _applicationManager.HasPermissionAsync(application, "ept:device"))
{
context.Logger.LogInformation(6062, SR.GetResourceString(SR.ID6062), context.ClientId);
context.Reject(
error: Errors.UnauthorizedClient,
description: SR.GetResourceString(SR.ID2056),
uri: SR.FormatID8000(SR.ID2056));
return;
}
}
}
/// <summary>
/// Contains the logic responsible for rejecting device requests made by unauthorized applications.
/// Note: this handler is not used when the degraded mode is enabled or when grant type permissions are disabled.
/// </summary>
public sealed class ValidateGrantTypePermissions : IOpenIddictServerHandler<ValidateDeviceAuthorizationRequestContext>
{
private readonly IOpenIddictApplicationManager _applicationManager;
public ValidateGrantTypePermissions() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
public ValidateGrantTypePermissions(IOpenIddictApplicationManager applicationManager)
=> _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateDeviceAuthorizationRequestContext>()
.AddFilter<RequireGrantTypePermissionsEnabled>()
.AddFilter<RequireDegradedModeDisabled>()
.UseScopedHandler<ValidateGrantTypePermissions>()
.SetOrder(ValidateEndpointPermissions.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public async ValueTask HandleAsync(ValidateDeviceAuthorizationRequestContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
var application = await _applicationManager.FindByClientIdAsync(context.ClientId) ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0032));
// Reject the request if the application is not allowed to use the device code grant.
if (!await _applicationManager.HasPermissionAsync(application, Permissions.GrantTypes.DeviceCode))
{
context.Logger.LogInformation(6118, SR.GetResourceString(SR.ID6118), context.ClientId);
context.Reject(
error: Errors.UnauthorizedClient,
description: SR.GetResourceString(SR.ID2027),
uri: SR.FormatID8000(SR.ID2027));
return;
}
// Reject the request if the offline_access scope was request and
// if the application is not allowed to use the refresh token grant.
if (context.Request.HasScope(Scopes.OfflineAccess) &&
!await _applicationManager.HasPermissionAsync(application, Permissions.GrantTypes.RefreshToken))
{
context.Logger.LogInformation(6120, SR.GetResourceString(SR.ID6120), context.ClientId, Scopes.OfflineAccess);
context.Reject(
error: Errors.InvalidRequest,
description: SR.FormatID2065(Scopes.OfflineAccess),
uri: SR.FormatID8000(SR.ID2065));
return;
}
}
}
/// <summary>
/// Contains the logic responsible for 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 sealed class ValidateScopePermissions : IOpenIddictServerHandler<ValidateDeviceAuthorizationRequestContext>
{
private readonly IOpenIddictApplicationManager _applicationManager;
public ValidateScopePermissions() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0016));
public ValidateScopePermissions(IOpenIddictApplicationManager applicationManager)
=> _applicationManager = applicationManager ?? throw new ArgumentNullException(nameof(applicationManager));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateDeviceAuthorizationRequestContext>()
.AddFilter<RequireClientIdParameter>()
.AddFilter<RequireDegradedModeDisabled>()
.AddFilter<RequireScopePermissionsEnabled>()
.UseScopedHandler<ValidateScopePermissions>()
.SetOrder(ValidateGrantTypePermissions.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public async ValueTask HandleAsync(ValidateDeviceAuthorizationRequestContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
Debug.Assert(!string.IsNullOrEmpty(context.ClientId), SR.FormatID4000(Parameters.ClientId));
var application = await _applicationManager.FindByClientIdAsync(context.ClientId) ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0032));
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.LogInformation(6063, SR.GetResourceString(SR.ID6063), context.ClientId, scope);
context.Reject(
error: Errors.InvalidRequest,
description: SR.GetResourceString(SR.ID2051),
uri: SR.FormatID8000(SR.ID2051));
return;
}
}
}
}
/// <summary>
/// Contains the logic responsible for extracting end-user verification requests and invoking the corresponding event handlers.
/// </summary>
public sealed class ExtractEndUserVerificationRequest : IOpenIddictServerHandler<ProcessRequestContext>
{
private readonly IOpenIddictServerDispatcher _dispatcher;
public ExtractEndUserVerificationRequest(IOpenIddictServerDispatcher dispatcher)
=> _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ProcessRequestContext>()
.AddFilter<RequireEndUserVerificationRequest>()
.UseScopedHandler<ExtractEndUserVerificationRequest>()
.SetOrder(100_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public async ValueTask HandleAsync(ProcessRequestContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
var notification = new ExtractEndUserVerificationRequestContext(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.ID0034));
}
context.Logger.LogInformation(6064, SR.GetResourceString(SR.ID6064), notification.Request);
}
}
/// <summary>
/// Contains the logic responsible for validating end-user verification requests and invoking the corresponding event handlers.
/// </summary>
public sealed class ValidateEndUserVerificationRequest : IOpenIddictServerHandler<ProcessRequestContext>
{
private readonly IOpenIddictServerDispatcher _dispatcher;
public ValidateEndUserVerificationRequest(IOpenIddictServerDispatcher dispatcher)
=> _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ProcessRequestContext>()
.AddFilter<RequireEndUserVerificationRequest>()
.UseScopedHandler<ValidateEndUserVerificationRequest>()
.SetOrder(ExtractEndUserVerificationRequest.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public async ValueTask HandleAsync(ProcessRequestContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
var notification = new ValidateEndUserVerificationRequestContext(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 context without triggering a new validation process.
context.Transaction.SetProperty(typeof(ValidateEndUserVerificationRequestContext).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;
}
context.Logger.LogInformation(6065, SR.GetResourceString(SR.ID6065));
}
}
/// <summary>
/// Contains the logic responsible for handling end-user verification requests and invoking the corresponding event handlers.
/// </summary>
public sealed class HandleEndUserVerificationRequest : IOpenIddictServerHandler<ProcessRequestContext>
{
private readonly IOpenIddictServerDispatcher _dispatcher;
public HandleEndUserVerificationRequest(IOpenIddictServerDispatcher dispatcher)
=> _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ProcessRequestContext>()
.AddFilter<RequireEndUserVerificationRequest>()
.UseScopedHandler<HandleEndUserVerificationRequest>()
.SetOrder(ValidateEndUserVerificationRequest.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public async ValueTask HandleAsync(ProcessRequestContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
var notification = new HandleEndUserVerificationRequestContext(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.Principal is not null)
{
var @event = new ProcessSignInContext(context.Transaction)
{
Principal = notification.Principal,
Response = new OpenIddictResponse()
};
if (notification.Parameters.Count is > 0)
{
foreach (var parameter in notification.Parameters)
{
@event.Parameters.Add(parameter.Key, parameter.Value);
}
}
await _dispatcher.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(SR.GetResourceString(SR.ID0035));
}
}
/// <summary>
/// Contains the logic responsible for processing sign-in responses and invoking the corresponding event handlers.
/// </summary>
public sealed class ApplyEndUserVerificationResponse<TContext> : IOpenIddictServerHandler<TContext> where TContext : BaseRequestContext
{
private readonly IOpenIddictServerDispatcher _dispatcher;
public ApplyEndUserVerificationResponse(IOpenIddictServerDispatcher dispatcher)
=> _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<TContext>()
.AddFilter<RequireEndUserVerificationRequest>()
.UseScopedHandler<ApplyEndUserVerificationResponse<TContext>>()
.SetOrder(500_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public async ValueTask HandleAsync(TContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
var notification = new ApplyEndUserVerificationResponseContext(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.ID0036));
}
}
/// <summary>
/// Contains the logic responsible for applying the authentication logic to end-user verification requests.
/// </summary>
public sealed class ValidateVerificationAuthentication : IOpenIddictServerHandler<ValidateEndUserVerificationRequestContext>
{
private readonly IOpenIddictServerDispatcher _dispatcher;
public ValidateVerificationAuthentication(IOpenIddictServerDispatcher dispatcher)
=> _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<ValidateEndUserVerificationRequestContext>()
.UseScopedHandler<ValidateVerificationAuthentication>()
.SetOrder(int.MinValue + 100_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public async ValueTask HandleAsync(ValidateEndUserVerificationRequestContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(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 principal extracted from the token to the validation context.
context.UserCodePrincipal = notification.UserCodePrincipal;
}
}
/// <summary>
/// Contains the logic responsible for attaching the principal extracted from the user code to the event context.
/// </summary>
public sealed class AttachUserCodePrincipal : IOpenIddictServerHandler<HandleEndUserVerificationRequestContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder<HandleEndUserVerificationRequestContext>()
.UseSingletonHandler<AttachUserCodePrincipal>()
.SetOrder(int.MinValue + 100_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(HandleEndUserVerificationRequestContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
var notification = context.Transaction.GetProperty<ValidateEndUserVerificationRequestContext>(
typeof(ValidateEndUserVerificationRequestContext).FullName!) ??
throw new InvalidOperationException(SR.GetResourceString(SR.ID0007));
context.UserCodePrincipal ??= notification.UserCodePrincipal;
return ValueTask.CompletedTask;
}
}
}
}