From b76ea37ca5822ac33d520077b162d7dfb339e907 Mon Sep 17 00:00:00 2001 From: Joe DeCock Date: Wed, 14 Aug 2024 11:32:56 -0500 Subject: [PATCH 1/2] Allow SupportedPromptModes customization --- .../DependencyInjection/Options/UserInteractionOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IdentityServer/Configuration/DependencyInjection/Options/UserInteractionOptions.cs b/src/IdentityServer/Configuration/DependencyInjection/Options/UserInteractionOptions.cs index a1cb98566..5bd59a45a 100644 --- a/src/IdentityServer/Configuration/DependencyInjection/Options/UserInteractionOptions.cs +++ b/src/IdentityServer/Configuration/DependencyInjection/Options/UserInteractionOptions.cs @@ -135,5 +135,5 @@ public class UserInteractionOptions /// The collection of OIDC prompt modes supported and that will be published in discovery. /// The value "create" is omitted unless the CreateAccountUrl value is set. /// - internal ICollection PromptValuesSupported { get; set; } = new HashSet(Constants.SupportedPromptModes); + public ICollection PromptValuesSupported { get; set; } = new HashSet(Constants.SupportedPromptModes); } From d4b3e458dae40f2ac5cd59292143f1ae5f1b050d Mon Sep 17 00:00:00 2001 From: Joe DeCock Date: Wed, 18 Sep 2024 13:37:51 -0500 Subject: [PATCH 2/2] Add tests for customized prompt values --- .../Options/UserInteractionOptions.cs | 10 +++- .../Endpoints/Authorize/AuthorizeTests.cs | 58 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/IdentityServer/Configuration/DependencyInjection/Options/UserInteractionOptions.cs b/src/IdentityServer/Configuration/DependencyInjection/Options/UserInteractionOptions.cs index 5bd59a45a..352161ea6 100644 --- a/src/IdentityServer/Configuration/DependencyInjection/Options/UserInteractionOptions.cs +++ b/src/IdentityServer/Configuration/DependencyInjection/Options/UserInteractionOptions.cs @@ -5,6 +5,7 @@ #nullable enable using Duende.IdentityServer.Extensions; +using Duende.IdentityServer.ResponseHandling; using System.Collections.Generic; namespace Duende.IdentityServer.Configuration; @@ -132,8 +133,13 @@ public class UserInteractionOptions public bool AllowOriginInReturnUrl { get; set; } /// - /// The collection of OIDC prompt modes supported and that will be published in discovery. - /// The value "create" is omitted unless the CreateAccountUrl value is set. + /// The collection of OIDC prompt modes supported and that will be published + /// in discovery. By default, this includes all values in . If the option is set, then the "create" value is also + /// included. If additional prompt values are added, a customized is also required to + /// handle those values. /// public ICollection PromptValuesSupported { get; set; } = new HashSet(Constants.SupportedPromptModes); } diff --git a/test/IdentityServer.IntegrationTests/Endpoints/Authorize/AuthorizeTests.cs b/test/IdentityServer.IntegrationTests/Endpoints/Authorize/AuthorizeTests.cs index 0b7a8f93a..f79c14b97 100644 --- a/test/IdentityServer.IntegrationTests/Endpoints/Authorize/AuthorizeTests.cs +++ b/test/IdentityServer.IntegrationTests/Endpoints/Authorize/AuthorizeTests.cs @@ -1485,14 +1485,72 @@ public async Task custom_request_should_have_authorization_params(Type storeType _mockPipeline.CustomRequest.Parameters.AllKeys.Should().Contain("foo"); _mockPipeline.CustomRequest.Parameters["foo"].Should().Be("bar"); } + + [Fact] + public async Task custom_prompt_values_should_raise_error_with_default_interaction_service() + { + _mockPipeline.Options.UserInteraction.PromptValuesSupported.Add("custom-prompt"); + await _mockPipeline.LoginAsync("bob"); + + var url = _mockPipeline.CreateAuthorizeUrl( + clientId: "client1", + responseType: "id_token", + scope: "openid profile", + redirectUri: "https://client1/callback", + state: "123_state", + nonce: "123_nonce", + extra: new { prompt = "custom-prompt" } + ); + + _mockPipeline.BrowserClient.AllowAutoRedirect = false; + + Func a = () => _mockPipeline.BrowserClient.GetAsync(url); + await a.Should().ThrowAsync(); + } + + [Fact] + public async Task custom_prompt_value_should_be_passed_to_custom_interaction_service() + { + var mockAuthzInteractionService = new MockAuthzInteractionService(); + mockAuthzInteractionService.Response.RedirectUrl = "/custom"; + _mockPipeline.OnPostConfigureServices += services => + { + services.AddTransient(typeof(IAuthorizeInteractionResponseGenerator), svc => mockAuthzInteractionService); + }; + _mockPipeline.Initialize(); + + _mockPipeline.Options.UserInteraction.PromptValuesSupported.Add("custom-prompt"); + + await _mockPipeline.LoginAsync("bob"); + + var url = _mockPipeline.CreateAuthorizeUrl( + clientId: "client1", + responseType: "id_token", + scope: "openid profile", + redirectUri: "https://client1/callback", + state: "123_state", + nonce: "123_nonce", + extra: new { prompt = "custom-prompt" } + ); + + _mockPipeline.BrowserClient.AllowAutoRedirect = false; + + var response = await _mockPipeline.BrowserClient.GetAsync(url); + response.Headers.Location.GetLeftPart(UriPartial.Path).Should().Be("https://server/custom"); + mockAuthzInteractionService.Request.PromptModes.Should() + .Contain("custom-prompt").And + .HaveCount(1); + } } public class MockAuthzInteractionService : IAuthorizeInteractionResponseGenerator { public InteractionResponse Response { get; set; } = new InteractionResponse(); + public ValidatedAuthorizeRequest Request { get; internal set; } public Task ProcessInteractionAsync(ValidatedAuthorizeRequest request, ConsentResponse consent = null) { + Request = request; return Task.FromResult(Response); } } \ No newline at end of file