diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs index d30405a1..563a3fac 100644 --- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs +++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandler.cs @@ -82,12 +82,10 @@ namespace OpenIddict.Server.AspNetCore { var notification = new ProcessErrorContext(transaction) { - Response = new OpenIddictResponse - { - Error = context.Error ?? Errors.InvalidRequest, - ErrorDescription = context.ErrorDescription, - ErrorUri = context.ErrorUri - } + Error = context.Error ?? Errors.InvalidRequest, + ErrorDescription = context.ErrorDescription, + ErrorUri = context.ErrorUri, + Response = new OpenIddictResponse() }; await _dispatcher.DispatchAsync(notification); @@ -308,12 +306,10 @@ namespace OpenIddict.Server.AspNetCore { var notification = new ProcessErrorContext(transaction) { - Response = new OpenIddictResponse - { - Error = context.Error ?? Errors.InvalidRequest, - ErrorDescription = context.ErrorDescription, - ErrorUri = context.ErrorUri - } + Error = context.Error ?? Errors.InvalidRequest, + ErrorDescription = context.ErrorDescription, + ErrorUri = context.ErrorUri, + Response = new OpenIddictResponse() }; await _dispatcher.DispatchAsync(notification); @@ -361,12 +357,10 @@ namespace OpenIddict.Server.AspNetCore { var notification = new ProcessErrorContext(transaction) { - Response = new OpenIddictResponse - { - Error = context.Error ?? Errors.InvalidRequest, - ErrorDescription = context.ErrorDescription, - ErrorUri = context.ErrorUri - } + Error = context.Error ?? Errors.InvalidRequest, + ErrorDescription = context.ErrorDescription, + ErrorUri = context.ErrorUri, + Response = new OpenIddictResponse() }; await _dispatcher.DispatchAsync(notification); @@ -404,12 +398,10 @@ namespace OpenIddict.Server.AspNetCore { var notification = new ProcessErrorContext(transaction) { - Response = new OpenIddictResponse - { - Error = context.Error ?? Errors.InvalidRequest, - ErrorDescription = context.ErrorDescription, - ErrorUri = context.ErrorUri - } + Error = context.Error ?? Errors.InvalidRequest, + ErrorDescription = context.ErrorDescription, + ErrorUri = context.ErrorUri, + Response = new OpenIddictResponse() }; await _dispatcher.DispatchAsync(notification); diff --git a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs index 843450a4..8f90e9e2 100644 --- a/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs +++ b/src/OpenIddict.Server.AspNetCore/OpenIddictServerAspNetCoreHandlers.cs @@ -46,18 +46,17 @@ namespace OpenIddict.Server.AspNetCore /* * Challenge processing: */ - AttachHostChallengeError.Descriptor, - AttachHostParameters.Descriptor, + ResolveHostChallengeParameters.Descriptor, /* * Sign-in processing: */ - AttachHostParameters.Descriptor, + ResolveHostSignInParameters.Descriptor, /* * Sign-out processing: */ - AttachHostParameters.Descriptor) + ResolveHostSignOutParameters.Descriptor) .AddRange(Authentication.DefaultHandlers) .AddRange(Device.DefaultHandlers) .AddRange(Discovery.DefaultHandlers) @@ -296,10 +295,11 @@ namespace OpenIddict.Server.AspNetCore } /// - /// Contains the logic responsible of attaching the error details using the ASP.NET Core authentication properties. + /// Contains the logic responsible of resolving the additional sign-in parameters stored in the ASP.NET + /// Core authentication properties specified by the application that triggered the sign-in operation. /// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core. /// - public class AttachHostChallengeError : IOpenIddictServerHandler + public class ResolveHostChallengeParameters : IOpenIddictServerHandler { /// /// Gets the default descriptor definition assigned to this handler. @@ -307,8 +307,8 @@ namespace OpenIddict.Server.AspNetCore public static OpenIddictServerHandlerDescriptor Descriptor { get; } = OpenIddictServerHandlerDescriptor.CreateBuilder() .AddFilter() - .UseSingletonHandler() - .SetOrder(AttachDefaultChallengeError.Descriptor.Order - 1_000) + .UseSingletonHandler() + .SetOrder(AttachDefaultChallengeError.Descriptor.Order - 500) .SetType(OpenIddictServerHandlerType.BuiltIn) .Build(); @@ -321,12 +321,49 @@ namespace OpenIddict.Server.AspNetCore } var properties = context.Transaction.GetProperty(typeof(AuthenticationProperties).FullName!); - if (properties is not null) + if (properties is null) + { + return default; + } + + if (properties.Items.TryGetValue(Properties.Error, out string? error) && + !string.IsNullOrEmpty(error)) { - context.Response.Error = properties.GetString(Properties.Error); - context.Response.ErrorDescription = properties.GetString(Properties.ErrorDescription); - context.Response.ErrorUri = properties.GetString(Properties.ErrorUri); - context.Response.Scope = properties.GetString(Properties.Scope); + context.Parameters[Parameters.Error] = error; + } + + if (properties.Items.TryGetValue(Properties.ErrorDescription, out string? description) && + !string.IsNullOrEmpty(description)) + { + context.Parameters[Parameters.ErrorDescription] = description; + } + + if (properties.Items.TryGetValue(Properties.ErrorUri, out string? uri) && + !string.IsNullOrEmpty(uri)) + { + context.Parameters[Parameters.ErrorUri] = uri; + } + + if (properties.Items.TryGetValue(Properties.Scope, out string? scope) && + !string.IsNullOrEmpty(scope)) + { + context.Parameters[Parameters.Scope] = scope; + } + + foreach (var parameter in properties.Parameters) + { + context.Parameters[parameter.Key] = parameter.Value switch + { + OpenIddictParameter value => value, + JsonElement value => new OpenIddictParameter(value), + bool value => new OpenIddictParameter(value), + int value => new OpenIddictParameter(value), + long value => new OpenIddictParameter(value), + string value => new OpenIddictParameter(value), + string[] value => new OpenIddictParameter(value), + + _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115)) + }; } return default; @@ -334,31 +371,82 @@ namespace OpenIddict.Server.AspNetCore } /// - /// Contains the logic responsible of attaching custom parameters stored in the ASP.NET Core authentication properties. + /// Contains the logic responsible of resolving the additional sign-in parameters stored in the ASP.NET + /// Core authentication properties specified by the application that triggered the sign-in operation. /// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core. /// - public class AttachHostParameters : IOpenIddictServerHandler where TContext : BaseContext + public class ResolveHostSignInParameters : IOpenIddictServerHandler { /// /// Gets the default descriptor definition assigned to this handler. /// public static OpenIddictServerHandlerDescriptor Descriptor { get; } - = OpenIddictServerHandlerDescriptor.CreateBuilder() + = OpenIddictServerHandlerDescriptor.CreateBuilder() .AddFilter() - .UseSingletonHandler>() - .SetOrder(int.MaxValue - 150_000) + .UseSingletonHandler() + .SetOrder(AttachSignInParameters.Descriptor.Order - 500) .SetType(OpenIddictServerHandlerType.BuiltIn) .Build(); /// - public ValueTask HandleAsync(TContext context) + public ValueTask HandleAsync(ProcessSignInContext context) { if (context is null) { throw new ArgumentNullException(nameof(context)); } - Debug.Assert(context.Transaction.Response is not null, SR.GetResourceString(SR.ID4007)); + var properties = context.Transaction.GetProperty(typeof(AuthenticationProperties).FullName!); + if (properties is null) + { + return default; + } + + foreach (var parameter in properties.Parameters) + { + context.Parameters[parameter.Key] = parameter.Value switch + { + OpenIddictParameter value => value, + JsonElement value => new OpenIddictParameter(value), + bool value => new OpenIddictParameter(value), + int value => new OpenIddictParameter(value), + long value => new OpenIddictParameter(value), + string value => new OpenIddictParameter(value), + string[] value => new OpenIddictParameter(value), + + _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115)) + }; + } + + return default; + } + } + + /// + /// Contains the logic responsible of resolving the additional sign-out parameters stored in the ASP.NET + /// Core authentication properties specified by the application that triggered the sign-out operation. + /// Note: this handler is not used when the OpenID Connect request is not initially handled by ASP.NET Core. + /// + public class ResolveHostSignOutParameters : IOpenIddictServerHandler + { + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictServerHandlerDescriptor Descriptor { get; } + = OpenIddictServerHandlerDescriptor.CreateBuilder() + .AddFilter() + .UseSingletonHandler() + .SetOrder(AttachSignOutParameters.Descriptor.Order - 500) + .SetType(OpenIddictServerHandlerType.BuiltIn) + .Build(); + + /// + public ValueTask HandleAsync(ProcessSignOutContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } var properties = context.Transaction.GetProperty(typeof(AuthenticationProperties).FullName!); if (properties is null) @@ -368,8 +456,7 @@ namespace OpenIddict.Server.AspNetCore foreach (var parameter in properties.Parameters) { - // Note: AddParameter() is used to ensure existing parameters are not overriden. - context.Transaction.Response.AddParameter(parameter.Key, parameter.Value switch + context.Parameters[parameter.Key] = parameter.Value switch { OpenIddictParameter value => value, JsonElement value => new OpenIddictParameter(value), @@ -380,7 +467,7 @@ namespace OpenIddict.Server.AspNetCore string[] value => new OpenIddictParameter(value), _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115)) - }); + }; } return default; diff --git a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlers.Protection.cs b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlers.Protection.cs index a720559e..096bce25 100644 --- a/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlers.Protection.cs +++ b/src/OpenIddict.Server.DataProtection/OpenIddictServerDataProtectionHandlers.Protection.cs @@ -35,7 +35,7 @@ namespace OpenIddict.Server.DataProtection ValidateDataProtectionToken.Descriptor, /* - * Token validation: + * Token generation: */ GenerateDataProtectionToken.Descriptor); diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs index 98178927..6002c097 100644 --- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs +++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandler.cs @@ -91,12 +91,10 @@ namespace OpenIddict.Server.Owin { var notification = new ProcessErrorContext(transaction) { - Response = new OpenIddictResponse - { - Error = context.Error ?? Errors.InvalidRequest, - ErrorDescription = context.ErrorDescription, - ErrorUri = context.ErrorUri - } + Error = context.Error ?? Errors.InvalidRequest, + ErrorDescription = context.ErrorDescription, + ErrorUri = context.ErrorUri, + Response = new OpenIddictResponse() }; await _dispatcher.DispatchAsync(notification); @@ -285,12 +283,10 @@ namespace OpenIddict.Server.Owin { var notification = new ProcessErrorContext(transaction) { - Response = new OpenIddictResponse - { - Error = context.Error ?? Errors.InvalidRequest, - ErrorDescription = context.ErrorDescription, - ErrorUri = context.ErrorUri - } + Error = context.Error ?? Errors.InvalidRequest, + ErrorDescription = context.ErrorDescription, + ErrorUri = context.ErrorUri, + Response = new OpenIddictResponse() }; await _dispatcher.DispatchAsync(notification); @@ -329,12 +325,10 @@ namespace OpenIddict.Server.Owin { var notification = new ProcessErrorContext(transaction) { - Response = new OpenIddictResponse - { - Error = context.Error ?? Errors.InvalidRequest, - ErrorDescription = context.ErrorDescription, - ErrorUri = context.ErrorUri - } + Error = context.Error ?? Errors.InvalidRequest, + ErrorDescription = context.ErrorDescription, + ErrorUri = context.ErrorUri, + Response = new OpenIddictResponse() }; await _dispatcher.DispatchAsync(notification); @@ -372,12 +366,10 @@ namespace OpenIddict.Server.Owin { var notification = new ProcessErrorContext(transaction) { - Response = new OpenIddictResponse - { - Error = context.Error ?? Errors.InvalidRequest, - ErrorDescription = context.ErrorDescription, - ErrorUri = context.ErrorUri - } + Error = context.Error ?? Errors.InvalidRequest, + ErrorDescription = context.ErrorDescription, + ErrorUri = context.ErrorUri, + Response = new OpenIddictResponse() }; await _dispatcher.DispatchAsync(notification); diff --git a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs index aecc76af..be0b731c 100644 --- a/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs +++ b/src/OpenIddict.Server.Owin/OpenIddictServerOwinHandlers.cs @@ -44,7 +44,7 @@ namespace OpenIddict.Server.Owin /* * Challenge processing: */ - AttachHostChallengeError.Descriptor) + ResolveHostChallengeParameters.Descriptor) .AddRange(Authentication.DefaultHandlers) .AddRange(Device.DefaultHandlers) .AddRange(Discovery.DefaultHandlers) @@ -285,10 +285,11 @@ namespace OpenIddict.Server.Owin } /// - /// Contains the logic responsible of attaching the error details using the OWIN authentication properties. + /// Contains the logic responsible of resolving the additional sign-in parameters stored in the OWIN + /// authentication properties specified by the application that triggered the sign-in operation. /// Note: this handler is not used when the OpenID Connect request is not initially handled by OWIN. /// - public class AttachHostChallengeError : IOpenIddictServerHandler + public class ResolveHostChallengeParameters : IOpenIddictServerHandler { /// /// Gets the default descriptor definition assigned to this handler. @@ -296,8 +297,8 @@ namespace OpenIddict.Server.Owin public static OpenIddictServerHandlerDescriptor Descriptor { get; } = OpenIddictServerHandlerDescriptor.CreateBuilder() .AddFilter() - .UseSingletonHandler() - .SetOrder(AttachDefaultChallengeError.Descriptor.Order - 1_000) + .UseSingletonHandler() + .SetOrder(AttachChallengeParameters.Descriptor.Order - 500) .SetType(OpenIddictServerHandlerType.BuiltIn) .Build(); @@ -310,18 +311,36 @@ namespace OpenIddict.Server.Owin } var properties = context.Transaction.GetProperty(typeof(AuthenticationProperties).FullName!); - if (properties is not null) + if (properties is null) { - context.Response.Error = GetProperty(properties, Properties.Error); - context.Response.ErrorDescription = GetProperty(properties, Properties.ErrorDescription); - context.Response.ErrorUri = GetProperty(properties, Properties.ErrorUri); - context.Response.Scope = GetProperty(properties, Properties.Scope); + return default; } - return default; + if (properties.Dictionary.TryGetValue(Properties.Error, out string? error) && + !string.IsNullOrEmpty(error)) + { + context.Parameters[Parameters.Error] = error; + } + + if (properties.Dictionary.TryGetValue(Properties.ErrorDescription, out string? description) && + !string.IsNullOrEmpty(description)) + { + context.Parameters[Parameters.ErrorDescription] = description; + } - static string? GetProperty(AuthenticationProperties properties, string name) - => properties.Dictionary.TryGetValue(name, out string? value) ? value : null; + if (properties.Dictionary.TryGetValue(Properties.ErrorUri, out string? uri) && + !string.IsNullOrEmpty(uri)) + { + context.Parameters[Parameters.ErrorUri] = uri; + } + + if (properties.Dictionary.TryGetValue(Properties.Scope, out string? scope) && + !string.IsNullOrEmpty(scope)) + { + context.Parameters[Parameters.Scope] = scope; + } + + return default; } } diff --git a/src/OpenIddict.Server/OpenIddictServerEvents.cs b/src/OpenIddict.Server/OpenIddictServerEvents.cs index 0c71fc6f..0ea09025 100644 --- a/src/OpenIddict.Server/OpenIddictServerEvents.cs +++ b/src/OpenIddict.Server/OpenIddictServerEvents.cs @@ -248,6 +248,26 @@ namespace OpenIddict.Server get => Transaction.Response!; set => Transaction.Response = value; } + + /// + /// Gets or sets the error returned to the caller. + /// + public string? Error { get; set; } + + /// + /// Gets or sets the error description returned to the caller. + /// + public string? ErrorDescription { get; set; } + + /// + /// Gets or sets the error URL returned to the caller. + /// + public string? ErrorUri { get; set; } + + /// + /// Gets the additional parameters returned to the caller. + /// + public Dictionary Parameters { get; } = new(StringComparer.Ordinal); } /// @@ -490,6 +510,11 @@ namespace OpenIddict.Server get => Transaction.Response!; set => Transaction.Response = value; } + + /// + /// Gets the additional parameters returned to caller. + /// + public Dictionary Parameters { get; } = new(StringComparer.Ordinal); } /// @@ -524,7 +549,7 @@ namespace OpenIddict.Server } /// - /// Gets the additional parameters returned to the client application. + /// Gets the additional parameters returned to caller. /// public Dictionary Parameters { get; } = new(StringComparer.Ordinal); @@ -735,7 +760,7 @@ namespace OpenIddict.Server } /// - /// Gets the additional parameters returned to the client application. + /// Gets the additional parameters returned to caller. /// public Dictionary Parameters { get; } = new(StringComparer.Ordinal); } diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.cs index 33e1a123..5b4dbe6c 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.cs @@ -49,6 +49,7 @@ namespace OpenIddict.Server AttachDefaultChallengeError.Descriptor, RejectDeviceCodeEntry.Descriptor, RejectUserCodeEntry.Descriptor, + AttachChallengeParameters.Descriptor, /* * Sign-in processing: @@ -88,7 +89,12 @@ namespace OpenIddict.Server * Sign-out processing: */ ValidateSignOutDemand.Descriptor, - AttachSignOutParameters.Descriptor) + AttachSignOutParameters.Descriptor, + + /* + * Error processing: + */ + AttachErrorParameters.Descriptor) .AddRange(Authentication.DefaultHandlers) .AddRange(Device.DefaultHandlers) @@ -918,8 +924,8 @@ namespace OpenIddict.Server } if (context.EndpointType is not (OpenIddictServerEndpointType.Authorization or - OpenIddictServerEndpointType.Token or - OpenIddictServerEndpointType.Userinfo or + OpenIddictServerEndpointType.Token or + OpenIddictServerEndpointType.Userinfo or OpenIddictServerEndpointType.Verification)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0006)); @@ -952,38 +958,47 @@ namespace OpenIddict.Server throw new ArgumentNullException(nameof(context)); } - context.Response.Error ??= context.EndpointType switch + if (!context.Parameters.ContainsKey(Parameters.Error)) { - OpenIddictServerEndpointType.Authorization or OpenIddictServerEndpointType.Verification - => Errors.AccessDenied, + context.Parameters[Parameters.Error] = context.EndpointType switch + { + OpenIddictServerEndpointType.Authorization or OpenIddictServerEndpointType.Verification + => Errors.AccessDenied, - OpenIddictServerEndpointType.Token => Errors.InvalidGrant, - OpenIddictServerEndpointType.Userinfo => Errors.InsufficientAccess, + OpenIddictServerEndpointType.Token => Errors.InvalidGrant, + OpenIddictServerEndpointType.Userinfo => Errors.InsufficientAccess, - _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0006)) - }; + _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0006)) + }; + } - context.Response.ErrorDescription ??= context.EndpointType switch + if (!context.Parameters.ContainsKey(Parameters.ErrorDescription)) { - OpenIddictServerEndpointType.Authorization or OpenIddictServerEndpointType.Verification - => SR.GetResourceString(SR.ID2015), + context.Parameters[Parameters.ErrorDescription] = context.EndpointType switch + { + OpenIddictServerEndpointType.Authorization or OpenIddictServerEndpointType.Verification + => SR.GetResourceString(SR.ID2015), - OpenIddictServerEndpointType.Token => SR.GetResourceString(SR.ID2024), - OpenIddictServerEndpointType.Userinfo => SR.GetResourceString(SR.ID2025), + OpenIddictServerEndpointType.Token => SR.GetResourceString(SR.ID2024), + OpenIddictServerEndpointType.Userinfo => SR.GetResourceString(SR.ID2025), - _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0006)) - }; + _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0006)) + }; + } - context.Response.ErrorUri ??= context.EndpointType switch + if (!context.Parameters.ContainsKey(Parameters.ErrorUri)) { - OpenIddictServerEndpointType.Authorization or OpenIddictServerEndpointType.Verification - => SR.FormatID8000(SR.ID2015), + context.Parameters[Parameters.ErrorUri] = context.EndpointType switch + { + OpenIddictServerEndpointType.Authorization or OpenIddictServerEndpointType.Verification + => SR.FormatID8000(SR.ID2015), - OpenIddictServerEndpointType.Token => SR.FormatID8000(SR.ID2024), - OpenIddictServerEndpointType.Userinfo => SR.FormatID8000(SR.ID2025), + OpenIddictServerEndpointType.Token => SR.FormatID8000(SR.ID2024), + OpenIddictServerEndpointType.Userinfo => SR.FormatID8000(SR.ID2025), - _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0006)) - }; + _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0006)) + }; + } return default; } @@ -1107,6 +1122,41 @@ namespace OpenIddict.Server } } + /// + /// Contains the logic responsible of attaching the appropriate parameters to the challenge response. + /// + public class AttachChallengeParameters : IOpenIddictServerHandler + { + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictServerHandlerDescriptor Descriptor { get; } + = OpenIddictServerHandlerDescriptor.CreateBuilder() + .UseSingletonHandler() + .SetOrder(RejectUserCodeEntry.Descriptor.Order + 1_000) + .SetType(OpenIddictServerHandlerType.BuiltIn) + .Build(); + + /// + public ValueTask HandleAsync(ProcessChallengeContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (context.Parameters.Count > 0) + { + foreach (var parameter in context.Parameters) + { + context.Response.SetParameter(parameter.Key, parameter.Value); + } + } + + return default; + } + } + /// /// Contains the logic responsible of ensuring that the sign-in demand /// is compatible with the type of the endpoint that handled the request. @@ -1132,8 +1182,8 @@ namespace OpenIddict.Server } if (context.EndpointType is not (OpenIddictServerEndpointType.Authorization or - OpenIddictServerEndpointType.Device or - OpenIddictServerEndpointType.Token or + OpenIddictServerEndpointType.Device or + OpenIddictServerEndpointType.Token or OpenIddictServerEndpointType.Verification)) { throw new InvalidOperationException(SR.GetResourceString(SR.ID0010)); @@ -3045,5 +3095,44 @@ namespace OpenIddict.Server return default; } } + + /// + /// Contains the logic responsible of attaching the appropriate parameters to the error response. + /// + public class AttachErrorParameters : IOpenIddictServerHandler + { + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictServerHandlerDescriptor Descriptor { get; } + = OpenIddictServerHandlerDescriptor.CreateBuilder() + .UseSingletonHandler() + .SetOrder(int.MinValue + 100_000) + .SetType(OpenIddictServerHandlerType.BuiltIn) + .Build(); + + /// + public ValueTask HandleAsync(ProcessErrorContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + context.Response.Error = context.Error; + context.Response.ErrorDescription = context.ErrorDescription; + context.Response.ErrorUri = context.ErrorUri; + + if (context.Parameters.Count > 0) + { + foreach (var parameter in context.Parameters) + { + context.Response.SetParameter(parameter.Key, parameter.Value); + } + } + + return default; + } + } } } diff --git a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs index 3546e282..672c9e2b 100644 --- a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs +++ b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandler.cs @@ -79,12 +79,10 @@ namespace OpenIddict.Validation.AspNetCore { var notification = new ProcessErrorContext(transaction) { - Response = new OpenIddictResponse - { - Error = context.Error ?? Errors.InvalidRequest, - ErrorDescription = context.ErrorDescription, - ErrorUri = context.ErrorUri - } + Error = context.Error ?? Errors.InvalidRequest, + ErrorDescription = context.ErrorDescription, + ErrorUri = context.ErrorUri, + Response = new OpenIddictResponse() }; await _dispatcher.DispatchAsync(notification); @@ -225,12 +223,10 @@ namespace OpenIddict.Validation.AspNetCore { var notification = new ProcessErrorContext(transaction) { - Response = new OpenIddictResponse - { - Error = context.Error ?? Errors.InvalidRequest, - ErrorDescription = context.ErrorDescription, - ErrorUri = context.ErrorUri - } + Error = context.Error ?? Errors.InvalidRequest, + ErrorDescription = context.ErrorDescription, + ErrorUri = context.ErrorUri, + Response = new OpenIddictResponse() }; await _dispatcher.DispatchAsync(notification); diff --git a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs index 5234e354..341c00a3 100644 --- a/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs +++ b/src/OpenIddict.Validation.AspNetCore/OpenIddictValidationAspNetCoreHandlers.cs @@ -21,6 +21,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; +using OpenIddict.Abstractions; using static OpenIddict.Abstractions.OpenIddictConstants; using static OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreHandlerFilters; using static OpenIddict.Validation.OpenIddictValidationEvents; @@ -325,12 +326,49 @@ namespace OpenIddict.Validation.AspNetCore } var properties = context.Transaction.GetProperty(typeof(AuthenticationProperties).FullName!); - if (properties is not null) + if (properties is null) { - context.Response.Error = properties.GetString(Properties.Error); - context.Response.ErrorDescription = properties.GetString(Properties.ErrorDescription); - context.Response.ErrorUri = properties.GetString(Properties.ErrorUri); - context.Response.Scope = properties.GetString(Properties.Scope); + return default; + } + + if (properties.Items.TryGetValue(Properties.Error, out string? error) && + !string.IsNullOrEmpty(error)) + { + context.Parameters[Parameters.Error] = error; + } + + if (properties.Items.TryGetValue(Properties.ErrorDescription, out string? description) && + !string.IsNullOrEmpty(description)) + { + context.Parameters[Parameters.ErrorDescription] = description; + } + + if (properties.Items.TryGetValue(Properties.ErrorUri, out string? uri) && + !string.IsNullOrEmpty(uri)) + { + context.Parameters[Parameters.ErrorUri] = uri; + } + + if (properties.Items.TryGetValue(Properties.Scope, out string? scope) && + !string.IsNullOrEmpty(scope)) + { + context.Parameters[Parameters.Scope] = scope; + } + + foreach (var parameter in properties.Parameters) + { + context.Parameters[parameter.Key] = parameter.Value switch + { + OpenIddictParameter value => value, + JsonElement value => new OpenIddictParameter(value), + bool value => new OpenIddictParameter(value), + int value => new OpenIddictParameter(value), + long value => new OpenIddictParameter(value), + string value => new OpenIddictParameter(value), + string[] value => new OpenIddictParameter(value), + + _ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0115)) + }; } return default; diff --git a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs index a65e2ca5..59afe8ff 100644 --- a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs +++ b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandler.cs @@ -91,12 +91,10 @@ namespace OpenIddict.Validation.Owin { var notification = new ProcessErrorContext(transaction) { - Response = new OpenIddictResponse - { - Error = context.Error ?? Errors.InvalidRequest, - ErrorDescription = context.ErrorDescription, - ErrorUri = context.ErrorUri - } + Error = context.Error ?? Errors.InvalidRequest, + ErrorDescription = context.ErrorDescription, + ErrorUri = context.ErrorUri, + Response = new OpenIddictResponse() }; await _dispatcher.DispatchAsync(notification); @@ -237,12 +235,10 @@ namespace OpenIddict.Validation.Owin { var notification = new ProcessErrorContext(transaction) { - Response = new OpenIddictResponse - { - Error = context.Error ?? Errors.InvalidRequest, - ErrorDescription = context.ErrorDescription, - ErrorUri = context.ErrorUri - } + Error = context.Error ?? Errors.InvalidRequest, + ErrorDescription = context.ErrorDescription, + ErrorUri = context.ErrorUri, + Response = new OpenIddictResponse() }; await _dispatcher.DispatchAsync(notification); diff --git a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlers.cs b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlers.cs index 522476e7..f4ace9e1 100644 --- a/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlers.cs +++ b/src/OpenIddict.Validation.Owin/OpenIddictValidationOwinHandlers.cs @@ -325,18 +325,36 @@ namespace OpenIddict.Validation.Owin } var properties = context.Transaction.GetProperty(typeof(AuthenticationProperties).FullName!); - if (properties is not null) + if (properties is null) { - context.Response.Error = GetProperty(properties, Properties.Error); - context.Response.ErrorDescription = GetProperty(properties, Properties.ErrorDescription); - context.Response.ErrorUri = GetProperty(properties, Properties.ErrorUri); - context.Response.Scope = GetProperty(properties, Properties.Scope); + return default; } - return default; + if (properties.Dictionary.TryGetValue(Properties.Error, out string? error) && + !string.IsNullOrEmpty(error)) + { + context.Parameters[Parameters.Error] = error; + } + + if (properties.Dictionary.TryGetValue(Properties.ErrorDescription, out string? description) && + !string.IsNullOrEmpty(description)) + { + context.Parameters[Parameters.ErrorDescription] = description; + } - static string? GetProperty(AuthenticationProperties properties, string name) - => properties.Dictionary.TryGetValue(name, out string? value) ? value : null; + if (properties.Dictionary.TryGetValue(Properties.ErrorUri, out string? uri) && + !string.IsNullOrEmpty(uri)) + { + context.Parameters[Parameters.ErrorUri] = uri; + } + + if (properties.Dictionary.TryGetValue(Properties.Scope, out string? scope) && + !string.IsNullOrEmpty(scope)) + { + context.Parameters[Parameters.Scope] = scope; + } + + return default; } } diff --git a/src/OpenIddict.Validation/OpenIddictValidationEvents.cs b/src/OpenIddict.Validation/OpenIddictValidationEvents.cs index c671176a..24719498 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationEvents.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationEvents.cs @@ -5,6 +5,7 @@ */ using System; +using System.Collections.Generic; using System.ComponentModel; using System.Security.Claims; using Microsoft.Extensions.Logging; @@ -213,6 +214,26 @@ namespace OpenIddict.Validation get => Transaction.Response!; set => Transaction.Response = value; } + + /// + /// Gets or sets the error returned to the caller. + /// + public string? Error { get; set; } + + /// + /// Gets or sets the error description returned to the caller. + /// + public string? ErrorDescription { get; set; } + + /// + /// Gets or sets the error URL returned to the caller. + /// + public string? ErrorUri { get; set; } + + /// + /// Gets the additional parameters returned to the caller. + /// + public Dictionary Parameters { get; } = new(StringComparer.Ordinal); } /// @@ -294,6 +315,11 @@ namespace OpenIddict.Validation get => Transaction.Response!; set => Transaction.Response = value; } + + /// + /// Gets the additional parameters returned to caller. + /// + public Dictionary Parameters { get; } = new(StringComparer.Ordinal); } } } diff --git a/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs b/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs index 8435e8c4..05de9d2b 100644 --- a/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs +++ b/src/OpenIddict.Validation/OpenIddictValidationHandlers.cs @@ -220,6 +220,45 @@ namespace OpenIddict.Validation } } + /// + /// Contains the logic responsible of attaching the appropriate parameters to the error response. + /// + public class AttachErrorParameters : IOpenIddictValidationHandler + { + /// + /// Gets the default descriptor definition assigned to this handler. + /// + public static OpenIddictValidationHandlerDescriptor Descriptor { get; } + = OpenIddictValidationHandlerDescriptor.CreateBuilder() + .UseSingletonHandler() + .SetOrder(int.MinValue + 100_000) + .SetType(OpenIddictValidationHandlerType.BuiltIn) + .Build(); + + /// + public ValueTask HandleAsync(ProcessErrorContext context) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + context.Response.Error = context.Error; + context.Response.ErrorDescription = context.ErrorDescription; + context.Response.ErrorUri = context.ErrorUri; + + if (context.Parameters.Count > 0) + { + foreach (var parameter in context.Parameters) + { + context.Response.SetParameter(parameter.Key, parameter.Value); + } + } + + return default; + } + } + /// /// Contains the logic responsible of extracting potential errors from the response. ///