1
0
mirror of https://github.com/bitwarden/server synced 2026-02-24 16:42:52 +00:00

fix(redirect): [PM-30810] Https Redirection for Cloud Users - Clarified messaging around how specific clients will build the response as well as added tests.

This commit is contained in:
Patrick Pimentel
2026-01-23 17:57:39 -05:00
parent dd6c49e214
commit 094754b58f
3 changed files with 254 additions and 15 deletions

View File

@@ -1,8 +1,12 @@
using Bit.Core.Auth.Identity.TokenProviders;
using Bit.Core.Auth.Models;
using Bit.Core.Context;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Microsoft.AspNetCore.Http;
using NSubstitute;
using Xunit;
using CoreSettings = Bit.Core.Settings;
namespace Bit.Core.Test.Auth.Services;
@@ -87,5 +91,203 @@ public class DuoUniversalTokenServiceTests
Assert.Equal(result, expectedResponse);
}
[Theory]
[BitAutoData("vault.bitwarden.com")] // Cloud US
[BitAutoData("vault.bitwarden.eu")] // Cloud EU
public void BuildDuoTwoFactorRedirectUri_MobileClient_CloudHost_ReturnsHttpsScheme(
string requestHost)
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Bitwarden-Client-Name"] = "mobile";
httpContext.Request.Host = new HostString(requestHost);
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.bitwarden.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = sut.BuildDuoTwoFactorRedirectUri();
// Assert
Assert.Contains("client=mobile", result);
Assert.Contains("deeplinkScheme=https", result);
Assert.StartsWith("https://vault.bitwarden.com/duo-redirect-connector.html", result);
}
[Theory]
[BitAutoData("selfhosted.example.com")]
[BitAutoData("192.168.1.100")]
public void BuildDuoTwoFactorRedirectUri_MobileClient_SelfHosted_ReturnsBitwardenScheme(
string requestHost)
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Bitwarden-Client-Name"] = "mobile";
httpContext.Request.Host = new HostString(requestHost);
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.example.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = sut.BuildDuoTwoFactorRedirectUri();
// Assert
Assert.Contains("client=mobile", result);
Assert.Contains("deeplinkScheme=bitwarden", result);
}
[Fact]
public void BuildDuoTwoFactorRedirectUri_DesktopClient_ReturnsBitwardenScheme()
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Bitwarden-Client-Name"] = "desktop";
httpContext.Request.Host = new HostString("vault.bitwarden.com");
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.bitwarden.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = sut.BuildDuoTwoFactorRedirectUri();
// Assert
Assert.Contains("client=desktop", result);
Assert.Contains("deeplinkScheme=bitwarden", result);
}
[Theory]
[BitAutoData("web")]
[BitAutoData("browser")]
[BitAutoData("cli")]
public void BuildDuoTwoFactorRedirectUri_NonMobileNonDesktopClient_NoDeeplinkScheme(
string clientName)
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Bitwarden-Client-Name"] = clientName;
httpContext.Request.Host = new HostString("vault.bitwarden.com");
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.bitwarden.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = sut.BuildDuoTwoFactorRedirectUri();
// Assert
Assert.Contains($"client={clientName}", result);
Assert.DoesNotContain("deeplinkScheme", result);
}
[Fact]
public void BuildDuoTwoFactorRedirectUri_NoClientHeader_DefaultsToWeb()
{
// Arrange
var httpContext = new DefaultHttpContext();
// No Bitwarden-Client-Name header set
httpContext.Request.Host = new HostString("vault.bitwarden.com");
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.bitwarden.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = sut.BuildDuoTwoFactorRedirectUri();
// Assert
Assert.Contains("client=web", result);
Assert.DoesNotContain("deeplinkScheme", result);
}
[Theory]
[BitAutoData("invalid-client")]
[BitAutoData("unknown")]
public void BuildDuoTwoFactorRedirectUri_InvalidClientHeader_DefaultsToWeb(
string invalidClientName)
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Bitwarden-Client-Name"] = invalidClientName;
httpContext.Request.Host = new HostString("vault.bitwarden.com");
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.bitwarden.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = sut.BuildDuoTwoFactorRedirectUri();
// Assert
Assert.Contains("client=web", result);
Assert.DoesNotContain("deeplinkScheme", result);
}
[Theory]
[BitAutoData("MOBILE")]
[BitAutoData("Mobile")]
[BitAutoData("MoBiLe")]
public void BuildDuoTwoFactorRedirectUri_ClientHeaderCaseInsensitive(
string clientName)
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Bitwarden-Client-Name"] = clientName;
httpContext.Request.Host = new HostString("vault.bitwarden.com");
var currentContext = Substitute.For<ICurrentContext>();
currentContext.HttpContext.Returns(httpContext);
var globalSettings = new CoreSettings.GlobalSettings
{
BaseServiceUri = new CoreSettings.GlobalSettings.BaseServiceUriSettings(new CoreSettings.GlobalSettings()) { Vault = "https://vault.bitwarden.com" }
};
var sut = new DuoUniversalTokenService(currentContext, globalSettings);
// Act
var result = sut.BuildDuoTwoFactorRedirectUri();
// Assert
Assert.Contains("client=mobile", result);
Assert.Contains("deeplinkScheme=https", result);
}
}