Browse Source

Change how authentication demands are marshalled and support token binding certificates/additional token request parameters for interactive flows

pull/2430/head
Kévin Chalet 4 weeks ago
parent
commit
29c6668d45
  1. 28
      sandbox/OpenIddict.Sandbox.Console.Client/InteractiveService.cs
  2. 673
      src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.cs
  3. 5
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs
  4. 6
      src/OpenIddict.Client/OpenIddictClientEvents.cs
  5. 1
      src/OpenIddict.Client/OpenIddictClientExtensions.cs
  6. 14
      src/OpenIddict.Client/OpenIddictClientHandlerFilters.cs
  7. 16
      src/OpenIddict.Client/OpenIddictClientHandlers.cs
  8. 22
      src/OpenIddict.Client/OpenIddictClientModels.cs
  9. 5
      src/OpenIddict.Client/OpenIddictClientService.cs

28
sandbox/OpenIddict.Sandbox.Console.Client/InteractiveService.cs

@ -44,6 +44,19 @@ public class InteractiveService : BackgroundService
if (await AuthenticateUserInteractivelyAsync(registration, configuration, stoppingToken)) if (await AuthenticateUserInteractivelyAsync(registration, configuration, stoppingToken))
{ {
// Note: the OpenIddict server stack supports mTLS-based token binding for public clients:
// while these clients cannot authenticate using a TLS client certificate, the certificate
// can be used to bind the refresh (and access) tokens returned by the authorization server
// to the client application, which prevents such tokens from being used without providing a
// proof-of-possession matching the TLS client certificate used when the token was acquired.
//
// While this sample deliberately doesn't store the generated certificate in a persistent
// location, the certificate used for token binding should typically be stored in the user
// certificate store to be reloaded across application restarts in a real-world application.
var certificate = configuration.TlsClientCertificateBoundAccessTokens is true
? GenerateEphemeralTlsClientCertificate()
: null;
var flow = await GetSelectedFlowAsync(registration, configuration, stoppingToken); var flow = await GetSelectedFlowAsync(registration, configuration, stoppingToken);
AnsiConsole.MarkupLine("[cyan]Launching the system browser.[/]"); AnsiConsole.MarkupLine("[cyan]Launching the system browser.[/]");
@ -64,7 +77,8 @@ public class InteractiveService : BackgroundService
var response = await _service.AuthenticateInteractivelyAsync(new() var response = await _service.AuthenticateInteractivelyAsync(new()
{ {
CancellationToken = stoppingToken, CancellationToken = stoppingToken,
Nonce = result.Nonce Nonce = result.Nonce,
TokenBindingCertificate = certificate
}); });
AnsiConsole.MarkupLine("[green]Interactive authentication successful:[/]"); AnsiConsole.MarkupLine("[green]Interactive authentication successful:[/]");
@ -112,7 +126,8 @@ public class InteractiveService : BackgroundService
{ {
CancellationToken = stoppingToken, CancellationToken = stoppingToken,
ProviderName = provider, ProviderName = provider,
RefreshToken = response.RefreshToken RefreshToken = response.RefreshToken,
TokenBindingCertificate = certificate
})).Principal)); })).Principal));
} }
@ -151,15 +166,6 @@ public class InteractiveService : BackgroundService
var type = await GetSelectedGrantTypeAsync(registration, configuration, stoppingToken); var type = await GetSelectedGrantTypeAsync(registration, configuration, stoppingToken);
if (type is GrantTypes.DeviceCode) if (type is GrantTypes.DeviceCode)
{ {
// Note: the OpenIddict server stack supports mTLS-based token binding for public clients:
// while these clients cannot authenticate using a TLS client certificate, the certificate
// can be used to bind the refresh (and access) tokens returned by the authorization server
// to the client application, which prevents such tokens from being used without providing a
// proof-of-possession matching the TLS client certificate used when the token was acquired.
//
// While this sample deliberately doesn't store the generated certificate in a persistent
// location, the certificate used for token binding should typically be stored in the user
// certificate store to be reloaded across application restarts in a real-world application.
var certificate = configuration.TlsClientCertificateBoundAccessTokens is true var certificate = configuration.TlsClientCertificateBoundAccessTokens is true
? GenerateEphemeralTlsClientCertificate() ? GenerateEphemeralTlsClientCertificate()
: null; : null;

673
src/OpenIddict.Client.SystemIntegration/OpenIddictClientSystemIntegrationHandlers.cs

@ -40,25 +40,20 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
WaitMarshalledAuthentication.Descriptor, WaitMarshalledAuthentication.Descriptor,
RestoreClientRegistrationFromMarshalledContext.Descriptor, RestoreClientRegistrationFromMarshalledContext.Descriptor,
RestoreStateTokenFromMarshalledAuthentication.Descriptor,
RestoreStateTokenPrincipalFromMarshalledAuthentication.Descriptor, EvaluateValidatedUpfrontTokensForMarshalledContext.Descriptor,
RestoreHostAuthenticationPropertiesFromMarshalledAuthentication.Descriptor, ResolveValidatedStateTokenFromMarshalledContext.Descriptor,
EvaluateValidatedFrontchannelTokensForMarshalledContext.Descriptor,
ResolveValidatedFrontchannelTokensFromMarshalledContext.Descriptor,
EvaluateValidatedBackchannelTokensForMarshalledContext.Descriptor,
DisableStateTokenRedeeming.Descriptor,
DisableTokenRequestSending.Descriptor,
DisableUserInfoRequestSending.Descriptor,
RedirectProtocolActivation.Descriptor, RedirectProtocolActivation.Descriptor,
ResolveRequestForgeryProtection.Descriptor, ResolveRequestForgeryProtection.Descriptor,
RestoreFrontchannelTokensFromMarshalledAuthentication.Descriptor,
RestoreFrontchannelIdentityTokenPrincipalFromMarshalledAuthentication.Descriptor,
RestoreFrontchannelAccessTokenPrincipalFromMarshalledAuthentication.Descriptor,
RestoreAuthorizationCodePrincipalFromMarshalledAuthentication.Descriptor,
RestoreTokenResponseFromMarshalledAuthentication.Descriptor,
RestoreBackchannelTokensFromMarshalledAuthentication.Descriptor,
RestoreBackchannelIdentityTokenPrincipalFromMarshalledAuthentication.Descriptor,
RestoreBackchannelAccessTokenPrincipalFromMarshalledAuthentication.Descriptor,
RestoreRefreshTokenPrincipalFromMarshalledAuthentication.Descriptor,
RestoreUserInfoDetailsFromMarshalledAuthentication.Descriptor,
RestoreMergedPrincipalFromMarshalledAuthentication.Descriptor,
CompleteAuthenticationOperation.Descriptor, CompleteAuthenticationOperation.Descriptor,
UntrackMarshalledAuthenticationOperation.Descriptor, UntrackMarshalledAuthenticationOperation.Descriptor,
@ -613,7 +608,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
throw new InvalidOperationException(SR.GetResourceString(SR.ID0379)); throw new InvalidOperationException(SR.GetResourceString(SR.ID0379));
} }
// At this point, user authentication demands cannot complete until the authorization response has been // At this point, the user authentication demand cannot complete until the authorization response has been
// returned to the redirection endpoint (materialized as a registered protocol activation URI) and handled // returned to the redirection endpoint (materialized as a registered protocol activation URI) and handled
// by OpenIddict via the ProcessRequest event. Since it is asynchronous by nature, this process requires // by OpenIddict via the ProcessRequest event. Since it is asynchronous by nature, this process requires
// using a signal mechanism to unblock the authentication operation once it is complete. For that, the // using a signal mechanism to unblock the authentication operation once it is complete. For that, the
@ -697,8 +692,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
(context.Configuration, context.Registration) = context.EndpointType switch (context.Configuration, context.Registration) = context.EndpointType switch
{ {
// When the authentication context is marshalled, restore the // When the authentication demand is marshalled from a different context,
// issuer registration and configuration from the other instance. // restore the registration and configuration from the other instance.
OpenIddictClientEndpointType.Unknown when _marshal.TryGetResult(context.Nonce, out var notification) OpenIddictClientEndpointType.Unknown when _marshal.TryGetResult(context.Nonce, out var notification)
=> (notification.Configuration, notification.Registration), => (notification.Configuration, notification.Registration),
@ -710,14 +705,14 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
} }
/// <summary> /// <summary>
/// Contains the logic responsible for restoring the state token /// Contains the logic responsible for determining the types of
/// from the marshalled authentication context, if applicable. /// tokens to validate upfront when the context is marshalled.
/// </summary> /// </summary>
public sealed class RestoreStateTokenFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext> public sealed class EvaluateValidatedUpfrontTokensForMarshalledContext : IOpenIddictClientHandler<ProcessAuthenticationContext>
{ {
private readonly OpenIddictClientSystemIntegrationMarshal _marshal; private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public RestoreStateTokenFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal) public EvaluateValidatedUpfrontTokensForMarshalledContext(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal)); => _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal));
/// <summary> /// <summary>
@ -726,8 +721,49 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
public static OpenIddictClientHandlerDescriptor Descriptor { get; } public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>() = OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireAuthenticationNonce>() .AddFilter<RequireAuthenticationNonce>()
.UseSingletonHandler<RestoreStateTokenFromMarshalledAuthentication>() .UseSingletonHandler<EvaluateValidatedUpfrontTokensForMarshalledContext>()
.SetOrder(ResolveValidatedStateToken.Descriptor.Order + 500) .SetOrder(EvaluateValidatedUpfrontTokens.Descriptor.Order + 250)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context)
{
ArgumentNullException.ThrowIfNull(context);
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
// When the authentication demand is marshalled from a different context, always
// extract and validate the state token to ensure the authentication details
// contained in the state token principal can be used to validate the operation.
if (context.EndpointType is OpenIddictClientEndpointType.Unknown && _marshal.IsTracked(context.Nonce))
{
context.ExtractStateToken = context.RequireStateToken = true;
context.ValidateStateToken = context.RejectStateToken = true;
}
return ValueTask.CompletedTask;
}
}
/// <summary>
/// Contains the logic responsible for resolving the state token to validate upfront from the marshalled context.
/// </summary>
public sealed class ResolveValidatedStateTokenFromMarshalledContext : IOpenIddictClientHandler<ProcessAuthenticationContext>
{
private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public ResolveValidatedStateTokenFromMarshalledContext(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireAuthenticationNonce>()
.UseSingletonHandler<ResolveValidatedStateTokenFromMarshalledContext>()
.SetOrder(ResolveValidatedStateToken.Descriptor.Order + 250)
.SetType(OpenIddictClientHandlerType.BuiltIn) .SetType(OpenIddictClientHandlerType.BuiltIn)
.Build(); .Build();
@ -740,12 +776,12 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
context.StateToken = context.EndpointType switch context.StateToken = context.EndpointType switch
{ {
// When the authentication context is marshalled, restore the state token from the other instance. // When the authentication demand is marshalled from a different context,
// always restore the state token from the instance that extracted it.
OpenIddictClientEndpointType.Unknown when _marshal.TryGetResult(context.Nonce, out var notification) OpenIddictClientEndpointType.Unknown when _marshal.TryGetResult(context.Nonce, out var notification)
=> notification.StateToken, => notification.StateToken,
// Otherwise, don't alter the current context. _ => null
_ => context.StateToken
}; };
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
@ -753,14 +789,14 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
} }
/// <summary> /// <summary>
/// Contains the logic responsible for restoring the state token /// Contains the logic responsible for determining the set of
/// principal from the marshalled authentication context, if applicable. /// frontchannel tokens to validate when the context is marshalled.
/// </summary> /// </summary>
public sealed class RestoreStateTokenPrincipalFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext> public sealed class EvaluateValidatedFrontchannelTokensForMarshalledContext : IOpenIddictClientHandler<ProcessAuthenticationContext>
{ {
private readonly OpenIddictClientSystemIntegrationMarshal _marshal; private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public RestoreStateTokenPrincipalFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal) public EvaluateValidatedFrontchannelTokensForMarshalledContext(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal)); => _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal));
/// <summary> /// <summary>
@ -769,8 +805,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
public static OpenIddictClientHandlerDescriptor Descriptor { get; } public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>() = OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireAuthenticationNonce>() .AddFilter<RequireAuthenticationNonce>()
.UseSingletonHandler<RestoreStateTokenPrincipalFromMarshalledAuthentication>() .UseSingletonHandler<EvaluateValidatedFrontchannelTokensForMarshalledContext>()
.SetOrder(ValidateStateToken.Descriptor.Order + 500) .SetOrder(EvaluateValidatedFrontchannelTokens.Descriptor.Order + 250)
.SetType(OpenIddictClientHandlerType.BuiltIn) .SetType(OpenIddictClientHandlerType.BuiltIn)
.Build(); .Build();
@ -781,30 +817,30 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019)); Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
context.StateTokenPrincipal = context.EndpointType switch // When the authentication demand is expected to be marshalled to a different context,
// always skip the validation of all the frontchannel tokens by default as the security
// principals they contain are not needed to marshal the authentication demand.
if (context.EndpointType is
OpenIddictClientEndpointType.Redirection or
OpenIddictClientEndpointType.PostLogoutRedirection && _marshal.IsTracked(context.Nonce))
{ {
// When the authentication context is marshalled, restore context.ValidateAuthorizationCode = context.RejectAuthorizationCode = false;
// the state token principal from the other instance. context.ValidateFrontchannelAccessToken = context.RejectFrontchannelAccessToken = false;
OpenIddictClientEndpointType.Unknown when _marshal.TryGetResult(context.Nonce, out var notification) context.ValidateFrontchannelIdentityToken = context.RejectFrontchannelIdentityToken = false;
=> notification.StateTokenPrincipal, }
// Otherwise, don't alter the current context.
_ => context.StateTokenPrincipal
};
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
} }
} }
/// <summary> /// <summary>
/// Contains the logic responsible for restoring the host authentication /// Contains the logic responsible for resolving the frontchannel tokens from the marshalled context.
/// properties from the marshalled authentication context, if applicable.
/// </summary> /// </summary>
public sealed class RestoreHostAuthenticationPropertiesFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext> public sealed class ResolveValidatedFrontchannelTokensFromMarshalledContext : IOpenIddictClientHandler<ProcessAuthenticationContext>
{ {
private readonly OpenIddictClientSystemIntegrationMarshal _marshal; private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public RestoreHostAuthenticationPropertiesFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal) public ResolveValidatedFrontchannelTokensFromMarshalledContext(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal)); => _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal));
/// <summary> /// <summary>
@ -813,8 +849,8 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
public static OpenIddictClientHandlerDescriptor Descriptor { get; } public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>() = OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireAuthenticationNonce>() .AddFilter<RequireAuthenticationNonce>()
.UseSingletonHandler<RestoreHostAuthenticationPropertiesFromMarshalledAuthentication>() .UseSingletonHandler<ResolveValidatedFrontchannelTokensFromMarshalledContext>()
.SetOrder(ResolveHostAuthenticationPropertiesFromStateToken.Descriptor.Order + 500) .SetOrder(ResolveValidatedFrontchannelTokens.Descriptor.Order + 250)
.SetType(OpenIddictClientHandlerType.BuiltIn) .SetType(OpenIddictClientHandlerType.BuiltIn)
.Build(); .Build();
@ -825,21 +861,271 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019)); Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
// When the authentication context is marshalled, restore the // When the authentication context is marshalled, restore the frontchannel tokens from the other instance.
// host authentication properties from the other instance.
if (context.EndpointType is OpenIddictClientEndpointType.Unknown && if (context.EndpointType is OpenIddictClientEndpointType.Unknown &&
_marshal.TryGetResult(context.Nonce, out var notification)) _marshal.TryGetResult(context.Nonce, out var notification))
{ {
foreach (var property in notification.Properties) context.AuthorizationCode = notification.AuthorizationCode;
context.FrontchannelAccessToken = notification.FrontchannelAccessToken;
context.FrontchannelAccessTokenExpirationDate = notification.FrontchannelAccessTokenExpirationDate;
context.FrontchannelIdentityToken = notification.FrontchannelIdentityToken;
}
return ValueTask.CompletedTask;
}
}
/// <summary>
/// Contains the logic responsible for determining the set of
/// backchannel tokens to validate when the context is marshalled.
/// </summary>
public sealed class EvaluateValidatedBackchannelTokensForMarshalledContext : IOpenIddictClientHandler<ProcessAuthenticationContext>
{
private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public EvaluateValidatedBackchannelTokensForMarshalledContext(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireAuthenticationNonce>()
.UseSingletonHandler<EvaluateValidatedBackchannelTokensForMarshalledContext>()
.SetOrder(EvaluateValidatedBackchannelTokens.Descriptor.Order + 250)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context)
{
ArgumentNullException.ThrowIfNull(context);
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
// When the authentication demand is expected to be marshalled to a different context,
// always skip the validation of all the backchannel tokens by default as the security
// principals they contain are not needed to marshal the authentication demand.
if (context.EndpointType is
OpenIddictClientEndpointType.Redirection or
OpenIddictClientEndpointType.PostLogoutRedirection && _marshal.IsTracked(context.Nonce))
{
context.ValidateBackchannelAccessToken = context.RejectBackchannelAccessToken = false;
context.ValidateBackchannelIdentityToken = context.RejectBackchannelIdentityToken = false;
context.ValidateIssuedToken = context.RejectIssuedToken = false;
context.ValidateRefreshToken = context.RejectRefreshToken = false;
}
return ValueTask.CompletedTask;
}
}
/// <summary>
/// Contains the logic responsible for disabling the redeeming of the state token, if applicable.
/// </summary>
public sealed class DisableStateTokenRedeeming : IOpenIddictClientHandler<ProcessAuthenticationContext>
{
private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public DisableStateTokenRedeeming(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireAuthenticationNonce>()
.UseSingletonHandler<DisableStateTokenRedeeming>()
.SetOrder(RedeemStateTokenEntry.Descriptor.Order - 250)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context)
{
ArgumentNullException.ThrowIfNull(context);
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
context.DisableStateTokenRedeeming = context.EndpointType switch
{ {
context.Properties[property.Key] = property.Value; // When the authentication demand is expected to be marshalled to a different context,
// disable the redeeming of the state token to ensure it is not in an invalid state
// when the marshalled authentication demand is processed by the other instance.
OpenIddictClientEndpointType.Redirection or
OpenIddictClientEndpointType.PostLogoutRedirection when _marshal.IsTracked(context.Nonce)
=> true,
_ => context.DisableStateTokenRedeeming
};
return ValueTask.CompletedTask;
} }
} }
/// <summary>
/// Contains the logic responsible for preventing a token request from being sent, if applicable.
/// </summary>
public sealed class DisableTokenRequestSending : IOpenIddictClientHandler<ProcessAuthenticationContext>
{
private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public DisableTokenRequestSending(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireAuthenticationNonce>()
.UseSingletonHandler<DisableTokenRequestSending>()
.SetOrder(EvaluateTokenRequest.Descriptor.Order + 250)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context)
{
ArgumentNullException.ThrowIfNull(context);
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
context.SendTokenRequest = context.EndpointType switch
{
// When the authentication demand is expected to be marshalled to a different
// context, do not send a token request and let the other instance do it.
OpenIddictClientEndpointType.Redirection or
OpenIddictClientEndpointType.PostLogoutRedirection when _marshal.IsTracked(context.Nonce)
=> false,
_ => context.SendTokenRequest
};
return ValueTask.CompletedTask;
}
}
/// <summary>
/// Contains the logic responsible for preventing a userinfo request from being sent, if applicable.
/// </summary>
public sealed class DisableUserInfoRequestSending : IOpenIddictClientHandler<ProcessAuthenticationContext>
{
private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public DisableUserInfoRequestSending(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireAuthenticationNonce>()
.UseSingletonHandler<DisableUserInfoRequestSending>()
.SetOrder(EvaluateUserInfoRequest.Descriptor.Order + 250)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context)
{
ArgumentNullException.ThrowIfNull(context);
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
context.SendUserInfoRequest = context.EndpointType switch
{
// When the authentication demand is expected to be marshalled to a different
// context, do not send a userinfo request and let the other instance do it.
OpenIddictClientEndpointType.Redirection or
OpenIddictClientEndpointType.PostLogoutRedirection when _marshal.IsTracked(context.Nonce)
=> false,
_ => context.SendUserInfoRequest
};
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
} }
} }
/// <summary>
/// Contains the logic responsible for restoring the state token
/// from the marshalled authentication context, if applicable.
/// </summary>
[Obsolete("This class is obsolete and will be removed in a future version.")]
public sealed class RestoreStateTokenFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext>
{
public RestoreStateTokenFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal)
=> throw new NotSupportedException(SR.GetResourceString(SR.ID0403));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireAuthenticationNonce>()
.UseSingletonHandler<RestoreStateTokenFromMarshalledAuthentication>()
.SetOrder(ResolveValidatedStateToken.Descriptor.Order + 500)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context) => ValueTask.CompletedTask;
}
/// <summary>
/// Contains the logic responsible for restoring the state token
/// principal from the marshalled authentication context, if applicable.
/// </summary>
[Obsolete("This class is obsolete and will be removed in a future version.")]
public sealed class RestoreStateTokenPrincipalFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext>
{
public RestoreStateTokenPrincipalFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal)
=> throw new NotSupportedException(SR.GetResourceString(SR.ID0403));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireAuthenticationNonce>()
.UseSingletonHandler<RestoreStateTokenPrincipalFromMarshalledAuthentication>()
.SetOrder(ValidateStateToken.Descriptor.Order + 500)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context) => ValueTask.CompletedTask;
}
/// <summary>
/// Contains the logic responsible for restoring the host authentication
/// properties from the marshalled authentication context, if applicable.
/// </summary>
[Obsolete("This class is obsolete and will be removed in a future version.")]
public sealed class RestoreHostAuthenticationPropertiesFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext>
{
public RestoreHostAuthenticationPropertiesFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal)
=> throw new NotSupportedException(SR.GetResourceString(SR.ID0403));
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireAuthenticationNonce>()
.UseSingletonHandler<RestoreHostAuthenticationPropertiesFromMarshalledAuthentication>()
.SetOrder(ResolveHostAuthenticationPropertiesFromStateToken.Descriptor.Order + 500)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context) => ValueTask.CompletedTask;
}
/// <summary> /// <summary>
/// Contains the logic responsible for redirecting the protocol activation to /// Contains the logic responsible for redirecting the protocol activation to
/// the instance that initially started the authentication demand, if applicable. /// the instance that initially started the authentication demand, if applicable.
@ -984,12 +1270,11 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
/// Contains the logic responsible for restoring the frontchannel tokens /// Contains the logic responsible for restoring the frontchannel tokens
/// from the marshalled authentication context, if applicable. /// from the marshalled authentication context, if applicable.
/// </summary> /// </summary>
[Obsolete("This class is obsolete and will be removed in a future version.")]
public sealed class RestoreFrontchannelTokensFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext> public sealed class RestoreFrontchannelTokensFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext>
{ {
private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public RestoreFrontchannelTokensFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal) public RestoreFrontchannelTokensFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal)); => throw new NotSupportedException(SR.GetResourceString(SR.ID0403));
/// <summary> /// <summary>
/// Gets the default descriptor definition assigned to this handler. /// Gets the default descriptor definition assigned to this handler.
@ -1003,38 +1288,18 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
.Build(); .Build();
/// <inheritdoc/> /// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context) public ValueTask HandleAsync(ProcessAuthenticationContext context) => ValueTask.CompletedTask;
{
ArgumentNullException.ThrowIfNull(context);
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
(context.AuthorizationCode,
context.FrontchannelAccessToken,
context.FrontchannelIdentityToken) = context.EndpointType switch
{
// When the authentication context is marshalled, restore the tokens from the other instance.
OpenIddictClientEndpointType.Unknown when _marshal.TryGetResult(context.Nonce, out var notification)
=> (notification.AuthorizationCode, notification.FrontchannelAccessToken, notification.FrontchannelIdentityToken),
// Otherwise, don't alter the current context.
_ => (context.AuthorizationCode, context.FrontchannelAccessToken, context.FrontchannelIdentityToken)
};
return ValueTask.CompletedTask;
}
} }
/// <summary> /// <summary>
/// Contains the logic responsible for restoring the frontchannel identity /// Contains the logic responsible for restoring the frontchannel identity
/// token principal from the marshalled authentication context, if applicable. /// token principal from the marshalled authentication context, if applicable.
/// </summary> /// </summary>
[Obsolete("This class is obsolete and will be removed in a future version.")]
public sealed class RestoreFrontchannelIdentityTokenPrincipalFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext> public sealed class RestoreFrontchannelIdentityTokenPrincipalFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext>
{ {
private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public RestoreFrontchannelIdentityTokenPrincipalFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal) public RestoreFrontchannelIdentityTokenPrincipalFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal)); => throw new NotSupportedException(SR.GetResourceString(SR.ID0403));
/// <summary> /// <summary>
/// Gets the default descriptor definition assigned to this handler. /// Gets the default descriptor definition assigned to this handler.
@ -1048,37 +1313,18 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
.Build(); .Build();
/// <inheritdoc/> /// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context) public ValueTask HandleAsync(ProcessAuthenticationContext context) => ValueTask.CompletedTask;
{
ArgumentNullException.ThrowIfNull(context);
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
context.FrontchannelIdentityTokenPrincipal = context.EndpointType switch
{
// When the authentication context is marshalled, restore the
// frontchannel identity token principal from the other instance.
OpenIddictClientEndpointType.Unknown when _marshal.TryGetResult(context.Nonce, out var notification)
=> notification.FrontchannelIdentityTokenPrincipal,
// Otherwise, don't alter the current context.
_ => context.FrontchannelIdentityTokenPrincipal
};
return ValueTask.CompletedTask;
}
} }
/// <summary> /// <summary>
/// Contains the logic responsible for restoring the frontchannel access /// Contains the logic responsible for restoring the frontchannel access
/// token principal from the marshalled authentication context, if applicable. /// token principal from the marshalled authentication context, if applicable.
/// </summary> /// </summary>
[Obsolete("This class is obsolete and will be removed in a future version.")]
public sealed class RestoreFrontchannelAccessTokenPrincipalFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext> public sealed class RestoreFrontchannelAccessTokenPrincipalFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext>
{ {
private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public RestoreFrontchannelAccessTokenPrincipalFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal) public RestoreFrontchannelAccessTokenPrincipalFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal)); => throw new NotSupportedException(SR.GetResourceString(SR.ID0403));
/// <summary> /// <summary>
/// Gets the default descriptor definition assigned to this handler. /// Gets the default descriptor definition assigned to this handler.
@ -1092,37 +1338,18 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
.Build(); .Build();
/// <inheritdoc/> /// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context) public ValueTask HandleAsync(ProcessAuthenticationContext context) => ValueTask.CompletedTask;
{
ArgumentNullException.ThrowIfNull(context);
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
context.FrontchannelAccessTokenPrincipal = context.EndpointType switch
{
// When the authentication context is marshalled, restore the
// frontchannel access token principal from the other instance.
OpenIddictClientEndpointType.Unknown when _marshal.TryGetResult(context.Nonce, out var notification)
=> notification.FrontchannelAccessTokenPrincipal,
// Otherwise, don't alter the current context.
_ => context.FrontchannelAccessTokenPrincipal
};
return ValueTask.CompletedTask;
}
} }
/// <summary> /// <summary>
/// Contains the logic responsible for restoring the authorization code /// Contains the logic responsible for restoring the authorization code
/// principal from the marshalled authentication context, if applicable. /// principal from the marshalled authentication context, if applicable.
/// </summary> /// </summary>
[Obsolete("This class is obsolete and will be removed in a future version.")]
public sealed class RestoreAuthorizationCodePrincipalFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext> public sealed class RestoreAuthorizationCodePrincipalFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext>
{ {
private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public RestoreAuthorizationCodePrincipalFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal) public RestoreAuthorizationCodePrincipalFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal)); => throw new NotSupportedException(SR.GetResourceString(SR.ID0403));
/// <summary> /// <summary>
/// Gets the default descriptor definition assigned to this handler. /// Gets the default descriptor definition assigned to this handler.
@ -1136,37 +1363,18 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
.Build(); .Build();
/// <inheritdoc/> /// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context) public ValueTask HandleAsync(ProcessAuthenticationContext context) => ValueTask.CompletedTask;
{
ArgumentNullException.ThrowIfNull(context);
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
context.AuthorizationCodePrincipal = context.EndpointType switch
{
// When the authentication context is marshalled, restore the
// authorization code principal from the other instance.
OpenIddictClientEndpointType.Unknown when _marshal.TryGetResult(context.Nonce, out var notification)
=> notification.AuthorizationCodePrincipal,
// Otherwise, don't alter the current context.
_ => context.AuthorizationCodePrincipal
};
return ValueTask.CompletedTask;
}
} }
/// <summary> /// <summary>
/// Contains the logic responsible for restoring the token response /// Contains the logic responsible for restoring the token response
/// from the marshalled authentication context, if applicable. /// from the marshalled authentication context, if applicable.
/// </summary> /// </summary>
[Obsolete("This class is obsolete and will be removed in a future version.")]
public sealed class RestoreTokenResponseFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext> public sealed class RestoreTokenResponseFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext>
{ {
private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public RestoreTokenResponseFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal) public RestoreTokenResponseFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal)); => throw new NotSupportedException(SR.GetResourceString(SR.ID0403));
/// <summary> /// <summary>
/// Gets the default descriptor definition assigned to this handler. /// Gets the default descriptor definition assigned to this handler.
@ -1180,36 +1388,18 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
.Build(); .Build();
/// <inheritdoc/> /// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context) public ValueTask HandleAsync(ProcessAuthenticationContext context) => ValueTask.CompletedTask;
{
ArgumentNullException.ThrowIfNull(context);
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
context.TokenResponse = context.EndpointType switch
{
// When the authentication context is marshalled, restore the token response from the other instance.
OpenIddictClientEndpointType.Unknown when _marshal.TryGetResult(context.Nonce, out var notification)
=> notification.TokenResponse,
// Otherwise, don't alter the current context.
_ => context.TokenResponse
};
return ValueTask.CompletedTask;
}
} }
/// <summary> /// <summary>
/// Contains the logic responsible for restoring the backchannel tokens /// Contains the logic responsible for restoring the backchannel tokens
/// from the marshalled authentication context, if applicable. /// from the marshalled authentication context, if applicable.
/// </summary> /// </summary>
[Obsolete("This class is obsolete and will be removed in a future version.")]
public sealed class RestoreBackchannelTokensFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext> public sealed class RestoreBackchannelTokensFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext>
{ {
private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public RestoreBackchannelTokensFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal) public RestoreBackchannelTokensFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal)); => throw new NotSupportedException(SR.GetResourceString(SR.ID0403));
/// <summary> /// <summary>
/// Gets the default descriptor definition assigned to this handler. /// Gets the default descriptor definition assigned to this handler.
@ -1223,38 +1413,18 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
.Build(); .Build();
/// <inheritdoc/> /// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context) public ValueTask HandleAsync(ProcessAuthenticationContext context) => ValueTask.CompletedTask;
{
ArgumentNullException.ThrowIfNull(context);
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
(context.BackchannelAccessToken,
context.BackchannelIdentityToken,
context.RefreshToken) = context.EndpointType switch
{
// When the authentication context is marshalled, restore the tokens from the other instance.
OpenIddictClientEndpointType.Unknown when _marshal.TryGetResult(context.Nonce, out var notification)
=> (notification.BackchannelAccessToken, notification.BackchannelIdentityToken, notification.RefreshToken),
// Otherwise, don't alter the current context.
_ => (context.BackchannelAccessToken, context.BackchannelIdentityToken, context.RefreshToken)
};
return ValueTask.CompletedTask;
}
} }
/// <summary> /// <summary>
/// Contains the logic responsible for restoring the backchannel identity /// Contains the logic responsible for restoring the backchannel identity
/// token principal from the marshalled authentication context, if applicable. /// token principal from the marshalled authentication context, if applicable.
/// </summary> /// </summary>
[Obsolete("This class is obsolete and will be removed in a future version.")]
public sealed class RestoreBackchannelIdentityTokenPrincipalFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext> public sealed class RestoreBackchannelIdentityTokenPrincipalFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext>
{ {
private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public RestoreBackchannelIdentityTokenPrincipalFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal) public RestoreBackchannelIdentityTokenPrincipalFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal)); => throw new NotSupportedException(SR.GetResourceString(SR.ID0403));
/// <summary> /// <summary>
/// Gets the default descriptor definition assigned to this handler. /// Gets the default descriptor definition assigned to this handler.
@ -1268,37 +1438,18 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
.Build(); .Build();
/// <inheritdoc/> /// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context) public ValueTask HandleAsync(ProcessAuthenticationContext context) => ValueTask.CompletedTask;
{
ArgumentNullException.ThrowIfNull(context);
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
context.BackchannelIdentityTokenPrincipal = context.EndpointType switch
{
// When the authentication context is marshalled, restore the
// frontchannel identity token principal from the other instance.
OpenIddictClientEndpointType.Unknown when _marshal.TryGetResult(context.Nonce, out var notification)
=> notification.BackchannelIdentityTokenPrincipal,
// Otherwise, don't alter the current context.
_ => context.BackchannelIdentityTokenPrincipal
};
return ValueTask.CompletedTask;
}
} }
/// <summary> /// <summary>
/// Contains the logic responsible for restoring the frontchannel access /// Contains the logic responsible for restoring the frontchannel access
/// token principal from the marshalled authentication context, if applicable. /// token principal from the marshalled authentication context, if applicable.
/// </summary> /// </summary>
[Obsolete("This class is obsolete and will be removed in a future version.")]
public sealed class RestoreBackchannelAccessTokenPrincipalFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext> public sealed class RestoreBackchannelAccessTokenPrincipalFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext>
{ {
private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public RestoreBackchannelAccessTokenPrincipalFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal) public RestoreBackchannelAccessTokenPrincipalFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal)); => throw new NotSupportedException(SR.GetResourceString(SR.ID0403));
/// <summary> /// <summary>
/// Gets the default descriptor definition assigned to this handler. /// Gets the default descriptor definition assigned to this handler.
@ -1312,37 +1463,18 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
.Build(); .Build();
/// <inheritdoc/> /// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context) public ValueTask HandleAsync(ProcessAuthenticationContext context) => ValueTask.CompletedTask;
{
ArgumentNullException.ThrowIfNull(context);
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
context.BackchannelAccessTokenPrincipal = context.EndpointType switch
{
// When the authentication context is marshalled, restore the
// frontchannel access token principal from the other instance.
OpenIddictClientEndpointType.Unknown when _marshal.TryGetResult(context.Nonce, out var notification)
=> notification.BackchannelAccessTokenPrincipal,
// Otherwise, don't alter the current context.
_ => context.BackchannelAccessTokenPrincipal
};
return ValueTask.CompletedTask;
}
} }
/// <summary> /// <summary>
/// Contains the logic responsible for restoring the refresh token /// Contains the logic responsible for restoring the refresh token
/// principal from the marshalled authentication context, if applicable. /// principal from the marshalled authentication context, if applicable.
/// </summary> /// </summary>
[Obsolete("This class is obsolete and will be removed in a future version.")]
public sealed class RestoreRefreshTokenPrincipalFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext> public sealed class RestoreRefreshTokenPrincipalFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext>
{ {
private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public RestoreRefreshTokenPrincipalFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal) public RestoreRefreshTokenPrincipalFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal)); => throw new NotSupportedException(SR.GetResourceString(SR.ID0403));
/// <summary> /// <summary>
/// Gets the default descriptor definition assigned to this handler. /// Gets the default descriptor definition assigned to this handler.
@ -1356,37 +1488,18 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
.Build(); .Build();
/// <inheritdoc/> /// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context) public ValueTask HandleAsync(ProcessAuthenticationContext context) => ValueTask.CompletedTask;
{
ArgumentNullException.ThrowIfNull(context);
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
context.RefreshTokenPrincipal = context.EndpointType switch
{
// When the authentication context is marshalled, restore
// the refresh token principal from the other instance.
OpenIddictClientEndpointType.Unknown when _marshal.TryGetResult(context.Nonce, out var notification)
=> notification.RefreshTokenPrincipal,
// Otherwise, don't alter the current context.
_ => context.RefreshTokenPrincipal
};
return ValueTask.CompletedTask;
}
} }
/// <summary> /// <summary>
/// Contains the logic responsible for restoring the userinfo details /// Contains the logic responsible for restoring the userinfo details
/// from the marshalled authentication context, if applicable. /// from the marshalled authentication context, if applicable.
/// </summary> /// </summary>
[Obsolete("This class is obsolete and will be removed in a future version.")]
public sealed class RestoreUserInfoDetailsFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext> public sealed class RestoreUserInfoDetailsFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext>
{ {
private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public RestoreUserInfoDetailsFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal) public RestoreUserInfoDetailsFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal)); => throw new NotSupportedException(SR.GetResourceString(SR.ID0403));
/// <summary> /// <summary>
/// Gets the default descriptor definition assigned to this handler. /// Gets the default descriptor definition assigned to this handler.
@ -1400,35 +1513,17 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
.Build(); .Build();
/// <inheritdoc/> /// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context) public ValueTask HandleAsync(ProcessAuthenticationContext context) => ValueTask.CompletedTask;
{
ArgumentNullException.ThrowIfNull(context);
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
(context.UserInfoResponse, context.UserInfoTokenPrincipal, context.UserInfoToken) = context.EndpointType switch
{
// When the authentication context is marshalled, restore the userinfo details from the other instance.
OpenIddictClientEndpointType.Unknown when _marshal.TryGetResult(context.Nonce, out var notification)
=> (notification.UserInfoResponse, notification.UserInfoTokenPrincipal, notification.UserInfoToken),
// Otherwise, don't alter the current context.
_ => (context.UserInfoResponse, context.UserInfoTokenPrincipal, context.UserInfoToken)
};
return ValueTask.CompletedTask;
}
} }
/// <summary> /// <summary>
/// Contains the logic responsible for restoring the merged principal from the marshalled authentication context, if applicable. /// Contains the logic responsible for restoring the merged principal from the marshalled authentication context, if applicable.
/// </summary> /// </summary>
[Obsolete("This class is obsolete and will be removed in a future version.")]
public sealed class RestoreMergedPrincipalFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext> public sealed class RestoreMergedPrincipalFromMarshalledAuthentication : IOpenIddictClientHandler<ProcessAuthenticationContext>
{ {
private readonly OpenIddictClientSystemIntegrationMarshal _marshal;
public RestoreMergedPrincipalFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal) public RestoreMergedPrincipalFromMarshalledAuthentication(OpenIddictClientSystemIntegrationMarshal marshal)
=> _marshal = marshal ?? throw new ArgumentNullException(nameof(marshal)); => throw new NotSupportedException(SR.GetResourceString(SR.ID0403));
/// <summary> /// <summary>
/// Gets the default descriptor definition assigned to this handler. /// Gets the default descriptor definition assigned to this handler.
@ -1442,24 +1537,7 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
.Build(); .Build();
/// <inheritdoc/> /// <inheritdoc/>
public ValueTask HandleAsync(ProcessAuthenticationContext context) public ValueTask HandleAsync(ProcessAuthenticationContext context) => ValueTask.CompletedTask;
{
ArgumentNullException.ThrowIfNull(context);
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
context.MergedPrincipal = context.EndpointType switch
{
// When the authentication context is marshalled, restore the merged principal from the other instance.
OpenIddictClientEndpointType.Unknown when _marshal.TryGetResult(context.Nonce, out var notification)
=> notification.MergedPrincipal,
// Otherwise, don't alter the current context.
_ => context.MergedPrincipal
};
return ValueTask.CompletedTask;
}
} }
/// <summary> /// <summary>
@ -1492,8 +1570,13 @@ public static partial class OpenIddictClientSystemIntegrationHandlers
Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019)); Debug.Assert(!string.IsNullOrEmpty(context.Nonce), SR.GetResourceString(SR.ID4019));
// Inform the marshal that the authentication demand is complete. if (context.EndpointType is not (OpenIddictClientEndpointType.Redirection or
if (!_marshal.TryComplete(context.Nonce, context)) OpenIddictClientEndpointType.PostLogoutRedirection))
{
return ValueTask.CompletedTask;
}
if (_marshal.IsTracked(context.Nonce) && !_marshal.TryComplete(context.Nonce, context))
{ {
throw new InvalidOperationException(SR.GetResourceString(SR.ID0380)); throw new InvalidOperationException(SR.GetResourceString(SR.ID0380));
} }

5
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.cs

@ -833,8 +833,9 @@ public static partial class OpenIddictClientWebIntegrationHandlers
ProviderTypes.PayPal => (false, false, false), ProviderTypes.PayPal => (false, false, false),
// NetSuite does not return an id_token when using the refresh_token grant type. // NetSuite does not return an id_token when using the refresh_token grant type.
// Additionally, the at_hash inside their id_token is not a valid hash of the //
// access token, but is instead a copy of the RS256 signature within the access token. // Additionally, the at_hash inside the id_token is not a valid hash of the access
// token, but is instead a copy of the RS256 signature within the access token.
ProviderTypes.NetSuite => (true, false, false), ProviderTypes.NetSuite => (true, false, false),
_ => (context.ExtractBackchannelIdentityToken, _ => (context.ExtractBackchannelIdentityToken,

6
src/OpenIddict.Client/OpenIddictClientEvents.cs

@ -441,6 +441,12 @@ public static partial class OpenIddictClientEvents
[Obsolete("This property is no longer used and will be removed in a future version.")] [Obsolete("This property is no longer used and will be removed in a future version.")]
public HashSet<string> UserInfoEndpointTokenBindingMethods { get; } = new(StringComparer.Ordinal); public HashSet<string> UserInfoEndpointTokenBindingMethods { get; } = new(StringComparer.Ordinal);
/// <summary>
/// Gets or sets a boolean indicating whether the token entry associated
/// with the state token should be marked as redeemed in the database.
/// </summary>
public bool DisableStateTokenRedeeming { get; set; }
/// <summary> /// <summary>
/// Gets or sets a boolean indicating whether a token request should be sent. /// Gets or sets a boolean indicating whether a token request should be sent.
/// </summary> /// </summary>

1
src/OpenIddict.Client/OpenIddictClientExtensions.cs

@ -60,6 +60,7 @@ public static class OpenIddictClientExtensions
builder.Services.TryAddSingleton<RequireRevocationClientAssertionGenerated>(); builder.Services.TryAddSingleton<RequireRevocationClientAssertionGenerated>();
builder.Services.TryAddSingleton<RequireRevocationRequest>(); builder.Services.TryAddSingleton<RequireRevocationRequest>();
builder.Services.TryAddSingleton<RequireStateTokenPrincipal>(); builder.Services.TryAddSingleton<RequireStateTokenPrincipal>();
builder.Services.TryAddSingleton<RequireStateTokenRedeemed>();
builder.Services.TryAddSingleton<RequireStateTokenValidated>(); builder.Services.TryAddSingleton<RequireStateTokenValidated>();
builder.Services.TryAddSingleton<RequireTokenAudienceValidationEnabled>(); builder.Services.TryAddSingleton<RequireTokenAudienceValidationEnabled>();
builder.Services.TryAddSingleton<RequireTokenEntryCreated>(); builder.Services.TryAddSingleton<RequireTokenEntryCreated>();

14
src/OpenIddict.Client/OpenIddictClientHandlerFilters.cs

@ -406,6 +406,20 @@ public static class OpenIddictClientHandlerFilters
} }
} }
/// <summary>
/// Represents a filter that excludes the associated handlers if the state token should not be redeemed.
/// </summary>
public sealed class RequireStateTokenRedeemed : IOpenIddictClientHandlerFilter<ProcessAuthenticationContext>
{
/// <inheritdoc/>
public ValueTask<bool> IsActiveAsync(ProcessAuthenticationContext context)
{
ArgumentNullException.ThrowIfNull(context);
return new(!context.DisableStateTokenRedeeming);
}
}
/// <summary> /// <summary>
/// Represents a filter that excludes the associated handlers if no state token is validated. /// Represents a filter that excludes the associated handlers if no state token is validated.
/// </summary> /// </summary>

16
src/OpenIddict.Client/OpenIddictClientHandlers.cs

@ -824,6 +824,7 @@ public static partial class OpenIddictClientHandlers
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>() = OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>()
.AddFilter<RequireTokenStorageEnabled>() .AddFilter<RequireTokenStorageEnabled>()
.AddFilter<RequireStateTokenPrincipal>() .AddFilter<RequireStateTokenPrincipal>()
.AddFilter<RequireStateTokenRedeemed>()
.AddFilter<RequireStateTokenValidated>() .AddFilter<RequireStateTokenValidated>()
.UseScopedHandler<RedeemStateTokenEntry>() .UseScopedHandler<RedeemStateTokenEntry>()
// Note: this handler is deliberately executed early in the pipeline to ensure that // Note: this handler is deliberately executed early in the pipeline to ensure that
@ -896,7 +897,7 @@ public static partial class OpenIddictClientHandlers
// Reject the authentication demand if the expected endpoint type doesn't // Reject the authentication demand if the expected endpoint type doesn't
// match the current endpoint type as it may indicate a mix-up attack (e.g a // match the current endpoint type as it may indicate a mix-up attack (e.g a
// state token created for a logout operation was used for a login operation). // state token created for a logout operation was used for a login operation).
if (type != context.EndpointType) if (context.EndpointType is not OpenIddictClientEndpointType.Unknown && context.EndpointType != type)
{ {
context.Reject( context.Reject(
error: Errors.InvalidRequest, error: Errors.InvalidRequest,
@ -995,6 +996,12 @@ public static partial class OpenIddictClientHandlers
Debug.Assert(context.StateTokenPrincipal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); Debug.Assert(context.StateTokenPrincipal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006));
// Only validate the endpoint type if the endpoint is well-known.
if (context.EndpointType is OpenIddictClientEndpointType.Unknown)
{
return ValueTask.CompletedTask;
}
// Resolve the endpoint type allowed to be used with the state token. // Resolve the endpoint type allowed to be used with the state token.
if (!Enum.TryParse(context.StateTokenPrincipal.GetClaim(Claims.Private.EndpointType), if (!Enum.TryParse(context.StateTokenPrincipal.GetClaim(Claims.Private.EndpointType),
ignoreCase: true, out OpenIddictClientEndpointType type)) ignoreCase: true, out OpenIddictClientEndpointType type))
@ -1174,8 +1181,8 @@ public static partial class OpenIddictClientHandlers
{ {
ArgumentNullException.ThrowIfNull(context); ArgumentNullException.ThrowIfNull(context);
// To help mitigate mix-up attacks, the identity of the issuer can be returned by // To help mitigate mix-up attacks, the identity of the issuer can be returned
// authorization servers that support it as a part of the "iss" parameter, which // by authorization servers that support it as part of the "iss" parameter, which
// allows comparing it to the issuer in the state token. Depending on the selected // allows comparing it to the issuer in the state token. Depending on the selected
// response_type, the same information could be retrieved from the identity token // response_type, the same information could be retrieved from the identity token
// that is expected to contain an "iss" claim containing the issuer identity. // that is expected to contain an "iss" claim containing the issuer identity.
@ -1217,6 +1224,7 @@ public static partial class OpenIddictClientHandlers
// Reject authorization responses containing an "iss" parameter if the configuration // Reject authorization responses containing an "iss" parameter if the configuration
// doesn't indicate this parameter is supported, as recommended by the specification. // doesn't indicate this parameter is supported, as recommended by the specification.
//
// See https://datatracker.ietf.org/doc/html/draft-ietf-oauth-iss-auth-resp-05#section-2.4 // See https://datatracker.ietf.org/doc/html/draft-ietf-oauth-iss-auth-resp-05#section-2.4
// for more information. // for more information.
else if (!string.IsNullOrEmpty(issuer)) else if (!string.IsNullOrEmpty(issuer))
@ -1467,7 +1475,7 @@ public static partial class OpenIddictClientHandlers
} }
/// <summary> /// <summary>
/// Contains the logic responsible for resolving the token from the incoming request. /// Contains the logic responsible for resolving the frontchannel tokens from the incoming request.
/// </summary> /// </summary>
public sealed class ResolveValidatedFrontchannelTokens : IOpenIddictClientHandler<ProcessAuthenticationContext> public sealed class ResolveValidatedFrontchannelTokens : IOpenIddictClientHandler<ProcessAuthenticationContext>
{ {

22
src/OpenIddict.Client/OpenIddictClientModels.cs

@ -20,6 +20,11 @@ public static class OpenIddictClientModels
/// </summary> /// </summary>
public sealed record class InteractiveAuthenticationRequest public sealed record class InteractiveAuthenticationRequest
{ {
/// <summary>
/// Gets or sets the parameters that will be added to the token request, if applicable.
/// </summary>
public Dictionary<string, OpenIddictParameter>? AdditionalTokenRequestParameters { get; init; }
/// <summary> /// <summary>
/// Gets or sets the cancellation token that will be /// Gets or sets the cancellation token that will be
/// used to determine if the operation was aborted. /// used to determine if the operation was aborted.
@ -35,6 +40,23 @@ public static class OpenIddictClientModels
/// Gets or sets the application-specific properties that will be added to the context. /// Gets or sets the application-specific properties that will be added to the context.
/// </summary> /// </summary>
public Dictionary<string, string?>? Properties { get; init; } public Dictionary<string, string?>? Properties { get; init; }
/// <summary>
/// Gets or sets the X.509 client certificate used to bind the access and/or
/// refresh tokens issued by the authorization server, if applicable.
/// </summary>
/// <remarks>
/// <para>
/// Note: when mTLs is also used for OAuth 2.0 client authentication, the
/// certificate set here replaces the client certificate chosen by OpenIddict.
/// </para>
/// <para>
/// Note: if a certificate-based client authentication or token binding method is
/// negotiated, the type of the certificate must match the negotiated methods.
/// </para>
/// </remarks>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public X509Certificate2? TokenBindingCertificate { get; init; }
} }
/// <summary> /// <summary>

5
src/OpenIddict.Client/OpenIddictClientService.cs

@ -271,7 +271,10 @@ public class OpenIddictClientService
var context = new ProcessAuthenticationContext(transaction) var context = new ProcessAuthenticationContext(transaction)
{ {
CancellationToken = request.CancellationToken, CancellationToken = request.CancellationToken,
Nonce = request.Nonce Nonce = request.Nonce,
TokenEndpointClientCertificate = request.TokenBindingCertificate,
TokenRequest = request.AdditionalTokenRequestParameters
is Dictionary<string, OpenIddictParameter> parameters ? new(parameters) : new()
}; };
if (request.Properties is { Count: > 0 }) if (request.Properties is { Count: > 0 })

Loading…
Cancel
Save