Browse Source

Introduce a simpler way to return additional parameters from the Handle*Request events that trigger a sign-in response

pull/1321/head
Kévin Chalet 4 years ago
parent
commit
e0c748f046
  1. 6
      src/OpenIddict.Server.Owin/OpenIddictServerOwinProperties.cs
  2. 18
      src/OpenIddict.Server/OpenIddictServerEvents.Authentication.cs
  3. 47
      src/OpenIddict.Server/OpenIddictServerEvents.Device.cs
  4. 3
      src/OpenIddict.Server/OpenIddictServerEvents.Discovery.cs
  5. 19
      src/OpenIddict.Server/OpenIddictServerEvents.Exchange.cs
  6. 5
      src/OpenIddict.Server/OpenIddictServerEvents.Introspection.cs
  7. 6
      src/OpenIddict.Server/OpenIddictServerEvents.Revocation.cs
  8. 3
      src/OpenIddict.Server/OpenIddictServerEvents.Userinfo.cs
  9. 6
      src/OpenIddict.Server/OpenIddictServerEvents.cs
  10. 8
      src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs
  11. 16
      src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs
  12. 8
      src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs
  13. 8
      src/OpenIddict.Server/OpenIddictServerHandlers.cs
  14. 3
      src/OpenIddict.Server/OpenIddictServerTransaction.cs
  15. 6
      src/OpenIddict.Validation.Owin/OpenIddictValidationOwinProperties.cs
  16. 3
      src/OpenIddict.Validation/OpenIddictValidationTransaction.cs
  17. 45
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs
  18. 123
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Device.cs
  19. 44
      test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs

6
src/OpenIddict.Server.Owin/OpenIddictServerOwinProperties.cs

@ -35,7 +35,9 @@ namespace OpenIddict.Server.Owin
IDictionary<string, string?>? items, IDictionary<string, string?>? items,
IDictionary<string, object?>? parameters) IDictionary<string, object?>? parameters)
: base(items) : base(items)
=> Parameters = parameters ?? new Dictionary<string, object?>(StringComparer.Ordinal); => Parameters = parameters is not null ?
new(parameters, StringComparer.Ordinal) :
new(StringComparer.Ordinal);
/// <summary> /// <summary>
/// Gets the collection of parameters passed to the authentication handler. /// Gets the collection of parameters passed to the authentication handler.
@ -44,7 +46,7 @@ namespace OpenIddict.Server.Owin
/// Note: these properties are not intended for serialization or persistence, /// Note: these properties are not intended for serialization or persistence,
/// only for flowing data between call sites. /// only for flowing data between call sites.
/// </remarks> /// </remarks>
public IDictionary<string, object?> Parameters { get; } public Dictionary<string, object?> Parameters { get; }
/// <summary> /// <summary>
/// Gets a parameter from the <see cref="Parameters"/> collection. /// Gets a parameter from the <see cref="Parameters"/> collection.

18
src/OpenIddict.Server/OpenIddictServerEvents.Authentication.cs

@ -5,6 +5,7 @@
*/ */
using System; using System;
using System.Collections.Generic;
using System.Security.Claims; using System.Security.Claims;
using OpenIddict.Abstractions; using OpenIddict.Abstractions;
using SR = OpenIddict.Abstractions.OpenIddictResources; using SR = OpenIddict.Abstractions.OpenIddictResources;
@ -118,11 +119,28 @@ namespace OpenIddict.Server
set => Transaction.Request = value; set => Transaction.Request = value;
} }
/// <summary>
/// Gets the additional parameters returned to the client application.
/// </summary>
public Dictionary<string, OpenIddictParameter> Parameters { get; private set; }
= new(StringComparer.Ordinal);
/// <summary> /// <summary>
/// Allows OpenIddict to return a sign-in response using the specified principal. /// Allows OpenIddict to return a sign-in response using the specified principal.
/// </summary> /// </summary>
/// <param name="principal">The claims principal.</param> /// <param name="principal">The claims principal.</param>
public void SignIn(ClaimsPrincipal principal) => Principal = principal; public void SignIn(ClaimsPrincipal principal) => Principal = principal;
/// <summary>
/// Allows OpenIddict to return a sign-in response using the specified principal.
/// </summary>
/// <param name="principal">The claims principal.</param>
/// <param name="parameters">The additional parameters returned to the client application.</param>
public void SignIn(ClaimsPrincipal principal, IDictionary<string, OpenIddictParameter> parameters)
{
Principal = principal;
Parameters = new(parameters, StringComparer.Ordinal);
}
} }
/// <summary> /// <summary>

47
src/OpenIddict.Server/OpenIddictServerEvents.Device.cs

@ -4,6 +4,8 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System;
using System.Collections.Generic;
using System.Security.Claims; using System.Security.Claims;
using OpenIddict.Abstractions; using OpenIddict.Abstractions;
@ -81,6 +83,29 @@ namespace OpenIddict.Server
get => Transaction.Request!; get => Transaction.Request!;
set => Transaction.Request = value; set => Transaction.Request = value;
} }
/// <summary>
/// Gets the additional parameters returned to the client application.
/// </summary>
public Dictionary<string, OpenIddictParameter> Parameters { get; private set; }
= new(StringComparer.Ordinal);
/// <summary>
/// Allows OpenIddict to return a sign-in response using the specified principal.
/// </summary>
/// <param name="principal">The claims principal.</param>
public void SignIn(ClaimsPrincipal principal) => Principal = principal;
/// <summary>
/// Allows OpenIddict to return a sign-in response using the specified principal.
/// </summary>
/// <param name="principal">The claims principal.</param>
/// <param name="parameters">The additional parameters returned to the client application.</param>
public void SignIn(ClaimsPrincipal principal, IDictionary<string, OpenIddictParameter> parameters)
{
Principal = principal;
Parameters = new(parameters, StringComparer.Ordinal);
}
} }
/// <summary> /// <summary>
@ -198,11 +223,33 @@ namespace OpenIddict.Server
set => Transaction.Request = value; set => Transaction.Request = value;
} }
/// <summary>
/// Gets the additional parameters returned to the caller.
/// </summary>
/// <remarks>
/// Note: by default, this property is not used as empty responses are typically
/// returned for user verification requests. To return a different response, a
/// custom event handler must be registered to handle user verification responses.
/// </remarks>
public Dictionary<string, OpenIddictParameter> Parameters { get; private set; }
= new(StringComparer.Ordinal);
/// <summary> /// <summary>
/// Allows OpenIddict to return a sign-in response using the specified principal. /// Allows OpenIddict to return a sign-in response using the specified principal.
/// </summary> /// </summary>
/// <param name="principal">The claims principal.</param> /// <param name="principal">The claims principal.</param>
public void SignIn(ClaimsPrincipal principal) => Principal = principal; public void SignIn(ClaimsPrincipal principal) => Principal = principal;
/// <summary>
/// Allows OpenIddict to return a sign-in response using the specified principal.
/// </summary>
/// <param name="principal">The claims principal.</param>
/// <param name="parameters">The additional parameters returned to the client application.</param>
public void SignIn(ClaimsPrincipal principal, IDictionary<string, OpenIddictParameter> parameters)
{
Principal = principal;
Parameters = new(parameters, StringComparer.Ordinal);
}
} }
/// <summary> /// <summary>

3
src/OpenIddict.Server/OpenIddictServerEvents.Discovery.cs

@ -87,8 +87,7 @@ namespace OpenIddict.Server
/// <summary> /// <summary>
/// Gets the additional parameters returned to the client application. /// Gets the additional parameters returned to the client application.
/// </summary> /// </summary>
public IDictionary<string, OpenIddictParameter> Metadata { get; } = public Dictionary<string, OpenIddictParameter> Metadata { get; } = new(StringComparer.Ordinal);
new Dictionary<string, OpenIddictParameter>(StringComparer.Ordinal);
/// <summary> /// <summary>
/// Gets or sets the authorization endpoint address. /// Gets or sets the authorization endpoint address.

19
src/OpenIddict.Server/OpenIddictServerEvents.Exchange.cs

@ -4,6 +4,8 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System;
using System.Collections.Generic;
using System.Security.Claims; using System.Security.Claims;
using OpenIddict.Abstractions; using OpenIddict.Abstractions;
@ -88,11 +90,28 @@ namespace OpenIddict.Server
set => Transaction.Request = value; set => Transaction.Request = value;
} }
/// <summary>
/// Gets the additional parameters returned to the client application.
/// </summary>
public Dictionary<string, OpenIddictParameter> Parameters { get; private set; }
= new(StringComparer.Ordinal);
/// <summary> /// <summary>
/// Allows OpenIddict to return a sign-in response using the specified principal. /// Allows OpenIddict to return a sign-in response using the specified principal.
/// </summary> /// </summary>
/// <param name="principal">The claims principal.</param> /// <param name="principal">The claims principal.</param>
public void SignIn(ClaimsPrincipal principal) => Principal = principal; public void SignIn(ClaimsPrincipal principal) => Principal = principal;
/// <summary>
/// Allows OpenIddict to return a sign-in response using the specified principal.
/// </summary>
/// <param name="principal">The claims principal.</param>
/// <param name="parameters">The additional parameters returned to the client application.</param>
public void SignIn(ClaimsPrincipal principal, IDictionary<string, OpenIddictParameter> parameters)
{
Principal = principal;
Parameters = new(parameters, StringComparer.Ordinal);
}
} }
/// <summary> /// <summary>

5
src/OpenIddict.Server/OpenIddictServerEvents.Introspection.cs

@ -101,10 +101,9 @@ namespace OpenIddict.Server
public ClaimsPrincipal Principal { get; set; } = default!; public ClaimsPrincipal Principal { get; set; } = default!;
/// <summary> /// <summary>
/// Gets the additional claims returned to the caller. /// Gets the additional claims returned to the client application.
/// </summary> /// </summary>
public IDictionary<string, OpenIddictParameter> Claims { get; } = public Dictionary<string, OpenIddictParameter> Claims { get; } = new(StringComparer.Ordinal);
new Dictionary<string, OpenIddictParameter>(StringComparer.Ordinal);
/// <summary> /// <summary>
/// Gets the list of audiences returned to the caller /// Gets the list of audiences returned to the caller

6
src/OpenIddict.Server/OpenIddictServerEvents.Revocation.cs

@ -99,12 +99,6 @@ namespace OpenIddict.Server
/// Gets or sets the security principal extracted from the revoked token. /// Gets or sets the security principal extracted from the revoked token.
/// </summary> /// </summary>
public ClaimsPrincipal Principal { get; set; } = default!; public ClaimsPrincipal Principal { get; set; } = default!;
/// <summary>
/// Gets the authentication ticket.
/// </summary>
public IDictionary<string, object> Claims { get; }
= new Dictionary<string, object>(StringComparer.Ordinal);
} }
/// <summary> /// <summary>

3
src/OpenIddict.Server/OpenIddictServerEvents.Userinfo.cs

@ -98,8 +98,7 @@ namespace OpenIddict.Server
/// <summary> /// <summary>
/// Gets the additional claims returned to the client application. /// Gets the additional claims returned to the client application.
/// </summary> /// </summary>
public IDictionary<string, OpenIddictParameter> Claims { get; } = public Dictionary<string, OpenIddictParameter> Claims { get; } = new(StringComparer.Ordinal);
new Dictionary<string, OpenIddictParameter>(StringComparer.Ordinal);
/// <summary> /// <summary>
/// Gets or sets the value used for the "address" claim. /// Gets or sets the value used for the "address" claim.

6
src/OpenIddict.Server/OpenIddictServerEvents.cs

@ -5,6 +5,7 @@
*/ */
using System; using System;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Security.Claims; using System.Security.Claims;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -522,6 +523,11 @@ namespace OpenIddict.Server
set => Transaction.Response = value; set => Transaction.Response = value;
} }
/// <summary>
/// Gets the additional parameters returned to the client application.
/// </summary>
public Dictionary<string, OpenIddictParameter> Parameters { get; } = new(StringComparer.Ordinal);
/// <summary> /// <summary>
/// Gets or sets a boolean indicating whether an access token /// Gets or sets a boolean indicating whether an access token
/// should be generated (and optionally returned to the client). /// should be generated (and optionally returned to the client).

8
src/OpenIddict.Server/OpenIddictServerHandlers.Authentication.cs

@ -256,6 +256,14 @@ namespace OpenIddict.Server
Response = new OpenIddictResponse() 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); await _dispatcher.DispatchAsync(@event);
if (@event.IsRequestHandled) if (@event.IsRequestHandled)

16
src/OpenIddict.Server/OpenIddictServerHandlers.Device.cs

@ -253,6 +253,14 @@ namespace OpenIddict.Server
Response = new OpenIddictResponse() 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); await _dispatcher.DispatchAsync(@event);
if (@event.IsRequestHandled) if (@event.IsRequestHandled)
@ -1051,6 +1059,14 @@ namespace OpenIddict.Server
Response = new OpenIddictResponse() 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); await _dispatcher.DispatchAsync(@event);
if (@event.IsRequestHandled) if (@event.IsRequestHandled)

8
src/OpenIddict.Server/OpenIddictServerHandlers.Exchange.cs

@ -263,6 +263,14 @@ namespace OpenIddict.Server
Response = new OpenIddictResponse() 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); await _dispatcher.DispatchAsync(@event);
if (@event.IsRequestHandled) if (@event.IsRequestHandled)

8
src/OpenIddict.Server/OpenIddictServerHandlers.cs

@ -2928,6 +2928,14 @@ namespace OpenIddict.Server
} }
} }
if (context.Parameters.Count > 0)
{
foreach (var parameter in context.Parameters)
{
context.Response.SetParameter(parameter.Key, parameter.Value);
}
}
return default; return default;
static Uri? GetEndpointAbsoluteUri(Uri? issuer, Uri? endpoint) static Uri? GetEndpointAbsoluteUri(Uri? issuer, Uri? endpoint)

3
src/OpenIddict.Server/OpenIddictServerTransaction.cs

@ -39,8 +39,7 @@ namespace OpenIddict.Server
/// <summary> /// <summary>
/// Gets the additional properties associated with the current request. /// Gets the additional properties associated with the current request.
/// </summary> /// </summary>
public IDictionary<string, object?> Properties { get; } public Dictionary<string, object?> Properties { get; } = new(StringComparer.OrdinalIgnoreCase);
= new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase);
/// <summary> /// <summary>
/// Gets or sets the current OpenID Connect request. /// Gets or sets the current OpenID Connect request.

6
src/OpenIddict.Validation.Owin/OpenIddictValidationOwinProperties.cs

@ -35,7 +35,9 @@ namespace OpenIddict.Validation.Owin
IDictionary<string, string?>? items, IDictionary<string, string?>? items,
IDictionary<string, object?>? parameters) IDictionary<string, object?>? parameters)
: base(items) : base(items)
=> Parameters = parameters ?? new Dictionary<string, object?>(StringComparer.Ordinal); => Parameters = parameters is not null ?
new(parameters, StringComparer.Ordinal) :
new(StringComparer.Ordinal);
/// <summary> /// <summary>
/// Gets the collection of parameters passed to the authentication handler. /// Gets the collection of parameters passed to the authentication handler.
@ -44,7 +46,7 @@ namespace OpenIddict.Validation.Owin
/// Note: these properties are not intended for serialization or persistence, /// Note: these properties are not intended for serialization or persistence,
/// only for flowing data between call sites. /// only for flowing data between call sites.
/// </remarks> /// </remarks>
public IDictionary<string, object?> Parameters { get; } public Dictionary<string, object?> Parameters { get; }
/// <summary> /// <summary>
/// Gets a parameter from the <see cref="Parameters"/> collection. /// Gets a parameter from the <see cref="Parameters"/> collection.

3
src/OpenIddict.Validation/OpenIddictValidationTransaction.cs

@ -39,8 +39,7 @@ namespace OpenIddict.Validation
/// <summary> /// <summary>
/// Gets the additional properties associated with the current request. /// Gets the additional properties associated with the current request.
/// </summary> /// </summary>
public IDictionary<string, object?> Properties { get; } public Dictionary<string, object?> Properties { get; } = new(StringComparer.OrdinalIgnoreCase);
= new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase);
/// <summary> /// <summary>
/// Gets or sets the current OpenID Connect request. /// Gets or sets the current OpenID Connect request.

45
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Authentication.cs

@ -2011,6 +2011,51 @@ namespace OpenIddict.Server.IntegrationTests
Assert.Equal("Bob le Magnifique", (string?) response["name"]); Assert.Equal("Bob le Magnifique", (string?) response["name"]);
} }
[Fact]
public async Task HandleAuthorizationRequest_ResponseContainsCustomParameters()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.AddEventHandler<HandleAuthorizationRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetClaim(Claims.Subject, "Bob le Magnifique");
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/authorize", new OpenIddictRequest
{
ClientId = "Fabrikam",
Nonce = "n-0S6_WzA2Mj",
RedirectUri = "http://www.fabrikam.com/path",
ResponseType = ResponseTypes.Token
});
// Assert
Assert.Null(response.Error);
Assert.Null(response.ErrorDescription);
Assert.Null(response.ErrorUri);
Assert.NotNull(response.AccessToken);
Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal(new[] { "custom_value_1", "custom_value_2" }, (string[]?) response["parameter_with_multiple_values"]);
}
[Theory] [Theory]
[InlineData("code", ResponseModes.Query)] [InlineData("code", ResponseModes.Query)]
[InlineData("code id_token", ResponseModes.Fragment)] [InlineData("code id_token", ResponseModes.Fragment)]

123
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Device.cs

@ -17,6 +17,7 @@ using OpenIddict.Abstractions;
using Xunit; using Xunit;
using static OpenIddict.Abstractions.OpenIddictConstants; using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Server.OpenIddictServerEvents; using static OpenIddict.Server.OpenIddictServerEvents;
using static OpenIddict.Server.OpenIddictServerHandlers.Protection;
using SR = OpenIddict.Abstractions.OpenIddictResources; using SR = OpenIddict.Abstractions.OpenIddictResources;
namespace OpenIddict.Server.IntegrationTests namespace OpenIddict.Server.IntegrationTests
@ -173,8 +174,6 @@ namespace OpenIddict.Server.IntegrationTests
.ReturnsAsync(true); .ReturnsAsync(true);
})); }));
options.EnableDegradedMode();
options.Configure(options => options.GrantTypes.Remove(GrantTypes.RefreshToken)); options.Configure(options => options.GrantTypes.Remove(GrantTypes.RefreshToken));
}); });
@ -251,8 +250,11 @@ namespace OpenIddict.Server.IntegrationTests
.ReturnsAsync(true); .ReturnsAsync(true);
})); }));
options.EnableDegradedMode();
options.RegisterScopes("registered_scope"); options.RegisterScopes("registered_scope");
options.SetRevocationEndpointUris(Array.Empty<Uri>());
options.DisableAuthorizationStorage();
options.DisableTokenStorage();
options.DisableSlidingRefreshTokenExpiration();
options.AddEventHandler<HandleDeviceRequestContext>(builder => options.AddEventHandler<HandleDeviceRequestContext>(builder =>
builder.UseInlineHandler(context => builder.UseInlineHandler(context =>
@ -885,6 +887,58 @@ namespace OpenIddict.Server.IntegrationTests
Assert.Equal("Bob le Magnifique", (string?) response["name"]); Assert.Equal("Bob le Magnifique", (string?) response["name"]);
} }
[Fact]
public async Task HandleDeviceRequest_ResponseContainsCustomParameters()
{
// Arrange
var application = new OpenIddictApplication();
await using var server = await CreateServerAsync(options =>
{
options.Services.AddSingleton(CreateApplicationManager(mock =>
{
mock.Setup(manager => manager.FindByClientIdAsync("Fabrikam", It.IsAny<CancellationToken>()))
.ReturnsAsync(application);
mock.Setup(manager => manager.HasClientTypeAsync(application, ClientTypes.Public, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
}));
options.EnableDegradedMode();
options.AddEventHandler<HandleDeviceRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.Principal = new ClaimsPrincipal(new ClaimsIdentity());
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/device", new OpenIddictRequest
{
ClientId = "Fabrikam"
});
// Assert
Assert.Null(response.Error);
Assert.Null(response.ErrorDescription);
Assert.Null(response.ErrorUri);
Assert.NotNull(response.DeviceCode);
Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal(new[] { "custom_value_1", "custom_value_2" }, (string[]?) response["parameter_with_multiple_values"]);
}
[Fact] [Fact]
public async Task ApplyDeviceResponse_AllowsHandlingResponse() public async Task ApplyDeviceResponse_AllowsHandlingResponse()
{ {
@ -1267,6 +1321,69 @@ namespace OpenIddict.Server.IntegrationTests
Assert.Equal("Bob le Magnifique", (string?) response["name"]); Assert.Equal("Bob le Magnifique", (string?) response["name"]);
} }
[Fact]
public async Task HandleVerificationRequest_ResponseContainsCustomParameters()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.AddEventHandler<ValidateTokenContext>(builder =>
{
builder.UseInlineHandler(context =>
{
Assert.Equal("WDJB-MJHT", context.Token);
Assert.Equal(new[] { TokenTypeHints.UserCode }, context.ValidTokenTypes);
context.Principal = new ClaimsPrincipal(new ClaimsIdentity())
.SetTokenType(TokenTypeHints.UserCode);
return default;
});
builder.SetOrder(ValidateIdentityModelToken.Descriptor.Order - 500);
});
options.AddEventHandler<HandleVerificationRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetClaim(Claims.Subject, "Bob le Magnifique");
context.Parameters["custom_parameter"] = "custom_value";
context.Parameters["parameter_with_multiple_values"] = new[]
{
"custom_value_1",
"custom_value_2"
};
return default;
}));
options.AddEventHandler<ApplyVerificationResponseContext>(builder =>
builder.UseInlineHandler(context =>
{
context.Transaction.SetProperty("custom_response", context.Response);
context.HandleRequest();
return default;
}));
});
await using var client = await server.CreateClientAsync();
// Act
var response = await client.PostAsync("/connect/verification", new OpenIddictRequest
{
UserCode = "WDJB-MJHT"
});
// 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] [Fact]
public async Task ApplyVerificationResponse_AllowsHandlingResponse() public async Task ApplyVerificationResponse_AllowsHandlingResponse()
{ {

44
test/OpenIddict.Server.IntegrationTests/OpenIddictServerIntegrationTests.Exchange.cs

@ -4024,6 +4024,50 @@ namespace OpenIddict.Server.IntegrationTests
Assert.Equal("Bob le Magnifique", (string?) response["name"]); Assert.Equal("Bob le Magnifique", (string?) response["name"]);
} }
[Fact]
public async Task HandleTokenRequest_ResponseContainsCustomParameters()
{
// Arrange
await using var server = await CreateServerAsync(options =>
{
options.EnableDegradedMode();
options.AddEventHandler<HandleTokenRequestContext>(builder =>
builder.UseInlineHandler(context =>
{
context.Principal = new ClaimsPrincipal(new ClaimsIdentity("Bearer"))
.SetClaim(Claims.Subject, "Bob le Magnifique");
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/token", new OpenIddictRequest
{
GrantType = GrantTypes.Password,
Username = "johndoe",
Password = "A3ddj3w"
});
// Assert
Assert.Null(response.Error);
Assert.Null(response.ErrorDescription);
Assert.Null(response.ErrorUri);
Assert.NotNull(response.AccessToken);
Assert.Equal("custom_value", (string?) response["custom_parameter"]);
Assert.Equal(new[] { "custom_value_1", "custom_value_2" }, (string[]?) response["parameter_with_multiple_values"]);
}
[Fact] [Fact]
public async Task ApplyTokenResponse_AllowsHandlingResponse() public async Task ApplyTokenResponse_AllowsHandlingResponse()
{ {

Loading…
Cancel
Save