mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 07:43:35 +00:00
Browser integration verification always re-prompts after desktop app is locked (#14370)
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { NgZone } from "@angular/core";
|
import { NgZone } from "@angular/core";
|
||||||
import { mock, MockProxy } from "jest-mock-extended";
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
import { of } from "rxjs";
|
import { BehaviorSubject, filter, firstValueFrom, of, take, timeout, timer } from "rxjs";
|
||||||
|
|
||||||
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||||
@@ -73,10 +73,13 @@ describe("BiometricMessageHandlerService", () => {
|
|||||||
ngZone = mock<NgZone>();
|
ngZone = mock<NgZone>();
|
||||||
i18nService = mock<I18nMockService>();
|
i18nService = mock<I18nMockService>();
|
||||||
|
|
||||||
|
desktopSettingsService.browserIntegrationEnabled$ = of(false);
|
||||||
|
desktopSettingsService.browserIntegrationFingerprintEnabled$ = of(false);
|
||||||
|
|
||||||
(global as any).ipc = {
|
(global as any).ipc = {
|
||||||
platform: {
|
platform: {
|
||||||
ephemeralStore: {
|
ephemeralStore: {
|
||||||
listEphemeralValueKeys: jest.fn(),
|
listEphemeralValueKeys: jest.fn(() => Promise.resolve([])),
|
||||||
getEphemeralValue: jest.fn(),
|
getEphemeralValue: jest.fn(),
|
||||||
removeEphemeralValue: jest.fn(),
|
removeEphemeralValue: jest.fn(),
|
||||||
setEphemeralValue: jest.fn(),
|
setEphemeralValue: jest.fn(),
|
||||||
@@ -107,6 +110,129 @@ describe("BiometricMessageHandlerService", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("constructor", () => {
|
||||||
|
let browserIntegrationEnabled = new BehaviorSubject<boolean>(true);
|
||||||
|
let browserIntegrationFingerprintEnabled = new BehaviorSubject<boolean>(true);
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
(global as any).ipc = {
|
||||||
|
platform: {
|
||||||
|
ephemeralStore: {
|
||||||
|
listEphemeralValueKeys: jest.fn(() =>
|
||||||
|
Promise.resolve(["connectedApp_appId1", "connectedApp_appId2"]),
|
||||||
|
),
|
||||||
|
getEphemeralValue: jest.fn((key) => {
|
||||||
|
if (key === "connectedApp_appId1") {
|
||||||
|
return Promise.resolve(
|
||||||
|
JSON.stringify({
|
||||||
|
publicKey: Utils.fromUtf8ToB64("publicKeyApp1"),
|
||||||
|
sessionSecret: Utils.fromUtf8ToB64("sessionSecretApp1"),
|
||||||
|
trusted: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else if (key === "connectedApp_appId2") {
|
||||||
|
return Promise.resolve(
|
||||||
|
JSON.stringify({
|
||||||
|
publicKey: Utils.fromUtf8ToB64("publicKeyApp2"),
|
||||||
|
sessionSecret: Utils.fromUtf8ToB64("sessionSecretApp2"),
|
||||||
|
trusted: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}),
|
||||||
|
removeEphemeralValue: jest.fn(),
|
||||||
|
setEphemeralValue: jest.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
desktopSettingsService.browserIntegrationEnabled$ = browserIntegrationEnabled.asObservable();
|
||||||
|
desktopSettingsService.browserIntegrationFingerprintEnabled$ =
|
||||||
|
browserIntegrationFingerprintEnabled.asObservable();
|
||||||
|
|
||||||
|
service = new BiometricMessageHandlerService(
|
||||||
|
cryptoFunctionService,
|
||||||
|
keyService,
|
||||||
|
encryptService,
|
||||||
|
logService,
|
||||||
|
messagingService,
|
||||||
|
desktopSettingsService,
|
||||||
|
biometricStateService,
|
||||||
|
biometricsService,
|
||||||
|
dialogService,
|
||||||
|
accountService,
|
||||||
|
authService,
|
||||||
|
ngZone,
|
||||||
|
i18nService,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
browserIntegrationEnabled = new BehaviorSubject<boolean>(true);
|
||||||
|
browserIntegrationFingerprintEnabled = new BehaviorSubject<boolean>(true);
|
||||||
|
|
||||||
|
desktopSettingsService.browserIntegrationEnabled$ = browserIntegrationEnabled.asObservable();
|
||||||
|
desktopSettingsService.browserIntegrationFingerprintEnabled$ =
|
||||||
|
browserIntegrationFingerprintEnabled.asObservable();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should clear connected apps when browser integration disabled", async () => {
|
||||||
|
browserIntegrationEnabled.next(false);
|
||||||
|
|
||||||
|
await firstValueFrom(
|
||||||
|
timer(0, 100).pipe(
|
||||||
|
filter(
|
||||||
|
() =>
|
||||||
|
(global as any).ipc.platform.ephemeralStore.removeEphemeralValue.mock.calls.length ==
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
take(1),
|
||||||
|
timeout(1000),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect((global as any).ipc.platform.ephemeralStore.removeEphemeralValue).toHaveBeenCalledWith(
|
||||||
|
"connectedApp_appId1",
|
||||||
|
);
|
||||||
|
expect((global as any).ipc.platform.ephemeralStore.removeEphemeralValue).toHaveBeenCalledWith(
|
||||||
|
"connectedApp_appId2",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should un-trust connected apps when browser integration verification fingerprint disabled", async () => {
|
||||||
|
browserIntegrationFingerprintEnabled.next(false);
|
||||||
|
|
||||||
|
await firstValueFrom(
|
||||||
|
timer(0, 100).pipe(
|
||||||
|
filter(
|
||||||
|
() =>
|
||||||
|
(global as any).ipc.platform.ephemeralStore.setEphemeralValue.mock.calls.length == 2,
|
||||||
|
),
|
||||||
|
take(1),
|
||||||
|
timeout(1000),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect((global as any).ipc.platform.ephemeralStore.setEphemeralValue).toHaveBeenCalledWith(
|
||||||
|
"connectedApp_appId1",
|
||||||
|
JSON.stringify({
|
||||||
|
publicKey: Utils.fromUtf8ToB64("publicKeyApp1"),
|
||||||
|
sessionSecret: Utils.fromUtf8ToB64("sessionSecretApp1"),
|
||||||
|
trusted: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect((global as any).ipc.platform.ephemeralStore.setEphemeralValue).toHaveBeenCalledWith(
|
||||||
|
"connectedApp_appId2",
|
||||||
|
JSON.stringify({
|
||||||
|
publicKey: Utils.fromUtf8ToB64("publicKeyApp2"),
|
||||||
|
sessionSecret: Utils.fromUtf8ToB64("sessionSecretApp2"),
|
||||||
|
trusted: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("setup encryption", () => {
|
describe("setup encryption", () => {
|
||||||
it("should ignore when public key missing in message", async () => {
|
it("should ignore when public key missing in message", async () => {
|
||||||
await service.handleMessage({
|
await service.handleMessage({
|
||||||
|
|||||||
@@ -91,12 +91,27 @@ export class BiometricMessageHandlerService {
|
|||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
) {
|
) {
|
||||||
combineLatest([
|
combineLatest([
|
||||||
this.desktopSettingService.browserIntegrationFingerprintEnabled$,
|
|
||||||
this.desktopSettingService.browserIntegrationEnabled$,
|
this.desktopSettingService.browserIntegrationEnabled$,
|
||||||
|
this.desktopSettingService.browserIntegrationFingerprintEnabled$,
|
||||||
])
|
])
|
||||||
.pipe(
|
.pipe(
|
||||||
concatMap(async () => {
|
concatMap(async ([browserIntegrationEnabled, browserIntegrationFingerprintEnabled]) => {
|
||||||
await this.connectedApps.clear();
|
if (!browserIntegrationEnabled) {
|
||||||
|
this.logService.info("[Native Messaging IPC] Clearing connected apps");
|
||||||
|
await this.connectedApps.clear();
|
||||||
|
} else if (!browserIntegrationFingerprintEnabled) {
|
||||||
|
this.logService.info(
|
||||||
|
"[Native Messaging IPC] Browser integration fingerprint validation is disabled, untrusting all connected apps",
|
||||||
|
);
|
||||||
|
const connected = await this.connectedApps.list();
|
||||||
|
for (const appId of connected) {
|
||||||
|
const connectedApp = await this.connectedApps.get(appId);
|
||||||
|
if (connectedApp != null) {
|
||||||
|
connectedApp.trusted = false;
|
||||||
|
await this.connectedApps.set(appId, connectedApp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
|
|||||||
Reference in New Issue
Block a user