From 11319ecd32b6a5fcfb03dabb48fd6fb417170935 Mon Sep 17 00:00:00 2001 From: Erwin van der Valk Date: Tue, 17 Feb 2026 17:08:14 +0100 Subject: [PATCH] Merge pull request #2363 from DuendeSoftware/ev/bff/fix-no-forbid-scheme Fix stack overflow exception when explicitly configuring authentication schemes without configuring a forbid scheme. --- .../BffConfigureAuthenticationOptions.cs | 15 +++-- .../Bff.Tests/Endpoints/LocalEndpointTests.cs | 56 +++++++++++++++++++ 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/bff/src/Bff/DynamicFrontends/BffConfigureAuthenticationOptions.cs b/bff/src/Bff/DynamicFrontends/BffConfigureAuthenticationOptions.cs index 72b02faab..ea2629f50 100644 --- a/bff/src/Bff/DynamicFrontends/BffConfigureAuthenticationOptions.cs +++ b/bff/src/Bff/DynamicFrontends/BffConfigureAuthenticationOptions.cs @@ -18,13 +18,16 @@ internal class BffConfigureAuthenticationOptions : IPostConfigureOptions + { + opt.BackchannelHttpHandler = Internet; + opt.ConfigureOpenIdConnectDefaults = The.DefaultOpenIdConnectConfiguration; + }; + + // this is regards to an issue reported by one of our users: + // https://github.com/orgs/DuendeSoftware/discussions/488 + // We have to explicitly configure the authentication schemes, but + // not set the DefaultForbidScheme, otherwise when an authorization policy + // fails, the BFF doesn't know which scheme to use for the forbid response, + // and that causes a StackOverflowException. + bff.OnConfigureServices += s => s.AddAuthentication(options => + { + options.DefaultScheme = BffAuthenticationSchemes.BffCookie; + options.DefaultChallengeScheme = BffAuthenticationSchemes.BffOpenIdConnect; + options.DefaultSignOutScheme = BffAuthenticationSchemes.BffOpenIdConnect; + }); + + // This test verifies that when an authorization policy fails (not just RequireAuthenticatedUser, + // but a custom policy like RequireClaim), the BFF correctly returns 403 without causing + // a StackOverflowException. This was a bug when DefaultForbidScheme was not set. + AddCustomUserClaims(new System.Security.Claims.Claim("given_name", "Alice")); + + bff.OnConfigureApp += app => + { + app.Map(The.Path, c => ApiHost.ReturnApiCallDetails(c, () => LocalApiResponseStatus)) + .RequireAuthorization(policy => + { + policy.RequireAuthenticatedUser(); + policy.RequireClaim("given_name", "Bob"); // Alice won't have this claim + }) + .AsBffApiEndpoint(); + }; + + await identityServer.InitializeAsync(); + await bff.InitializeAsync(); + + await bff.BrowserClient.Login(); + bff.BrowserClient.RedirectHandler.AutoFollowRedirects = false; + + await bff.BrowserClient.CallBffHostApi( + url: Bff.Url(The.Path), + expectedStatusCode: HttpStatusCode.Forbidden + ); + } }