From 4defea8e4d813094c6bf2b76baff989828740bcc Mon Sep 17 00:00:00 2001 From: boris324 Date: Wed, 4 Mar 2026 22:46:40 +0000 Subject: [PATCH 1/2] Fix OIDC response_mode to use spec-compliant default (#5461) Change hardcoded response_mode from "form_post" to "query" for external IdP OIDC configurations. Per OpenID Connect Discovery 1.0 spec, the default supported response modes when omitted are "query" and "fragment" - not "form_post". Using "form_post" breaks SSO login with strict IdPs that don't support it and actively reject it. The "query" mode is what Bitwarden already accepts on the callback, so this change aligns the request with actual behavior. Co-Authored-By: Claude Opus 4.6 --- .../src/Sso/Utilities/DynamicAuthenticationSchemeProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitwarden_license/src/Sso/Utilities/DynamicAuthenticationSchemeProvider.cs b/bitwarden_license/src/Sso/Utilities/DynamicAuthenticationSchemeProvider.cs index db574e71c536..7a26a37f0d09 100644 --- a/bitwarden_license/src/Sso/Utilities/DynamicAuthenticationSchemeProvider.cs +++ b/bitwarden_license/src/Sso/Utilities/DynamicAuthenticationSchemeProvider.cs @@ -299,7 +299,7 @@ private DynamicAuthenticationScheme GetOidcAuthenticationScheme(string name, Sso ClientId = config.ClientId, ClientSecret = config.ClientSecret, ResponseType = "code", - ResponseMode = "form_post", + ResponseMode = "query", SignInScheme = AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme, SignOutScheme = IdentityServerConstants.SignoutScheme, SaveTokens = false, // reduce overall request size From 83f18521ba8e587f1c1846763722fa1b41747bf7 Mon Sep 17 00:00:00 2001 From: boris324 Date: Fri, 6 Mar 2026 18:35:49 +0000 Subject: [PATCH 2/2] Fix OSS runtime error by registering IBusinessUnitConverter in DI When Bitwarden is compiled with the OSS flag, IBusinessUnitConverter was not registered in the dependency injection container, causing a runtime error when OrganizationBillingController was activated. This adds a NoopBusinessUnitConverter implementation that throws NotSupportedException (consistent with other OSS noop services) and registers it in the OSS service collection via AddOosServices(). Fixes bitwarden/server#6292 Co-Authored-By: Claude Opus 4.6 --- .../NoopBusinessUnitConverter.cs | 63 +++++++++++++++++++ .../Utilities/ServiceCollectionExtensions.cs | 3 + .../NoopBusinessUnitConverterTests.cs | 63 +++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 src/Core/Billing/Providers/Services/NoopImplementations/NoopBusinessUnitConverter.cs create mode 100644 test/Core.Test/Billing/Providers/Services/NoopImplementations/NoopBusinessUnitConverterTests.cs diff --git a/src/Core/Billing/Providers/Services/NoopImplementations/NoopBusinessUnitConverter.cs b/src/Core/Billing/Providers/Services/NoopImplementations/NoopBusinessUnitConverter.cs new file mode 100644 index 000000000000..65732f8a02c4 --- /dev/null +++ b/src/Core/Billing/Providers/Services/NoopImplementations/NoopBusinessUnitConverter.cs @@ -0,0 +1,63 @@ +using Bit.Core.AdminConsole.Entities; +using OneOf; + +namespace Bit.Core.Billing.Providers.Services.NoopImplementations; + +/// +/// A no-op implementation of for use in OSS (non-commercial) builds. +/// Business unit conversion is a commercial feature and is not available in OSS deployments. +/// All methods throw to indicate the feature is unavailable. +/// +public class NoopBusinessUnitConverter : IBusinessUnitConverter +{ + /// + /// + /// Always thrown because business unit conversion is not available in OSS builds. + /// + public Task FinalizeConversion( + Organization organization, + Guid userId, + string token, + string providerKey, + string organizationKey) + { + throw new NotSupportedException( + "Business unit conversion is not available in non-commercial Bitwarden builds."); + } + + /// + /// + /// Always thrown because business unit conversion is not available in OSS builds. + /// + public Task>> InitiateConversion( + Organization organization, + string providerAdminEmail) + { + throw new NotSupportedException( + "Business unit conversion is not available in non-commercial Bitwarden builds."); + } + + /// + /// + /// Always thrown because business unit conversion is not available in OSS builds. + /// + public Task ResendConversionInvite( + Organization organization, + string providerAdminEmail) + { + throw new NotSupportedException( + "Business unit conversion is not available in non-commercial Bitwarden builds."); + } + + /// + /// + /// Always thrown because business unit conversion is not available in OSS builds. + /// + public Task ResetConversion( + Organization organization, + string providerAdminEmail) + { + throw new NotSupportedException( + "Business unit conversion is not available in non-commercial Bitwarden builds."); + } +} diff --git a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs index 8d65547f7621..1d8666de7eaf 100644 --- a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs +++ b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs @@ -23,6 +23,8 @@ using Bit.Core.Auth.UserFeatures; using Bit.Core.Auth.UserFeatures.EmergencyAccess; using Bit.Core.Auth.UserFeatures.PasswordValidation; +using Bit.Core.Billing.Providers.Services; +using Bit.Core.Billing.Providers.Services.NoopImplementations; using Bit.Core.Billing.Services; using Bit.Core.Billing.Services.Implementations; using Bit.Core.Billing.TrialInitiation; @@ -366,6 +368,7 @@ public static void AddDefaultServices(this IServiceCollection services, GlobalSe public static void AddOosServices(this IServiceCollection services) { services.AddScoped(); + services.AddTransient(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/test/Core.Test/Billing/Providers/Services/NoopImplementations/NoopBusinessUnitConverterTests.cs b/test/Core.Test/Billing/Providers/Services/NoopImplementations/NoopBusinessUnitConverterTests.cs new file mode 100644 index 000000000000..814d8a8ece33 --- /dev/null +++ b/test/Core.Test/Billing/Providers/Services/NoopImplementations/NoopBusinessUnitConverterTests.cs @@ -0,0 +1,63 @@ +using Bit.Core.AdminConsole.Entities; +using Bit.Core.Billing.Providers.Services.NoopImplementations; +using Xunit; + +namespace Bit.Core.Test.Billing.Providers.Services.NoopImplementations; + +public class NoopBusinessUnitConverterTests +{ + private readonly NoopBusinessUnitConverter _sut = new(); + + [Fact] + public async Task FinalizeConversion_ThrowsNotSupportedException() + { + var organization = new Organization(); + + await Assert.ThrowsAsync( + () => _sut.FinalizeConversion( + organization, + Guid.NewGuid(), + "token", + "providerKey", + "organizationKey")); + } + + [Fact] + public async Task InitiateConversion_ThrowsNotSupportedException() + { + var organization = new Organization(); + + await Assert.ThrowsAsync( + () => _sut.InitiateConversion( + organization, + "admin@example.com")); + } + + [Fact] + public async Task ResendConversionInvite_ThrowsNotSupportedException() + { + var organization = new Organization(); + + await Assert.ThrowsAsync( + () => _sut.ResendConversionInvite( + organization, + "admin@example.com")); + } + + [Fact] + public async Task ResetConversion_ThrowsNotSupportedException() + { + var organization = new Organization(); + + await Assert.ThrowsAsync( + () => _sut.ResetConversion( + organization, + "admin@example.com")); + } + + [Fact] + public void ImplementsIBusinessUnitConverter() + { + Assert.IsAssignableFrom(_sut); + } +}