From 762d1a7d2f220dd2d1e2fc9cf841ba7d649e5fee Mon Sep 17 00:00:00 2001 From: Patrick Pimentel Date: Fri, 9 Jan 2026 15:23:07 -0500 Subject: [PATCH] fix(redirect): [PM-26578] Https Redirection for Cloud Users - Added changes to be able to test locally. --- .../DuoUniversalTokenService.cs | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/Core/Auth/Identity/TokenProviders/DuoUniversalTokenService.cs b/src/Core/Auth/Identity/TokenProviders/DuoUniversalTokenService.cs index 2a299cbcd9..d7ac1e2510 100644 --- a/src/Core/Auth/Identity/TokenProviders/DuoUniversalTokenService.cs +++ b/src/Core/Auth/Identity/TokenProviders/DuoUniversalTokenService.cs @@ -7,6 +7,7 @@ using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Settings; using Bit.Core.Tokens; +using Microsoft.AspNetCore.Http; using Duo = DuoUniversal; namespace Bit.Core.Auth.Identity.TokenProviders; @@ -170,13 +171,51 @@ public class DuoUniversalTokenService( normalizedHost.EndsWith("bitwarden.pw"); } + private static bool IsLocalRequestHost(string host) + { + if (string.IsNullOrWhiteSpace(host)) + { + return false; + } + + var normalizedHost = host.ToLowerInvariant(); + return normalizedHost == "localhost" || + normalizedHost == "127.0.0.1" || + normalizedHost == "::1" || + normalizedHost.EndsWith(".localhost"); + } + + private static string GetDeeplinkSchemeOverride(HttpContext httpContext) + { + if (httpContext == null) + { + return null; + } + + var host = httpContext.Request?.Host.Host; + // Only allow overrides when developing/testing locally to avoid abuse in production + if (!IsLocalRequestHost(host)) + { + return null; + } + + // Querystring has precedence over header for manual local testing + var overrideFromQuery = httpContext.Request?.Query["deeplinkScheme"].FirstOrDefault(); + var overrideFromHeader = httpContext.Request?.Headers["Bitwarden-Deeplink-Scheme"].FirstOrDefault(); + var candidate = (overrideFromQuery ?? overrideFromHeader)?.Trim().ToLowerInvariant(); + + // Allow only the two supported values + return candidate is "https" or "bitwarden" ? candidate : null; + } + public async Task BuildDuoTwoFactorClientAsync(TwoFactorProvider provider) { // Fetch Client name from header value since duo auth can be initiated from multiple clients and we want // to redirect back to the initiating client _currentContext.HttpContext.Request.Headers.TryGetValue("Bitwarden-Client-Name", out var bitwardenClientName); var requestHost = _currentContext.HttpContext?.Request?.Host.Host; - var deeplinkScheme = IsBitwardenCloudHost(requestHost) ? "https" : "bitwarden"; + var deeplinkScheme = GetDeeplinkSchemeOverride(_currentContext.HttpContext) ?? + (IsBitwardenCloudHost(requestHost) ? "https" : "bitwarden"); var redirectUri = string.Format("{0}/duo-redirect-connector.html?client={1}&deeplinkScheme={2}", _globalSettings.BaseServiceUri.Vault, bitwardenClientName.FirstOrDefault() ?? "web", deeplinkScheme);