From aa2d2637518aa049fd07272f0d830b970826e897 Mon Sep 17 00:00:00 2001
From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com>
Date: Fri, 21 Nov 2025 14:48:50 -0600
Subject: [PATCH 1/5] [PM-24505] Manually open extension error message (#17116)
* update manual open message to be more generic to cover more scenarios
* update error state when attempting to open the extension via button press
---
...browser-extension-prompt.component.spec.ts | 4 +--
.../browser-extension-prompt.component.ts | 5 ++--
.../manually-open-extension.component.html | 8 ++---
.../manually-open-extension.component.ts | 5 ++--
.../setup-extension.component.html | 30 ++++++++-----------
.../setup-extension.component.spec.ts | 13 --------
.../setup-extension.component.ts | 24 +++++++--------
apps/web/src/locales/en/messages.json | 23 +++++++-------
8 files changed, 44 insertions(+), 68 deletions(-)
diff --git a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.spec.ts b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.spec.ts
index a19606f6d9c..b31759a1fe1 100644
--- a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.spec.ts
+++ b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.spec.ts
@@ -166,8 +166,8 @@ describe("BrowserExtensionPromptComponent", () => {
it("shows manual open error message", () => {
const manualText = fixture.debugElement.query(By.css("p")).nativeElement;
- expect(manualText.textContent.trim()).toContain("openExtensionManuallyPart1");
- expect(manualText.textContent.trim()).toContain("openExtensionManuallyPart2");
+ expect(manualText.textContent.trim()).toContain("openExtensionFromToolbarPart1");
+ expect(manualText.textContent.trim()).toContain("openExtensionFromToolbarPart2");
});
});
});
diff --git a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.ts b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.ts
index cb927d0848c..505a0df5032 100644
--- a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.ts
+++ b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.ts
@@ -1,5 +1,5 @@
import { CommonModule, DOCUMENT } from "@angular/common";
-import { Component, Inject, OnDestroy, OnInit } from "@angular/core";
+import { Component, Inject, OnDestroy, OnInit, ChangeDetectionStrategy } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import { ActivatedRoute } from "@angular/router";
import { map, Observable, of, tap } from "rxjs";
@@ -14,12 +14,11 @@ import {
} from "../../services/browser-extension-prompt.service";
import { ManuallyOpenExtensionComponent } from "../manually-open-extension/manually-open-extension.component";
-// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
-// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
selector: "vault-browser-extension-prompt",
templateUrl: "./browser-extension-prompt.component.html",
imports: [CommonModule, I18nPipe, ButtonComponent, IconModule, ManuallyOpenExtensionComponent],
+ changeDetection: ChangeDetectionStrategy.OnPush,
})
export class BrowserExtensionPromptComponent implements OnInit, OnDestroy {
protected VaultMessages = VaultMessages;
diff --git a/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.html b/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.html
index 22c36e51177..d15cdaa712b 100644
--- a/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.html
+++ b/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.html
@@ -1,8 +1,8 @@
-
- {{ "openExtensionManuallyPart1" | i18n }}
+
+ {{ "openExtensionFromToolbarPart1" | i18n }}
- {{ "openExtensionManuallyPart2" | i18n }}
+ {{ "openExtensionFromToolbarPart2" | i18n }}
diff --git a/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.ts b/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.ts
index 6105aeacf9c..435e847f6e9 100644
--- a/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.ts
+++ b/apps/web/src/app/vault/components/manually-open-extension/manually-open-extension.component.ts
@@ -1,12 +1,11 @@
-import { Component } from "@angular/core";
+import { Component, ChangeDetectionStrategy } from "@angular/core";
import { BitwardenIcon } from "@bitwarden/assets/svg";
import { IconModule } from "@bitwarden/components";
import { I18nPipe } from "@bitwarden/ui-common";
-// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
-// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
+ changeDetection: ChangeDetectionStrategy.OnPush,
selector: "vault-manually-open-extension",
templateUrl: "./manually-open-extension.component.html",
imports: [I18nPipe, IconModule],
diff --git a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.html b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.html
index 038c258d4b6..1976321b4ee 100644
--- a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.html
+++ b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.html
@@ -29,10 +29,7 @@
-
+
@@ -40,20 +37,15 @@
{{
(state === SetupExtensionState.Success
? "bitwardenExtensionInstalled"
- : "openTheBitwardenExtension"
+ : "bitwardenExtensionIsInstalled"
) | i18n
}}
- {{
- (state === SetupExtensionState.Success
- ? "openExtensionToAutofill"
- : "bitwardenExtensionInstalledOpenExtension"
- ) | i18n
- }}
+ {{ "openExtensionToAutofill" | i18n }}
-
+
+
{{ "gettingStartedWithBitwardenPart1" | i18n }}
{{ "gettingStartedWithBitwardenPart2" | i18n }}
@@ -73,7 +73,3 @@
-
-
diff --git a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.spec.ts b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.spec.ts
index fbf61f9a277..ef67e072116 100644
--- a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.spec.ts
+++ b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.spec.ts
@@ -4,7 +4,6 @@ import { Router, RouterModule } from "@angular/router";
import { BehaviorSubject } from "rxjs";
import { SYSTEM_THEME_OBSERVABLE } from "@bitwarden/angular/services/injection-tokens";
-import { BrowserExtensionIcon } from "@bitwarden/assets/svg";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { DeviceType } from "@bitwarden/common/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -12,7 +11,6 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { StateProvider } from "@bitwarden/common/platform/state";
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
-import { AnonLayoutWrapperDataService } from "@bitwarden/components";
import { WebBrowserInteractionService } from "../../services/web-browser-interaction.service";
@@ -25,14 +23,12 @@ describe("SetupExtensionComponent", () => {
const navigate = jest.fn().mockResolvedValue(true);
const openExtension = jest.fn().mockResolvedValue(true);
const update = jest.fn().mockResolvedValue(true);
- const setAnonLayoutWrapperData = jest.fn();
const extensionInstalled$ = new BehaviorSubject(null);
beforeEach(async () => {
navigate.mockClear();
openExtension.mockClear();
update.mockClear();
- setAnonLayoutWrapperData.mockClear();
window.matchMedia = jest.fn().mockReturnValue(false);
await TestBed.configureTestingModule({
@@ -43,7 +39,6 @@ describe("SetupExtensionComponent", () => {
{ provide: PlatformUtilsService, useValue: { getDevice: () => DeviceType.UnknownBrowser } },
{ provide: SYSTEM_THEME_OBSERVABLE, useValue: new BehaviorSubject("system") },
{ provide: ThemeStateService, useValue: { selectedTheme$: new BehaviorSubject("system") } },
- { provide: AnonLayoutWrapperDataService, useValue: { setAnonLayoutWrapperData } },
{
provide: AccountService,
useValue: { activeAccount$: new BehaviorSubject({ account: { id: "account-id" } }) },
@@ -133,14 +128,6 @@ describe("SetupExtensionComponent", () => {
tick();
expect(component["state"]).toBe(SetupExtensionState.ManualOpen);
- expect(setAnonLayoutWrapperData).toHaveBeenCalledWith({
- pageTitle: {
- key: "somethingWentWrong",
- },
- pageIcon: BrowserExtensionIcon,
- hideCardWrapper: false,
- maxWidth: "md",
- });
}));
});
});
diff --git a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts
index 974e73bc91e..809e404f5f1 100644
--- a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts
+++ b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts
@@ -5,7 +5,7 @@ import { Router, RouterModule } from "@angular/router";
import { firstValueFrom, pairwise, startWith } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
-import { BrowserExtensionIcon, Party } from "@bitwarden/assets/svg";
+import { Party } from "@bitwarden/assets/svg";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@@ -14,7 +14,6 @@ import { StateProvider } from "@bitwarden/common/platform/state";
import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values";
import { getWebStoreUrl } from "@bitwarden/common/vault/utils/get-web-store-url";
import {
- AnonLayoutWrapperDataService,
ButtonComponent,
CenterPositionStrategy,
DialogRef,
@@ -68,7 +67,6 @@ export class SetupExtensionComponent implements OnInit, OnDestroy {
private stateProvider = inject(StateProvider);
private accountService = inject(AccountService);
private document = inject(DOCUMENT);
- private anonLayoutWrapperDataService = inject(AnonLayoutWrapperDataService);
protected SetupExtensionState = SetupExtensionState;
protected PartyIcon = Party;
@@ -144,6 +142,16 @@ export class SetupExtensionComponent implements OnInit, OnDestroy {
}
}
+ get showSuccessUI(): boolean {
+ const successStates = [
+ SetupExtensionState.Success,
+ SetupExtensionState.AlreadyInstalled,
+ SetupExtensionState.ManualOpen,
+ ] as string[];
+
+ return successStates.includes(this.state);
+ }
+
/** Opens the add extension later dialog */
addItLater() {
this.dialogRef = this.dialogService.open(
@@ -161,16 +169,6 @@ export class SetupExtensionComponent implements OnInit, OnDestroy {
async openExtension() {
await this.webBrowserExtensionInteractionService.openExtension().catch(() => {
this.state = SetupExtensionState.ManualOpen;
-
- // Update the anon layout data to show the proper error design
- this.anonLayoutWrapperDataService.setAnonLayoutWrapperData({
- pageTitle: {
- key: "somethingWentWrong",
- },
- pageIcon: BrowserExtensionIcon,
- hideCardWrapper: false,
- maxWidth: "md",
- });
});
}
diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json
index dccb6d28af5..90468c61d5c 100644
--- a/apps/web/src/locales/en/messages.json
+++ b/apps/web/src/locales/en/messages.json
@@ -11351,14 +11351,6 @@
"openedExtensionViewAtRiskPasswords": {
"message": "Successfully opened the Bitwarden browser extension. You can now review your at-risk passwords."
},
- "openExtensionManuallyPart1": {
- "message": "We had trouble opening the Bitwarden browser extension. Open the Bitwarden icon",
- "description": "This will be used as part of a larger sentence, broken up to include the Bitwarden icon. The full sentence will read 'We had trouble opening the Bitwarden browser extension. Open the Bitwarden icon [Bitwarden Icon] from the toolbar.'"
- },
- "openExtensionManuallyPart2": {
- "message": "from the toolbar.",
- "description": "This will be used as part of a larger sentence, broken up to include the Bitwarden icon. The full sentence will read 'We had trouble opening the Bitwarden browser extension. Open the Bitwarden icon [Bitwarden Icon] from the toolbar.'"
- },
"resellerRenewalWarningMsg": {
"message": "Your subscription will renew soon. To ensure uninterrupted service, contact $RESELLER$ to confirm your renewal before $RENEWAL_DATE$.",
"placeholders": {
@@ -11679,11 +11671,8 @@
"bitwardenExtensionInstalled": {
"message": "Bitwarden extension installed!"
},
- "openTheBitwardenExtension": {
- "message": "Open the Bitwarden extension"
- },
- "bitwardenExtensionInstalledOpenExtension": {
- "message": "The Bitwarden extension is installed! Open the extension to log in and start autofilling."
+ "bitwardenExtensionIsInstalled": {
+ "message": "Bitwarden extension is installed!"
},
"openExtensionToAutofill": {
"message": "Open the extension to log in and start autofilling."
@@ -11699,6 +11688,14 @@
"message": "Learning Center",
"description": "This will be displayed as part of a larger sentence. The whole sentence reads: 'For tips on getting started with Bitwarden visit the Learning Center and Help Center'"
},
+ "openExtensionFromToolbarPart1": {
+ "message": "If the extension didn't open, you may need to open Bitwarden from the icon ",
+ "description": "This will be used as part of a larger sentence, broken up to include the Bitwarden icon. The full sentence will read 'If the extension didn't open, you may need to open Bitwarden from the icon [Bitwarden Icon] on the toolbar.'"
+ },
+ "openExtensionFromToolbarPart2": {
+ "message": " on the toolbar.",
+ "description": "This will be used as part of a larger sentence, broken up to include the Bitwarden icon. The full sentence will read 'If the extension didn't open, you may need to open Bitwarden from the icon [Bitwarden Icon] on the toolbar.'"
+ },
"gettingStartedWithBitwardenPart3": {
"message": "Help Center",
"description": "This will be displayed as part of a larger sentence. The whole sentence reads: 'For tips on getting started with Bitwarden visit the Learning Center and Help Center'"
From 23ac477bbc120c331da5cb70bea86c6b8c2cc91a Mon Sep 17 00:00:00 2001
From: Todd Martin <106564991+trmartin4@users.noreply.github.com>
Date: Fri, 21 Nov 2025 16:01:41 -0500
Subject: [PATCH 2/5] chore(feature-flag): Removed
pm-28325-remove-pm-22110-disable-alternate-login-methods flag
---
.../organization-options.component.ts | 17 +-----
.../auth/src/angular/login/login.component.ts | 11 +---
...ault-login-success-handler.service.spec.ts | 58 +++++--------------
.../default-login-success-handler.service.ts | 21 +++----
libs/common/src/enums/feature-flag.enum.ts | 2 -
5 files changed, 27 insertions(+), 82 deletions(-)
diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/components/organization-options.component.ts b/apps/web/src/app/vault/individual-vault/vault-filter/components/organization-options.component.ts
index 981e5703cb3..3b707f2d78c 100644
--- a/apps/web/src/app/vault/individual-vault/vault-filter/components/organization-options.component.ts
+++ b/apps/web/src/app/vault/individual-vault/vault-filter/components/organization-options.component.ts
@@ -25,7 +25,6 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
-import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -181,13 +180,7 @@ export class OrganizationOptionsComponent implements OnInit, OnDestroy {
message: this.i18nService.t("unlinkedSso"),
});
- const disableAlternateLoginMethodsFlagEnabled = await this.configService.getFeatureFlag(
- FeatureFlag.PM22110_DisableAlternateLoginMethods,
- );
-
- if (disableAlternateLoginMethodsFlagEnabled) {
- await this.removeEmailFromSsoRequiredCacheIfPresent();
- }
+ await this.removeEmailFromSsoRequiredCacheIfPresent();
} catch (e) {
this.logService.error(e);
}
@@ -214,13 +207,7 @@ export class OrganizationOptionsComponent implements OnInit, OnDestroy {
message: this.i18nService.t("leftOrganization"),
});
- const disableAlternateLoginMethodsFlagEnabled = await this.configService.getFeatureFlag(
- FeatureFlag.PM22110_DisableAlternateLoginMethods,
- );
-
- if (disableAlternateLoginMethodsFlagEnabled) {
- await this.removeEmailFromSsoRequiredCacheIfPresent();
- }
+ await this.removeEmailFromSsoRequiredCacheIfPresent();
} catch (e) {
this.logService.error(e);
}
diff --git a/libs/auth/src/angular/login/login.component.ts b/libs/auth/src/angular/login/login.component.ts
index 51379ed213e..0b011b5641f 100644
--- a/libs/auth/src/angular/login/login.component.ts
+++ b/libs/auth/src/angular/login/login.component.ts
@@ -205,14 +205,9 @@ export class LoginComponent implements OnInit, OnDestroy {
await this.loadRememberedEmail();
}
- const disableAlternateLoginMethodsFlagEnabled = await this.configService.getFeatureFlag(
- FeatureFlag.PM22110_DisableAlternateLoginMethods,
- );
- if (disableAlternateLoginMethodsFlagEnabled) {
- // This SSO required check should come after email has had a chance to be pre-filled (if it
- // was found in query params or was the remembered email)
- await this.determineIfSsoRequired();
- }
+ // This SSO required check should come after email has had a chance to be pre-filled (if it
+ // was found in query params or was the remembered email)
+ await this.determineIfSsoRequired();
}
private async desktopOnInit(): Promise {
diff --git a/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.spec.ts b/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.spec.ts
index 86f7be8dfc7..975e065e21e 100644
--- a/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.spec.ts
+++ b/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.spec.ts
@@ -1,7 +1,6 @@
import { MockProxy, mock } from "jest-mock-extended";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
-import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { SyncService } from "@bitwarden/common/platform/sync";
import { UserId } from "@bitwarden/common/types/guid";
@@ -58,62 +57,35 @@ describe("DefaultLoginSuccessHandlerService", () => {
expect(loginEmailService.clearLoginEmail).toHaveBeenCalled();
});
- describe("when PM22110_DisableAlternateLoginMethods flag is disabled", () => {
+ it("should get SSO email", async () => {
+ await service.run(userId);
+
+ expect(ssoLoginService.getSsoEmail).toHaveBeenCalled();
+ });
+
+ describe("given SSO email is not found", () => {
beforeEach(() => {
- configService.getFeatureFlag.mockResolvedValue(false);
+ ssoLoginService.getSsoEmail.mockResolvedValue(null);
});
- it("should not check SSO requirements", async () => {
+ it("should log error and return early", async () => {
await service.run(userId);
- expect(ssoLoginService.getSsoEmail).not.toHaveBeenCalled();
+ expect(logService.error).toHaveBeenCalledWith("SSO login email not found.");
expect(ssoLoginService.updateSsoRequiredCache).not.toHaveBeenCalled();
});
});
- describe("given PM22110_DisableAlternateLoginMethods flag is enabled", () => {
+ describe("given SSO email is found", () => {
beforeEach(() => {
- configService.getFeatureFlag.mockResolvedValue(true);
+ ssoLoginService.getSsoEmail.mockResolvedValue(testEmail);
});
- it("should check feature flag", async () => {
+ it("should call updateSsoRequiredCache() and clearSsoEmail()", async () => {
await service.run(userId);
- expect(configService.getFeatureFlag).toHaveBeenCalledWith(
- FeatureFlag.PM22110_DisableAlternateLoginMethods,
- );
- });
-
- it("should get SSO email", async () => {
- await service.run(userId);
-
- expect(ssoLoginService.getSsoEmail).toHaveBeenCalled();
- });
-
- describe("given SSO email is not found", () => {
- beforeEach(() => {
- ssoLoginService.getSsoEmail.mockResolvedValue(null);
- });
-
- it("should log error and return early", async () => {
- await service.run(userId);
-
- expect(logService.error).toHaveBeenCalledWith("SSO login email not found.");
- expect(ssoLoginService.updateSsoRequiredCache).not.toHaveBeenCalled();
- });
- });
-
- describe("given SSO email is found", () => {
- beforeEach(() => {
- ssoLoginService.getSsoEmail.mockResolvedValue(testEmail);
- });
-
- it("should call updateSsoRequiredCache() and clearSsoEmail()", async () => {
- await service.run(userId);
-
- expect(ssoLoginService.updateSsoRequiredCache).toHaveBeenCalledWith(testEmail, userId);
- expect(ssoLoginService.clearSsoEmail).toHaveBeenCalled();
- });
+ expect(ssoLoginService.updateSsoRequiredCache).toHaveBeenCalledWith(testEmail, userId);
+ expect(ssoLoginService.clearSsoEmail).toHaveBeenCalled();
});
});
});
diff --git a/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.ts b/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.ts
index 78003a4fca0..27d058c311a 100644
--- a/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.ts
+++ b/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.ts
@@ -1,5 +1,4 @@
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
-import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { SyncService } from "@bitwarden/common/platform/sync";
import { UserId } from "@bitwarden/common/types/guid";
@@ -23,20 +22,14 @@ export class DefaultLoginSuccessHandlerService implements LoginSuccessHandlerSer
await this.userAsymmetricKeysRegenerationService.regenerateIfNeeded(userId);
await this.loginEmailService.clearLoginEmail();
- const disableAlternateLoginMethodsFlagEnabled = await this.configService.getFeatureFlag(
- FeatureFlag.PM22110_DisableAlternateLoginMethods,
- );
+ const ssoLoginEmail = await this.ssoLoginService.getSsoEmail();
- if (disableAlternateLoginMethodsFlagEnabled) {
- const ssoLoginEmail = await this.ssoLoginService.getSsoEmail();
-
- if (!ssoLoginEmail) {
- this.logService.error("SSO login email not found.");
- return;
- }
-
- await this.ssoLoginService.updateSsoRequiredCache(ssoLoginEmail, userId);
- await this.ssoLoginService.clearSsoEmail();
+ if (!ssoLoginEmail) {
+ this.logService.error("SSO login email not found.");
+ return;
}
+
+ await this.ssoLoginService.updateSsoRequiredCache(ssoLoginEmail, userId);
+ await this.ssoLoginService.clearSsoEmail();
}
}
diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts
index d06a14d242f..17d5f4e9df5 100644
--- a/libs/common/src/enums/feature-flag.enum.ts
+++ b/libs/common/src/enums/feature-flag.enum.ts
@@ -16,7 +16,6 @@ export enum FeatureFlag {
BlockClaimedDomainAccountCreation = "block-claimed-domain-account-creation",
/* Auth */
- PM22110_DisableAlternateLoginMethods = "pm-22110-disable-alternate-login-methods",
PM23801_PrefetchPasswordPrelogin = "pm-23801-prefetch-password-prelogin",
/* Autofill */
@@ -118,7 +117,6 @@ export const DefaultFeatureFlagValue = {
[FeatureFlag.VaultLoadingSkeletons]: FALSE,
/* Auth */
- [FeatureFlag.PM22110_DisableAlternateLoginMethods]: FALSE,
[FeatureFlag.PM23801_PrefetchPasswordPrelogin]: FALSE,
/* Billing */
From 489eb4005780046a4f87719e5a30ae4449958fc2 Mon Sep 17 00:00:00 2001
From: neuronull <9162534+neuronull@users.noreply.github.com>
Date: Fri, 21 Nov 2025 14:02:22 -0700
Subject: [PATCH 3/5] Desktop Autotype fix IPC error handling (#17332)
* Desktop Autotype fix IPC error handling
* TS lint
* sweep sweep: fix unecessary member name qualifier
---
.../src/autofill/main/main-desktop-autotype.service.ts | 9 +++++++++
apps/desktop/src/autofill/models/autotype-errors.ts | 8 ++++++++
apps/desktop/src/autofill/preload.ts | 10 ++++++----
3 files changed, 23 insertions(+), 4 deletions(-)
create mode 100644 apps/desktop/src/autofill/models/autotype-errors.ts
diff --git a/apps/desktop/src/autofill/main/main-desktop-autotype.service.ts b/apps/desktop/src/autofill/main/main-desktop-autotype.service.ts
index e33ab0d4c3b..4dcf05a4220 100644
--- a/apps/desktop/src/autofill/main/main-desktop-autotype.service.ts
+++ b/apps/desktop/src/autofill/main/main-desktop-autotype.service.ts
@@ -5,6 +5,7 @@ import { LogService } from "@bitwarden/logging";
import { WindowMain } from "../../main/window.main";
import { stringIsNotUndefinedNullAndEmpty } from "../../utils";
+import { AutotypeMatchError } from "../models/autotype-errors";
import { AutotypeVaultData } from "../models/autotype-vault-data";
import { AutotypeKeyboardShortcut } from "../models/main-autotype-keyboard-shortcut";
@@ -56,6 +57,14 @@ export class MainDesktopAutotypeService {
this.doAutotype(vaultData, this.autotypeKeyboardShortcut.getArrayFormat());
}
});
+
+ ipcMain.on("autofill.completeAutotypeError", (_event, matchError: AutotypeMatchError) => {
+ this.logService.debug(
+ "autofill.completeAutotypeError",
+ "No match for window: " + matchError.windowTitle,
+ );
+ this.logService.error("autofill.completeAutotypeError", matchError.errorMessage);
+ });
}
disableAutotype() {
diff --git a/apps/desktop/src/autofill/models/autotype-errors.ts b/apps/desktop/src/autofill/models/autotype-errors.ts
new file mode 100644
index 00000000000..9e59b102302
--- /dev/null
+++ b/apps/desktop/src/autofill/models/autotype-errors.ts
@@ -0,0 +1,8 @@
+/**
+ * This error is surfaced when there is no matching
+ * vault item found.
+ */
+export interface AutotypeMatchError {
+ windowTitle: string;
+ errorMessage: string;
+}
diff --git a/apps/desktop/src/autofill/preload.ts b/apps/desktop/src/autofill/preload.ts
index 22b5cdf9463..e839ac223b7 100644
--- a/apps/desktop/src/autofill/preload.ts
+++ b/apps/desktop/src/autofill/preload.ts
@@ -5,6 +5,7 @@ import type { autofill } from "@bitwarden/desktop-napi";
import { Command } from "../platform/main/autofill/command";
import { RunCommandParams, RunCommandResult } from "../platform/main/autofill/native-autofill.main";
+import { AutotypeMatchError } from "./models/autotype-errors";
import { AutotypeVaultData } from "./models/autotype-vault-data";
export default {
@@ -141,7 +142,7 @@ export default {
ipcRenderer.on(
"autofill.listenAutotypeRequest",
(
- event,
+ _event,
data: {
windowTitle: string;
},
@@ -150,10 +151,11 @@ export default {
fn(windowTitle, (error, vaultData) => {
if (error) {
- ipcRenderer.send("autofill.completeError", {
+ const matchError: AutotypeMatchError = {
windowTitle,
- error: error.message,
- });
+ errorMessage: error.message,
+ };
+ ipcRenderer.send("autofill.completeAutotypeError", matchError);
return;
}
if (vaultData !== null) {
From 13940a74ae88e146286f40e9c6dec164d11c21db Mon Sep 17 00:00:00 2001
From: Bernd Schoolmann
Date: Sat, 22 Nov 2025 11:53:45 +0100
Subject: [PATCH 4/5] Fix biometrics unlock when pin is enabled (#17528)
---
.../browser/src/background/main.background.ts | 42 +++++++++----------
1 file changed, 21 insertions(+), 21 deletions(-)
diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts
index f59b6648486..fecc47af981 100644
--- a/apps/browser/src/background/main.background.ts
+++ b/apps/browser/src/background/main.background.ts
@@ -726,17 +726,6 @@ export default class MainBackground {
const pinStateService = new PinStateService(this.stateProvider);
- this.pinService = new PinService(
- this.accountService,
- this.encryptService,
- this.kdfConfigService,
- this.keyGenerationService,
- this.logService,
- this.keyService,
- this.sdkService,
- pinStateService,
- );
-
this.appIdService = new AppIdService(this.storageService, this.logService);
this.userDecryptionOptionsService = new UserDecryptionOptionsService(this.stateProvider);
@@ -756,16 +745,6 @@ export default class MainBackground {
VaultTimeoutStringType.OnRestart, // default vault timeout
);
- this.biometricsService = new BackgroundBrowserBiometricsService(
- runtimeNativeMessagingBackground,
- this.logService,
- this.keyService,
- this.biometricStateService,
- this.messagingService,
- this.vaultTimeoutSettingsService,
- this.pinService,
- );
-
this.apiService = new ApiService(
this.tokenService,
this.platformUtilsService,
@@ -849,6 +828,27 @@ export default class MainBackground {
this.configService,
);
+ this.pinService = new PinService(
+ this.accountService,
+ this.encryptService,
+ this.kdfConfigService,
+ this.keyGenerationService,
+ this.logService,
+ this.keyService,
+ this.sdkService,
+ pinStateService,
+ );
+
+ this.biometricsService = new BackgroundBrowserBiometricsService(
+ runtimeNativeMessagingBackground,
+ this.logService,
+ this.keyService,
+ this.biometricStateService,
+ this.messagingService,
+ this.vaultTimeoutSettingsService,
+ this.pinService,
+ );
+
this.passwordStrengthService = new PasswordStrengthService();
this.passwordGenerationService = legacyPasswordGenerationServiceFactory(
From 637f4961bb239b671895c972ea9f6f37c2d1a978 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 24 Nov 2025 10:23:03 +0100
Subject: [PATCH 5/5] [deps] Billing: Update braintree-web-drop-in to v1.46.0
(#14451)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com>
---
package-lock.json | 112 +++++++++++++++++++++-------------------------
package.json | 2 +-
2 files changed, 51 insertions(+), 63 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 005eb38d80b..9ec580e3a5a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -38,7 +38,7 @@
"@nx/js": "21.6.8",
"@nx/webpack": "21.6.8",
"big-integer": "1.6.52",
- "braintree-web-drop-in": "1.44.0",
+ "braintree-web-drop-in": "1.46.0",
"buffer": "6.0.3",
"bufferutil": "4.0.9",
"chalk": "4.1.2",
@@ -4798,15 +4798,15 @@
"link": true
},
"node_modules/@braintree/asset-loader": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@braintree/asset-loader/-/asset-loader-2.0.1.tgz",
- "integrity": "sha512-OGAoBA5MRVsr5qg0sXM6NMJbqHnYZhBudtM6WGgpQnoX42fjUYbE6Y6qFuuerD5z3lsOAjnu80DooBs1VBuh5Q==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@braintree/asset-loader/-/asset-loader-2.0.3.tgz",
+ "integrity": "sha512-uREap1j30wKRlC0mK99nNPMpEp77NtB6XixpDfFJPZHmkrmw7IB4skKe+26LZBK1H6oSainFhAyKoP7x3eyOKA==",
"license": "MIT"
},
"node_modules/@braintree/browser-detection": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@braintree/browser-detection/-/browser-detection-2.0.1.tgz",
- "integrity": "sha512-wpRI7AXEUh6o3ILrJbpNOYE7ItfjX/S8JZP7Z5FF66ULngBGYOqE8SeLlLKXG69Nc07HtlL/6nk/h539iz9hcQ==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@braintree/browser-detection/-/browser-detection-2.0.2.tgz",
+ "integrity": "sha512-Zrv/pyodvwv/hsjsBKXKVcwHZOkx4A/5Cy2hViXtqghAhLd3483bYUIfHZJE5JKTrd018ny1FI5pN1PHFtW7vw==",
"license": "MIT"
},
"node_modules/@braintree/event-emitter": {
@@ -4822,9 +4822,9 @@
"license": "MIT"
},
"node_modules/@braintree/iframer": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@braintree/iframer/-/iframer-2.0.0.tgz",
- "integrity": "sha512-x1kHOyIJNDvi4P1s6pVBZhqhBa1hqDG9+yzcsCR1oNVC0LxH9CAP8bKxioT8/auY1sUyy+D8T4Vp/jv7QqSqLQ==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@braintree/iframer/-/iframer-2.0.1.tgz",
+ "integrity": "sha512-t1zJX5+f1yxHAzBJPaQT/XVMocKodUqjTE+hYvuxxWjqEZIbH8/eT5b5n767jY16mYw3+XiDkKHqcp4Pclq1wg==",
"license": "MIT"
},
"node_modules/@braintree/sanitize-url": {
@@ -4834,9 +4834,9 @@
"license": "MIT"
},
"node_modules/@braintree/uuid": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@braintree/uuid/-/uuid-1.0.0.tgz",
- "integrity": "sha512-AtI5hfttWSuWAgcwLUZdcZ7Fp/8jCCUf9JTs7+Xow9ditU28zuoBovqq083yph2m3SxPYb84lGjOq+cXlXBvJg==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@braintree/uuid/-/uuid-1.0.1.tgz",
+ "integrity": "sha512-Tgu5GoODkf4oj4aLlVIapEPEfjitIHrg5ftqY6pa5Ghr4ZUA9XtZIIZ6ZPdP9x8/X0lt/FB8tRq83QuCQCwOrA==",
"license": "ISC"
},
"node_modules/@braintree/wrap-promise": {
@@ -17762,40 +17762,40 @@
}
},
"node_modules/braintree-web": {
- "version": "3.113.0",
- "resolved": "https://registry.npmjs.org/braintree-web/-/braintree-web-3.113.0.tgz",
- "integrity": "sha512-qykYxZyld4X1tRNgXZQ3ZGzmhDGTBTRQ6Q24KaG9PuYqo+P2TVDEDOVC6tRbkx2RUIdXLv2M6WpkG7oLqEia9Q==",
+ "version": "3.123.2",
+ "resolved": "https://registry.npmjs.org/braintree-web/-/braintree-web-3.123.2.tgz",
+ "integrity": "sha512-N4IH75vKY67eONc0Ao4e7F+XagFW+3ok+Nfs/eOjw5D/TUt03diMAQ8woOwJghi2ql6/yjqNzZi2zE/sTWXmJg==",
"license": "MIT",
"dependencies": {
- "@braintree/asset-loader": "2.0.1",
- "@braintree/browser-detection": "2.0.1",
+ "@braintree/asset-loader": "2.0.3",
+ "@braintree/browser-detection": "2.0.2",
"@braintree/event-emitter": "0.4.1",
"@braintree/extended-promise": "1.0.0",
- "@braintree/iframer": "2.0.0",
+ "@braintree/iframer": "2.0.1",
"@braintree/sanitize-url": "7.0.4",
- "@braintree/uuid": "1.0.0",
+ "@braintree/uuid": "1.0.1",
"@braintree/wrap-promise": "2.1.0",
"@paypal/accelerated-checkout-loader": "1.1.0",
- "card-validator": "10.0.0",
- "credit-card-type": "10.0.1",
- "framebus": "6.0.0",
- "inject-stylesheet": "6.0.1",
+ "card-validator": "10.0.3",
+ "credit-card-type": "10.0.2",
+ "framebus": "6.0.3",
+ "inject-stylesheet": "6.0.2",
"promise-polyfill": "8.2.3",
- "restricted-input": "3.0.5"
+ "restricted-input": "4.0.3"
}
},
"node_modules/braintree-web-drop-in": {
- "version": "1.44.0",
- "resolved": "https://registry.npmjs.org/braintree-web-drop-in/-/braintree-web-drop-in-1.44.0.tgz",
- "integrity": "sha512-maOq9SwiXztIzixJhOras7K44x4UIqqnkyQMYAJqxQ8WkADv9AkflCu2j3IeVYCus/Th9gWWFHcBugn3C4sZGw==",
+ "version": "1.46.0",
+ "resolved": "https://registry.npmjs.org/braintree-web-drop-in/-/braintree-web-drop-in-1.46.0.tgz",
+ "integrity": "sha512-KxCjJpaigoMajYD/iIA+ohXaI6Olt2Bj/Yu45WpJOjolKO9n1UmXl9bsq9UIiGOFIGqi/JWva1wI4cIHHvcI1A==",
"license": "MIT",
"dependencies": {
- "@braintree/asset-loader": "2.0.1",
- "@braintree/browser-detection": "2.0.1",
+ "@braintree/asset-loader": "2.0.3",
+ "@braintree/browser-detection": "2.0.2",
"@braintree/event-emitter": "0.4.1",
- "@braintree/uuid": "1.0.0",
+ "@braintree/uuid": "1.0.1",
"@braintree/wrap-promise": "2.1.0",
- "braintree-web": "3.113.0"
+ "braintree-web": "3.123.2"
}
},
"node_modules/browser-assert": {
@@ -18444,20 +18444,14 @@
"license": "CC-BY-4.0"
},
"node_modules/card-validator": {
- "version": "10.0.0",
- "resolved": "https://registry.npmjs.org/card-validator/-/card-validator-10.0.0.tgz",
- "integrity": "sha512-2fLyCBOxO7/b56sxoYav8FeJqv9bWpZSyKq8sXKxnpxTGXHnM/0c8WEKG+ZJ+OXFcabnl98pD0EKBtTn+Tql0g==",
+ "version": "10.0.3",
+ "resolved": "https://registry.npmjs.org/card-validator/-/card-validator-10.0.3.tgz",
+ "integrity": "sha512-xOEDsK3hojV0OIpmrR64eZGpngnOqRDEP20O+WSRtvjLSW6nyekW4i2N9SzYg679uFO3RyHcFHxb+mml5tXc4A==",
"license": "MIT",
"dependencies": {
- "credit-card-type": "^9.1.0"
+ "credit-card-type": "^10.0.2"
}
},
- "node_modules/card-validator/node_modules/credit-card-type": {
- "version": "9.1.0",
- "resolved": "https://registry.npmjs.org/credit-card-type/-/credit-card-type-9.1.0.tgz",
- "integrity": "sha512-CpNFuLxiPFxuZqhSKml3M+t0K/484pMAnfYWH14JoD7OZMnmC0Lmo+P7JX9SobqFpRoo7ifA18kOHdxJywYPEA==",
- "license": "MIT"
- },
"node_modules/case-sensitive-paths-webpack-plugin": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz",
@@ -19692,9 +19686,9 @@
"license": "MIT"
},
"node_modules/credit-card-type": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/credit-card-type/-/credit-card-type-10.0.1.tgz",
- "integrity": "sha512-vQOuWmBgsgG1ovGeDi8m6Zeu1JaqH/JncrxKmaqMbv/LunyOQdLiQhPHtOsNlbUI05TocR5nod/Mbs3HYtr6sQ==",
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/credit-card-type/-/credit-card-type-10.0.2.tgz",
+ "integrity": "sha512-vt/iQokU0mtrT7ceRU75FSmWnIh5JFpLsUUUWYRmztYekOGm0ZbCuzwFTbNkq41k92y+0B8ChscFhRN9DhVZEA==",
"license": "MIT"
},
"node_modules/cross-dirname": {
@@ -23410,20 +23404,14 @@
}
},
"node_modules/framebus": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/framebus/-/framebus-6.0.0.tgz",
- "integrity": "sha512-bL9V68hVaVBCY9rveoWbPFFI9hAXIJtESs51B+9XmzvMt38+wP8b4VdiJsavjMS6NfPZ/afQ/jc2qaHmSGI1kQ==",
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/framebus/-/framebus-6.0.3.tgz",
+ "integrity": "sha512-G/N2p+kFZ1xPBge7tbtTq2KcTR1kSKs1rVbTqH//WdtvJSexS33fsTTOq3yfUWvUczqhujyaFc+omawC9YyRBg==",
"license": "MIT",
"dependencies": {
- "@braintree/uuid": "^0.1.0"
+ "@braintree/uuid": "^1.0.0"
}
},
- "node_modules/framebus/node_modules/@braintree/uuid": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/@braintree/uuid/-/uuid-0.1.0.tgz",
- "integrity": "sha512-YvZJdlNcK5EnR+7M8AjgEAf4Qx696+FOSYlPfy5ePn80vODtVAUU0FxHnzKZC0og1VbDNQDDiwhthR65D4Na0g==",
- "license": "ISC"
- },
"node_modules/fresh": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
@@ -24987,9 +24975,9 @@
}
},
"node_modules/inject-stylesheet": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/inject-stylesheet/-/inject-stylesheet-6.0.1.tgz",
- "integrity": "sha512-2fvune1D4+8mvJoLVo95ncY4HrDkIaYIReRzXv8tkWFgdG9iuc5QuX57gtSDPWTWQI/f5BGwwtH85wxHouzucg==",
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/inject-stylesheet/-/inject-stylesheet-6.0.2.tgz",
+ "integrity": "sha512-sswMueya1LXEfwcy7KXPuq3zAW6HvgAeViApEhIaCviCkP4XYoKrQj8ftEmxPmIHn88X4R3xOAsnN/QCPvVKWw==",
"license": "MIT"
},
"node_modules/inquirer": {
@@ -36154,12 +36142,12 @@
"license": "ISC"
},
"node_modules/restricted-input": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/restricted-input/-/restricted-input-3.0.5.tgz",
- "integrity": "sha512-lUuXZ3wUnHURRarj5/0C8vomWIfWJO+p7T6RYwB46v7Oyuyr3yyupU+i7SjqUv4S6RAeAAZt1C/QCLJ9xhQBow==",
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/restricted-input/-/restricted-input-4.0.3.tgz",
+ "integrity": "sha512-VpkwT5Fr3DhvoRZfPnmHDhnYAYETjjNzDlvA4NlW0iknFS47C5X4OCHEpOOxaPjvmka5V8d1ty1jVVoorZKvHg==",
"license": "MIT",
"dependencies": {
- "@braintree/browser-detection": "^1.12.1"
+ "@braintree/browser-detection": "^1.17.2"
}
},
"node_modules/restricted-input/node_modules/@braintree/browser-detection": {
diff --git a/package.json b/package.json
index 7ca866b3c4d..87a78a30796 100644
--- a/package.json
+++ b/package.json
@@ -175,7 +175,7 @@
"@nx/js": "21.6.8",
"@nx/webpack": "21.6.8",
"big-integer": "1.6.52",
- "braintree-web-drop-in": "1.44.0",
+ "braintree-web-drop-in": "1.46.0",
"buffer": "6.0.3",
"bufferutil": "4.0.9",
"chalk": "4.1.2",