Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 9 additions & 12 deletions src/Api/Utilities/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,17 @@ [this document](https://bitwarden.com/help/vault-management-api/).

config.SwaggerDoc("internal", new OpenApiInfo { Title = "Bitwarden Internal API", Version = "latest" });

// Configure Bitwarden cloud US and EU servers. These will appear in the swagger.json build artifact
// Configure Bitwarden cloud servers. These will appear in the swagger.json build artifact
// used for our help center. These are overwritten with the local server when running in self-hosted
// or dev mode (see Api Startup.cs).
config.AddSwaggerServerWithSecurity(
serverId: "US_server",
serverUrl: "https://api.bitwarden.com",
identityTokenUrl: "https://identity.bitwarden.com/connect/token",
serverDescription: "US server");

config.AddSwaggerServerWithSecurity(
serverId: "EU_server",
serverUrl: "https://api.bitwarden.eu",
identityTokenUrl: "https://identity.bitwarden.eu/connect/token",
serverDescription: "EU server");
foreach (var regionConfig in CloudRegionConfig.All)
{
config.AddSwaggerServerWithSecurity(
serverId: $"{regionConfig.Region}_server",
serverUrl: regionConfig.ApiUrl,
identityTokenUrl: $"{regionConfig.IdentityUrl}/connect/token",
serverDescription: $"{regionConfig.Region} server");
}

// Security scheme for send access token endpoints (V2). The x-explicit-bearer-token
// extension signals the SDK code generator to emit an explicit Bearer token parameter
Expand Down
14 changes: 11 additions & 3 deletions src/Core/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#nullable disable

using System.Reflection;
using Bit.Core.Settings;

namespace Bit.Core;

Expand Down Expand Up @@ -39,17 +40,24 @@ public static class Constants
/// <summary>
/// Domain suffixes for Bitwarden cloud-hosted environments.
/// </summary>
public static readonly string[] BitwardenCloudDomains = ["bitwarden.com", "bitwarden.eu", "bitwarden.pw"];
public static readonly string[] BitwardenCloudDomains =
[
// bitwarden.pw is the QA environment domain; not a user-facing cloud region so it
// has no CloudRegionConfig entry, but must remain in the allowlist for HTTPS redirect
// validation to pass in QA deployments.
"bitwarden.pw",
..CloudRegionConfig.All.Select(c => c.Domain),
];

/// <summary>
/// Server permitted SSO callback redirect URIs for mobile clients.
/// </summary>
public static readonly string[] BitwardenMobileSsoCallbackUris =
[
"bitwarden://sso-callback",
"https://bitwarden.com/sso-callback",
"https://bitwarden.eu/sso-callback",
// bitwarden.pw is the QA environment domain; retained for QA SSO callback validation.
"https://bitwarden.pw/sso-callback",
..CloudRegionConfig.All.Select(c => c.SsoCallbackUri),
];

/// <summary>
Expand Down
15 changes: 9 additions & 6 deletions src/Core/Platform/Mail/HandlebarsMailService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Bit.Core.Billing.Enums;
using Bit.Core.Billing.Models.Mail;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Data.Organizations;
using Bit.Core.Models.Mail;
Expand Down Expand Up @@ -1641,10 +1642,12 @@ private static string GetUserIdentifier(string email, string? userName)
return string.IsNullOrEmpty(userName) ? email : CoreHelpers.SanitizeForEmail(userName, false);
}

private string GetCloudVaultSubscriptionUrl(Guid organizationId)
=> _globalSettings.BaseServiceUri.CloudRegion?.ToLower() switch
{
"eu" => $"https://vault.bitwarden.eu/#/organizations/{organizationId}/billing/subscription",
_ => $"https://vault.bitwarden.com/#/organizations/{organizationId}/billing/subscription"
};
public string GetCloudVaultSubscriptionUrl(Guid organizationId)
{
var region = Enum.TryParse<CloudRegion>(_globalSettings.BaseServiceUri.CloudRegion, ignoreCase: true, out var parsed)
? parsed
: CloudRegion.US;
var regionConfig = CloudRegionConfig.FindByRegion(region);
return $"{regionConfig.VaultUrl}/#/organizations/{organizationId}/billing/subscription";
}
}
63 changes: 63 additions & 0 deletions src/Core/Settings/CloudRegionConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using Bit.Core.Enums;

namespace Bit.Core.Settings;

/// <summary>
/// Canonical per-region URLs for Bitwarden's public cloud regions. A region is one
/// entry in <see cref="All"/>; consumers resolve URLs through
/// <see cref="FindByRegion"/> or <see cref="FindByDomain"/>.
/// </summary>
/// <remarks>
/// Scope: URLs that are fixed by region and shared across multiple consumers belong
/// here. URLs that vary per deployment -- those sourced from <c>GlobalSettings</c>/
/// appsettings, such as notifications, icons, and events -- do not.
/// </remarks>
public sealed class CloudRegionConfig
{
private CloudRegionConfig(
CloudRegion region,
string domain,
string apiUrl,
string identityUrl,
string vaultUrl,
string ssoCallbackUri)
{
Region = region;
Domain = domain;
ApiUrl = apiUrl;
IdentityUrl = identityUrl;
VaultUrl = vaultUrl;
SsoCallbackUri = ssoCallbackUri;
}

public CloudRegion Region { get; }
public string Domain { get; }
public string ApiUrl { get; }
public string IdentityUrl { get; }
public string VaultUrl { get; }
public string SsoCallbackUri { get; }

public static readonly CloudRegionConfig[] All =
[
new(
CloudRegion.US,
"bitwarden.com",
"https://api.bitwarden.com",
"https://identity.bitwarden.com",
"https://vault.bitwarden.com",
"https://bitwarden.com/sso-callback"),
new(
CloudRegion.EU,
"bitwarden.eu",
"https://api.bitwarden.eu",
"https://identity.bitwarden.eu",
"https://vault.bitwarden.eu",
"https://bitwarden.eu/sso-callback"),
];

public static CloudRegionConfig FindByDomain(string domain) =>
All.FirstOrDefault(x => x.Domain == domain) ?? All[0];

public static CloudRegionConfig FindByRegion(CloudRegion region) =>
All.FirstOrDefault(x => x.Region == region) ?? All[0];
}
Loading