diff --git a/src/OpenIddict.Server/OpenIddictServerEvents.Session.cs b/src/OpenIddict.Server/OpenIddictServerEvents.Session.cs
index 58702e16..889879f6 100644
--- a/src/OpenIddict.Server/OpenIddictServerEvents.Session.cs
+++ b/src/OpenIddict.Server/OpenIddictServerEvents.Session.cs
@@ -5,6 +5,7 @@
*/
using System;
+using System.Collections.Generic;
using OpenIddict.Abstractions;
using SR = OpenIddict.Abstractions.OpenIddictResources;
@@ -115,10 +116,26 @@ namespace OpenIddict.Server
///
public bool IsSignOutTriggered { get; private set; }
+ ///
+ /// Gets the additional parameters returned to the client application.
+ ///
+ public Dictionary Parameters { get; private set; }
+ = new(StringComparer.Ordinal);
+
///
/// Allows OpenIddict to return a sign-out response.
///
public void SignOut() => IsSignOutTriggered = true;
+
+ ///
+ /// Allows OpenIddict to return a sign-out response.
+ ///
+ /// The additional parameters returned to the client application.
+ public void SignOut(IDictionary parameters)
+ {
+ IsSignOutTriggered = true;
+ Parameters = new(parameters, StringComparer.Ordinal);
+ }
}
///
diff --git a/src/OpenIddict.Server/OpenIddictServerEvents.cs b/src/OpenIddict.Server/OpenIddictServerEvents.cs
index eba4369e..0c71fc6f 100644
--- a/src/OpenIddict.Server/OpenIddictServerEvents.cs
+++ b/src/OpenIddict.Server/OpenIddictServerEvents.cs
@@ -733,6 +733,11 @@ namespace OpenIddict.Server
get => Transaction.Response!;
set => Transaction.Response = value;
}
+
+ ///
+ /// Gets the additional parameters returned to the client application.
+ ///
+ public Dictionary Parameters { get; } = new(StringComparer.Ordinal);
}
}
}
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs
index 307e4b08..2e6c4a73 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.Session.cs
@@ -227,6 +227,14 @@ namespace OpenIddict.Server
Response = new OpenIddictResponse()
};
+ if (notification.Parameters.Count > 0)
+ {
+ foreach (var parameter in notification.Parameters)
+ {
+ @event.Parameters.Add(parameter.Key, parameter.Value);
+ }
+ }
+
await _dispatcher.DispatchAsync(@event);
if (@event.IsRequestHandled)
diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.cs
index 45b7ef8c..33e1a123 100644
--- a/src/OpenIddict.Server/OpenIddictServerHandlers.cs
+++ b/src/OpenIddict.Server/OpenIddictServerHandlers.cs
@@ -82,12 +82,13 @@ namespace OpenIddict.Server
GenerateUserCode.Descriptor,
GenerateIdentityToken.Descriptor,
- AttachTokenParameters.Descriptor,
+ AttachSignInParameters.Descriptor,
/*
* Sign-out processing:
*/
- ValidateSignOutDemand.Descriptor)
+ ValidateSignOutDemand.Descriptor,
+ AttachSignOutParameters.Descriptor)
.AddRange(Authentication.DefaultHandlers)
.AddRange(Device.DefaultHandlers)
@@ -2832,16 +2833,16 @@ namespace OpenIddict.Server
}
///
- /// Contains the logic responsible of attaching the tokens and their metadata to the sign-in response.
+ /// Contains the logic responsible of attaching the appropriate parameters to the sign-in response.
///
- public class AttachTokenParameters : IOpenIddictServerHandler
+ public class AttachSignInParameters : IOpenIddictServerHandler
{
///
/// Gets the default descriptor definition assigned to this handler.
///
public static OpenIddictServerHandlerDescriptor Descriptor { get; }
= OpenIddictServerHandlerDescriptor.CreateBuilder()
- .UseSingletonHandler()
+ .UseSingletonHandler()
.SetOrder(GenerateIdentityToken.Descriptor.Order + 1_000)
.SetType(OpenIddictServerHandlerType.BuiltIn)
.Build();
@@ -3009,5 +3010,40 @@ namespace OpenIddict.Server
return default;
}
}
+
+ ///
+ /// Contains the logic responsible of attaching the appropriate parameters to the sign-out response.
+ ///
+ public class AttachSignOutParameters : IOpenIddictServerHandler
+ {
+ ///
+ /// Gets the default descriptor definition assigned to this handler.
+ ///
+ public static OpenIddictServerHandlerDescriptor Descriptor { get; }
+ = OpenIddictServerHandlerDescriptor.CreateBuilder()
+ .UseSingletonHandler()
+ .SetOrder(ValidateSignOutDemand.Descriptor.Order + 1_000)
+ .SetType(OpenIddictServerHandlerType.BuiltIn)
+ .Build();
+
+ ///
+ public ValueTask HandleAsync(ProcessSignOutContext 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;
+ }
+ }
}
}
diff --git a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Session.cs b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Session.cs
index b619c763..3e840aad 100644
--- a/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Session.cs
+++ b/test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Session.cs
@@ -478,6 +478,43 @@ namespace OpenIddict.Server.IntegrationTests
Assert.Equal("Bob le Magnifique", (string?) response["name"]);
}
+ [Fact]
+ public async Task HandleLogoutResponse_ResponseContainsCustomParameters()
+ {
+ // Arrange
+ await using var server = await CreateServerAsync(options =>
+ {
+ options.EnableDegradedMode();
+
+ options.AddEventHandler(builder =>
+ builder.UseInlineHandler(context =>
+ {
+ context.SignOut();
+
+ context.Parameters["custom_parameter"] = "custom_value";
+ context.Parameters["parameter_with_multiple_values"] = new[]
+ {
+ "custom_value_1",
+ "custom_value_2"
+ };
+
+ return default;
+ }));
+ });
+
+ await using var client = await server.CreateClientAsync();
+
+ // Act
+ var response = await client.PostAsync("/connect/logout", new OpenIddictRequest
+ {
+ PostLogoutRedirectUri = "http://www.fabrikam.com/path"
+ });
+
+ // Assert
+ Assert.Equal("custom_value", (string?) response["custom_parameter"]);
+ Assert.Equal(new[] { "custom_value_1", "custom_value_2" }, (string[]?) response["parameter_with_multiple_values"]);
+ }
+
[Fact]
public async Task ApplyLogoutResponse_AllowsHandlingResponse()
{