mirror of
https://github.com/bitwarden/browser
synced 2026-02-14 07:23:45 +00:00
[PM-4903] - If you back out of autofill flow from locked vault screen, credentials autofilled on normal unlock (#17283)
* PM-4903- added a check for auth status and popout tabs, if no popup tab and auth is locked, abandon autofill * add test * clear all notifications if unlock popout closed * add more tests and use tabid for performance optimization
This commit is contained in:
@@ -1530,5 +1530,63 @@ describe("NotificationBackground", () => {
|
||||
expect(environmentServiceSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("handleUnlockPopoutClosed", () => {
|
||||
let onRemovedListeners: Array<(tabId: number, removeInfo: chrome.tabs.OnRemovedInfo) => void>;
|
||||
let tabsQuerySpy: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
onRemovedListeners = [];
|
||||
chrome.tabs.onRemoved.addListener = jest.fn((listener) => {
|
||||
onRemovedListeners.push(listener);
|
||||
});
|
||||
chrome.runtime.getURL = jest.fn().mockReturnValue("chrome-extension://id/popup/index.html");
|
||||
notificationBackground.init();
|
||||
});
|
||||
|
||||
const triggerTabRemoved = async (tabId: number) => {
|
||||
onRemovedListeners[0](tabId, mock<chrome.tabs.OnRemovedInfo>());
|
||||
await flushPromises();
|
||||
};
|
||||
|
||||
it("sends abandon message when unlock popout is closed and vault is locked", async () => {
|
||||
activeAccountStatusMock$.next(AuthenticationStatus.Locked);
|
||||
tabsQuerySpy = jest.spyOn(BrowserApi, "tabsQuery").mockResolvedValue([]);
|
||||
|
||||
await triggerTabRemoved(1);
|
||||
|
||||
expect(tabsQuerySpy).toHaveBeenCalled();
|
||||
expect(messagingService.send).toHaveBeenCalledWith("abandonAutofillPendingNotifications");
|
||||
});
|
||||
|
||||
it("uses tracked tabId for fast lookup when available", async () => {
|
||||
activeAccountStatusMock$.next(AuthenticationStatus.Locked);
|
||||
tabsQuerySpy = jest.spyOn(BrowserApi, "tabsQuery").mockResolvedValue([
|
||||
{
|
||||
id: 123,
|
||||
url: "chrome-extension://id/popup/index.html?singleActionPopout=auth_unlockExtension",
|
||||
} as chrome.tabs.Tab,
|
||||
]);
|
||||
|
||||
await triggerTabRemoved(999);
|
||||
tabsQuerySpy.mockClear();
|
||||
messagingService.send.mockClear();
|
||||
|
||||
await triggerTabRemoved(123);
|
||||
|
||||
expect(tabsQuerySpy).not.toHaveBeenCalled();
|
||||
expect(messagingService.send).toHaveBeenCalledWith("abandonAutofillPendingNotifications");
|
||||
});
|
||||
|
||||
it("returns early when vault is unlocked", async () => {
|
||||
activeAccountStatusMock$.next(AuthenticationStatus.Unlocked);
|
||||
tabsQuerySpy = jest.spyOn(BrowserApi, "tabsQuery").mockResolvedValue([]);
|
||||
|
||||
await triggerTabRemoved(1);
|
||||
|
||||
expect(tabsQuerySpy).not.toHaveBeenCalled();
|
||||
expect(messagingService.send).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -45,7 +45,7 @@ import { SecurityTask } from "@bitwarden/common/vault/tasks/models/security-task
|
||||
|
||||
// FIXME (PM-22628): Popup imports are forbidden in background
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { openUnlockPopout } from "../../auth/popup/utils/auth-popout-window";
|
||||
import { AuthPopoutType, openUnlockPopout } from "../../auth/popup/utils/auth-popout-window";
|
||||
import { BrowserApi } from "../../platform/browser/browser-api";
|
||||
// FIXME (PM-22628): Popup imports are forbidden in background
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
@@ -89,6 +89,7 @@ export default class NotificationBackground {
|
||||
ExtensionCommand.AutofillCard,
|
||||
ExtensionCommand.AutofillIdentity,
|
||||
]);
|
||||
private unlockPopoutTabId?: number;
|
||||
private readonly extensionMessageHandlers: NotificationBackgroundExtensionMessageHandlers = {
|
||||
bgAdjustNotificationBar: ({ message, sender }) =>
|
||||
this.handleAdjustNotificationBarMessage(message, sender),
|
||||
@@ -146,6 +147,7 @@ export default class NotificationBackground {
|
||||
}
|
||||
|
||||
this.setupExtensionMessageListener();
|
||||
this.setupUnlockPopoutCloseListener();
|
||||
|
||||
this.cleanupNotificationQueue();
|
||||
}
|
||||
@@ -1163,6 +1165,7 @@ export default class NotificationBackground {
|
||||
message: NotificationBackgroundExtensionMessage,
|
||||
sender: chrome.runtime.MessageSender,
|
||||
): Promise<void> {
|
||||
this.unlockPopoutTabId = undefined;
|
||||
const messageData = message.data as LockedVaultPendingNotificationsData;
|
||||
const retryCommand = messageData.commandToRetry.message.command as ExtensionCommandType;
|
||||
if (this.allowedRetryCommands.has(retryCommand)) {
|
||||
@@ -1313,4 +1316,43 @@ export default class NotificationBackground {
|
||||
const tabDomain = Utils.getDomain(tab.url);
|
||||
return tabDomain === queueMessage.domain || tabDomain === Utils.getDomain(queueMessage.tab.url);
|
||||
}
|
||||
|
||||
private setupUnlockPopoutCloseListener() {
|
||||
chrome.tabs.onRemoved.addListener(async (tabId: number) => {
|
||||
await this.handleUnlockPopoutClosed(tabId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* If the unlock popout is closed while the vault
|
||||
* is still locked and there are pending autofill notifications, abandon them.
|
||||
*/
|
||||
private async handleUnlockPopoutClosed(removedTabId: number) {
|
||||
const authStatus = await this.getAuthStatus();
|
||||
if (authStatus >= AuthenticationStatus.Unlocked) {
|
||||
this.unlockPopoutTabId = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.unlockPopoutTabId === removedTabId) {
|
||||
this.unlockPopoutTabId = undefined;
|
||||
this.messagingService.send("abandonAutofillPendingNotifications");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.unlockPopoutTabId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const extensionUrl = chrome.runtime.getURL("popup/index.html");
|
||||
const unlockPopoutTabs = (await BrowserApi.tabsQuery({ url: `${extensionUrl}*` })).filter(
|
||||
(tab) => tab.url?.includes(`singleActionPopout=${AuthPopoutType.unlockExtension}`),
|
||||
);
|
||||
|
||||
if (unlockPopoutTabs.length === 0) {
|
||||
this.messagingService.send("abandonAutofillPendingNotifications");
|
||||
} else if (unlockPopoutTabs[0].id) {
|
||||
this.unlockPopoutTabId = unlockPopoutTabs[0].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,6 +256,9 @@ export default class RuntimeBackground {
|
||||
case "addToLockedVaultPendingNotifications":
|
||||
this.lockedVaultPendingNotifications.push(msg.data);
|
||||
break;
|
||||
case "abandonAutofillPendingNotifications":
|
||||
this.lockedVaultPendingNotifications = [];
|
||||
break;
|
||||
case "lockVault":
|
||||
await this.lockService.lock(msg.userId);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user