diff --git a/apps/desktop/src/key-management/biometrics/biometrics.service.spec.ts b/apps/desktop/src/key-management/biometrics/biometrics.service.spec.ts deleted file mode 100644 index 9e5755dd579..00000000000 --- a/apps/desktop/src/key-management/biometrics/biometrics.service.spec.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { mock, MockProxy } from "jest-mock-extended"; - -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { UserId } from "@bitwarden/common/types/guid"; -import { - BiometricsService, - BiometricsStatus, - BiometricStateService, -} from "@bitwarden/key-management"; - -import { WindowMain } from "../../main/window.main"; - -import { MainBiometricsService } from "./main-biometrics.service"; -import OsBiometricsServiceLinux from "./os-biometrics-linux.service"; -import OsBiometricsServiceMac from "./os-biometrics-mac.service"; -import OsBiometricsServiceWindows from "./os-biometrics-windows.service"; -import { OsBiometricService } from "./os-biometrics.service"; - -jest.mock("@bitwarden/desktop-napi", () => { - return { - biometrics: jest.fn(), - passwords: jest.fn(), - }; -}); - -describe("biometrics tests", function () { - const i18nService = mock(); - const windowMain = mock(); - const logService = mock(); - const messagingService = mock(); - const biometricStateService = mock(); - - it("Should call the platformspecific methods", async () => { - const sut = new MainBiometricsService( - i18nService, - windowMain, - logService, - messagingService, - process.platform, - biometricStateService, - ); - - const mockService = mock(); - (sut as any).osBiometricsService = mockService; - - await sut.authenticateBiometric(); - expect(mockService.authenticateBiometric).toBeCalled(); - }); - - describe("Should create a platform specific service", function () { - it("Should create a biometrics service specific for Windows", () => { - const sut = new MainBiometricsService( - i18nService, - windowMain, - logService, - messagingService, - "win32", - biometricStateService, - ); - - const internalService = (sut as any).osBiometricsService; - expect(internalService).not.toBeNull(); - expect(internalService).toBeInstanceOf(OsBiometricsServiceWindows); - }); - - it("Should create a biometrics service specific for MacOs", () => { - const sut = new MainBiometricsService( - i18nService, - windowMain, - logService, - messagingService, - "darwin", - biometricStateService, - ); - const internalService = (sut as any).osBiometricsService; - expect(internalService).not.toBeNull(); - expect(internalService).toBeInstanceOf(OsBiometricsServiceMac); - }); - - it("Should create a biometrics service specific for Linux", () => { - const sut = new MainBiometricsService( - i18nService, - windowMain, - logService, - messagingService, - "linux", - biometricStateService, - ); - - const internalService = (sut as any).osBiometricsService; - expect(internalService).not.toBeNull(); - expect(internalService).toBeInstanceOf(OsBiometricsServiceLinux); - }); - }); - - describe("can auth biometric", () => { - let sut: BiometricsService; - let innerService: MockProxy; - - beforeEach(() => { - sut = new MainBiometricsService( - i18nService, - windowMain, - logService, - messagingService, - process.platform, - biometricStateService, - ); - - innerService = mock(); - (sut as any).osBiometricsService = innerService; - }); - - it("should return the correct biometric status for system status", async () => { - const testCases = [ - // happy path - [true, false, false, BiometricsStatus.Available], - [false, true, true, BiometricsStatus.HardwareUnavailable], - [true, true, true, BiometricsStatus.AutoSetupNeeded], - [true, true, false, BiometricsStatus.ManualSetupNeeded], - - // should not happen - [false, false, true, BiometricsStatus.HardwareUnavailable], - [true, false, true, BiometricsStatus.Available], - [false, true, false, BiometricsStatus.HardwareUnavailable], - [false, false, false, BiometricsStatus.HardwareUnavailable], - ]; - - for (const [supportsBiometric, needsSetup, canAutoSetup, expected] of testCases) { - innerService.osSupportsBiometric.mockResolvedValue(supportsBiometric as boolean); - innerService.osBiometricsNeedsSetup.mockResolvedValue(needsSetup as boolean); - innerService.osBiometricsCanAutoSetup.mockResolvedValue(canAutoSetup as boolean); - - const actual = await sut.getBiometricsStatus(); - expect(actual).toBe(expected); - } - }); - - it("should return the correct biometric status for user status", async () => { - const testCases = [ - // system status, biometric unlock enabled, require password on start, has key half, result - [BiometricsStatus.Available, false, false, false, BiometricsStatus.NotEnabledLocally], - [BiometricsStatus.Available, false, true, false, BiometricsStatus.NotEnabledLocally], - [BiometricsStatus.Available, false, false, true, BiometricsStatus.NotEnabledLocally], - [BiometricsStatus.Available, false, true, true, BiometricsStatus.NotEnabledLocally], - - [ - BiometricsStatus.PlatformUnsupported, - true, - true, - true, - BiometricsStatus.PlatformUnsupported, - ], - [BiometricsStatus.ManualSetupNeeded, true, true, true, BiometricsStatus.ManualSetupNeeded], - [BiometricsStatus.AutoSetupNeeded, true, true, true, BiometricsStatus.AutoSetupNeeded], - - [BiometricsStatus.Available, true, false, true, BiometricsStatus.Available], - [BiometricsStatus.Available, true, true, false, BiometricsStatus.UnlockNeeded], - [BiometricsStatus.Available, true, false, true, BiometricsStatus.Available], - ]; - - for (const [ - systemStatus, - unlockEnabled, - requirePasswordOnStart, - hasKeyHalf, - expected, - ] of testCases) { - sut.getBiometricsStatus = jest.fn().mockResolvedValue(systemStatus as BiometricsStatus); - biometricStateService.getBiometricUnlockEnabled.mockResolvedValue(unlockEnabled as boolean); - biometricStateService.getRequirePasswordOnStart.mockResolvedValue( - requirePasswordOnStart as boolean, - ); - (sut as any).clientKeyHalves = new Map(); - const userId = "test" as UserId; - if (hasKeyHalf) { - (sut as any).clientKeyHalves.set(userId, "test"); - } - - const actual = await sut.getBiometricsStatusForUser(userId); - expect(actual).toBe(expected); - } - }); - }); -}); diff --git a/apps/desktop/src/key-management/biometrics/desktop.biometrics.service.ts b/apps/desktop/src/key-management/biometrics/desktop.biometrics.service.ts index 0c0efea78f9..6415443bfbc 100644 --- a/apps/desktop/src/key-management/biometrics/desktop.biometrics.service.ts +++ b/apps/desktop/src/key-management/biometrics/desktop.biometrics.service.ts @@ -11,5 +11,5 @@ export abstract class DesktopBiometricsService extends BiometricsService { abstract setupBiometrics(): Promise; - abstract setClientKeyHalfForUser(userId: UserId, value: string): Promise; + abstract setClientKeyHalfForUser(userId: UserId, value: string | null): Promise; } diff --git a/apps/desktop/src/key-management/biometrics/main-biometrics-ipc.listener.ts b/apps/desktop/src/key-management/biometrics/main-biometrics-ipc.listener.ts index 1605f650e12..fe40aad54d9 100644 --- a/apps/desktop/src/key-management/biometrics/main-biometrics-ipc.listener.ts +++ b/apps/desktop/src/key-management/biometrics/main-biometrics-ipc.listener.ts @@ -44,9 +44,6 @@ export class MainBiometricsIPCListener { message.userId as UserId, ); case BiometricAction.SetClientKeyHalf: - if (message.key == null) { - return; - } return await this.biometricService.setClientKeyHalfForUser( message.userId as UserId, message.key, diff --git a/apps/desktop/src/key-management/biometrics/main-biometrics.service.spec.ts b/apps/desktop/src/key-management/biometrics/main-biometrics.service.spec.ts new file mode 100644 index 00000000000..88dd8c60ed5 --- /dev/null +++ b/apps/desktop/src/key-management/biometrics/main-biometrics.service.spec.ts @@ -0,0 +1,426 @@ +import { mock, MockProxy } from "jest-mock-extended"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { EncryptionType } from "@bitwarden/common/platform/enums"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { UserId } from "@bitwarden/common/types/guid"; +import { + BiometricsService, + BiometricsStatus, + BiometricStateService, +} from "@bitwarden/key-management"; + +import { WindowMain } from "../../main/window.main"; + +import { MainBiometricsService } from "./main-biometrics.service"; +import OsBiometricsServiceLinux from "./os-biometrics-linux.service"; +import OsBiometricsServiceMac from "./os-biometrics-mac.service"; +import OsBiometricsServiceWindows from "./os-biometrics-windows.service"; +import { OsBiometricService } from "./os-biometrics.service"; + +jest.mock("@bitwarden/desktop-napi", () => { + return { + biometrics: jest.fn(), + passwords: jest.fn(), + }; +}); + +describe("MainBiometricsService", function () { + const i18nService = mock(); + const windowMain = mock(); + const logService = mock(); + const messagingService = mock(); + const biometricStateService = mock(); + + it("Should call the platformspecific methods", async () => { + const sut = new MainBiometricsService( + i18nService, + windowMain, + logService, + messagingService, + process.platform, + biometricStateService, + ); + + const mockService = mock(); + (sut as any).osBiometricsService = mockService; + + await sut.authenticateBiometric(); + expect(mockService.authenticateBiometric).toBeCalled(); + }); + + describe("Should create a platform specific service", function () { + it("Should create a biometrics service specific for Windows", () => { + const sut = new MainBiometricsService( + i18nService, + windowMain, + logService, + messagingService, + "win32", + biometricStateService, + ); + + const internalService = (sut as any).osBiometricsService; + expect(internalService).not.toBeNull(); + expect(internalService).toBeInstanceOf(OsBiometricsServiceWindows); + }); + + it("Should create a biometrics service specific for MacOs", () => { + const sut = new MainBiometricsService( + i18nService, + windowMain, + logService, + messagingService, + "darwin", + biometricStateService, + ); + const internalService = (sut as any).osBiometricsService; + expect(internalService).not.toBeNull(); + expect(internalService).toBeInstanceOf(OsBiometricsServiceMac); + }); + + it("Should create a biometrics service specific for Linux", () => { + const sut = new MainBiometricsService( + i18nService, + windowMain, + logService, + messagingService, + "linux", + biometricStateService, + ); + + const internalService = (sut as any).osBiometricsService; + expect(internalService).not.toBeNull(); + expect(internalService).toBeInstanceOf(OsBiometricsServiceLinux); + }); + }); + + describe("can auth biometric", () => { + let sut: BiometricsService; + let innerService: MockProxy; + + beforeEach(() => { + sut = new MainBiometricsService( + i18nService, + windowMain, + logService, + messagingService, + process.platform, + biometricStateService, + ); + + innerService = mock(); + (sut as any).osBiometricsService = innerService; + }); + + it("should return the correct biometric status for system status", async () => { + const testCases = [ + // happy path + [true, false, false, BiometricsStatus.Available], + [false, true, true, BiometricsStatus.HardwareUnavailable], + [true, true, true, BiometricsStatus.AutoSetupNeeded], + [true, true, false, BiometricsStatus.ManualSetupNeeded], + + // should not happen + [false, false, true, BiometricsStatus.HardwareUnavailable], + [true, false, true, BiometricsStatus.Available], + [false, true, false, BiometricsStatus.HardwareUnavailable], + [false, false, false, BiometricsStatus.HardwareUnavailable], + ]; + + for (const [supportsBiometric, needsSetup, canAutoSetup, expected] of testCases) { + innerService.osSupportsBiometric.mockResolvedValue(supportsBiometric as boolean); + innerService.osBiometricsNeedsSetup.mockResolvedValue(needsSetup as boolean); + innerService.osBiometricsCanAutoSetup.mockResolvedValue(canAutoSetup as boolean); + + const actual = await sut.getBiometricsStatus(); + expect(actual).toBe(expected); + } + }); + + it("should return the correct biometric status for user status", async () => { + const testCases = [ + // system status, biometric unlock enabled, require password on start, has key half, result + [BiometricsStatus.Available, false, false, false, BiometricsStatus.NotEnabledLocally], + [BiometricsStatus.Available, false, true, false, BiometricsStatus.NotEnabledLocally], + [BiometricsStatus.Available, false, false, true, BiometricsStatus.NotEnabledLocally], + [BiometricsStatus.Available, false, true, true, BiometricsStatus.NotEnabledLocally], + + [ + BiometricsStatus.PlatformUnsupported, + true, + true, + true, + BiometricsStatus.PlatformUnsupported, + ], + [BiometricsStatus.ManualSetupNeeded, true, true, true, BiometricsStatus.ManualSetupNeeded], + [BiometricsStatus.AutoSetupNeeded, true, true, true, BiometricsStatus.AutoSetupNeeded], + + [BiometricsStatus.Available, true, false, true, BiometricsStatus.Available], + [BiometricsStatus.Available, true, true, false, BiometricsStatus.UnlockNeeded], + [BiometricsStatus.Available, true, false, true, BiometricsStatus.Available], + ]; + + for (const [ + systemStatus, + unlockEnabled, + requirePasswordOnStart, + hasKeyHalf, + expected, + ] of testCases) { + sut.getBiometricsStatus = jest.fn().mockResolvedValue(systemStatus as BiometricsStatus); + biometricStateService.getBiometricUnlockEnabled.mockResolvedValue(unlockEnabled as boolean); + biometricStateService.getRequirePasswordOnStart.mockResolvedValue( + requirePasswordOnStart as boolean, + ); + (sut as any).clientKeyHalves = new Map(); + const userId = "test" as UserId; + if (hasKeyHalf) { + (sut as any).clientKeyHalves.set(userId, "test"); + } + + const actual = await sut.getBiometricsStatusForUser(userId); + expect(actual).toBe(expected); + } + }); + }); + + describe("setupBiometrics", () => { + it("should call the platform specific setup method", async () => { + const sut = new MainBiometricsService( + i18nService, + windowMain, + logService, + messagingService, + process.platform, + biometricStateService, + ); + const osBiometricsService = mock(); + (sut as any).osBiometricsService = osBiometricsService; + + await sut.setupBiometrics(); + + expect(osBiometricsService.osBiometricsSetup).toHaveBeenCalled(); + }); + }); + + describe("setClientKeyHalfForUser", () => { + let sut: MainBiometricsService; + + beforeEach(() => { + sut = new MainBiometricsService( + i18nService, + windowMain, + logService, + messagingService, + process.platform, + biometricStateService, + ); + }); + + it("should set the client key half for the user", async () => { + const userId = "test" as UserId; + const keyHalf = "testKeyHalf"; + + await sut.setClientKeyHalfForUser(userId, keyHalf); + + expect((sut as any).clientKeyHalves.has(userId)).toBe(true); + expect((sut as any).clientKeyHalves.get(userId)).toBe(keyHalf); + }); + + it("should reset the client key half for the user", async () => { + const userId = "test" as UserId; + + await sut.setClientKeyHalfForUser(userId, null); + + expect((sut as any).clientKeyHalves.has(userId)).toBe(true); + expect((sut as any).clientKeyHalves.get(userId)).toBe(null); + }); + }); + + describe("authenticateWithBiometrics", () => { + it("should call the platform specific authenticate method", async () => { + const sut = new MainBiometricsService( + i18nService, + windowMain, + logService, + messagingService, + process.platform, + biometricStateService, + ); + const osBiometricsService = mock(); + (sut as any).osBiometricsService = osBiometricsService; + + await sut.authenticateWithBiometrics(); + + expect(osBiometricsService.authenticateBiometric).toHaveBeenCalled(); + }); + }); + + describe("unlockWithBiometricsForUser", () => { + let sut: MainBiometricsService; + let osBiometricsService: MockProxy; + + beforeEach(() => { + sut = new MainBiometricsService( + i18nService, + windowMain, + logService, + messagingService, + process.platform, + biometricStateService, + ); + osBiometricsService = mock(); + (sut as any).osBiometricsService = osBiometricsService; + }); + + it("should return null if no biometric key is returned ", async () => { + const userId = "test" as UserId; + (sut as any).clientKeyHalves.set(userId, "testKeyHalf"); + + const userKey = await sut.unlockWithBiometricsForUser(userId); + + expect(userKey).toBeNull(); + expect(osBiometricsService.getBiometricKey).toHaveBeenCalledWith( + "Bitwarden_biometric", + `${userId}_user_biometric`, + "testKeyHalf", + ); + }); + + it("should return the biometric key if a valid key is returned", async () => { + const userId = "test" as UserId; + (sut as any).clientKeyHalves.set(userId, "testKeyHalf"); + const biometricKey = Utils.fromBufferToB64(new Uint8Array(64)); + osBiometricsService.getBiometricKey.mockResolvedValue(biometricKey); + + const userKey = await sut.unlockWithBiometricsForUser(userId); + + expect(userKey).not.toBeNull(); + expect(userKey!.keyB64).toBe(biometricKey); + expect(userKey!.encType).toBe(EncryptionType.AesCbc256_HmacSha256_B64); + expect(osBiometricsService.getBiometricKey).toHaveBeenCalledWith( + "Bitwarden_biometric", + `${userId}_user_biometric`, + "testKeyHalf", + ); + }); + }); + + describe("setBiometricProtectedUnlockKeyForUser", () => { + let sut: MainBiometricsService; + let osBiometricsService: MockProxy; + + beforeEach(() => { + sut = new MainBiometricsService( + i18nService, + windowMain, + logService, + messagingService, + process.platform, + biometricStateService, + ); + osBiometricsService = mock(); + (sut as any).osBiometricsService = osBiometricsService; + }); + + it("should throw an error if no client key half is provided", async () => { + const userId = "test" as UserId; + const unlockKey = "testUnlockKey"; + + await expect(sut.setBiometricProtectedUnlockKeyForUser(userId, unlockKey)).rejects.toThrow( + "No client key half provided for user", + ); + }); + + it("should call the platform specific setBiometricKey method", async () => { + const userId = "test" as UserId; + const unlockKey = "testUnlockKey"; + + (sut as any).clientKeyHalves.set(userId, "testKeyHalf"); + + await sut.setBiometricProtectedUnlockKeyForUser(userId, unlockKey); + + expect(osBiometricsService.setBiometricKey).toHaveBeenCalledWith( + "Bitwarden_biometric", + `${userId}_user_biometric`, + unlockKey, + "testKeyHalf", + ); + }); + }); + + describe("deleteBiometricUnlockKeyForUser", () => { + it("should call the platform specific deleteBiometricKey method", async () => { + const sut = new MainBiometricsService( + i18nService, + windowMain, + logService, + messagingService, + process.platform, + biometricStateService, + ); + const osBiometricsService = mock(); + (sut as any).osBiometricsService = osBiometricsService; + + const userId = "test" as UserId; + + await sut.deleteBiometricUnlockKeyForUser(userId); + + expect(osBiometricsService.deleteBiometricKey).toHaveBeenCalledWith( + "Bitwarden_biometric", + `${userId}_user_biometric`, + ); + }); + }); + + describe("setShouldAutopromptNow", () => { + let sut: MainBiometricsService; + + beforeEach(() => { + sut = new MainBiometricsService( + i18nService, + windowMain, + logService, + messagingService, + process.platform, + biometricStateService, + ); + }); + + it("should set shouldAutopromptNow to false", async () => { + await sut.setShouldAutopromptNow(false); + + const shouldAutoPrompt = await sut.getShouldAutopromptNow(); + + expect(shouldAutoPrompt).toBe(false); + }); + + it("should set shouldAutopromptNow to true", async () => { + await sut.setShouldAutopromptNow(true); + + const shouldAutoPrompt = await sut.getShouldAutopromptNow(); + + expect(shouldAutoPrompt).toBe(true); + }); + }); + + describe("getShouldAutopromptNow", () => { + it("defaults shouldAutoPrompt is true", async () => { + const sut = new MainBiometricsService( + i18nService, + windowMain, + logService, + messagingService, + process.platform, + biometricStateService, + ); + + const shouldAutoPrompt = await sut.getShouldAutopromptNow(); + + expect(shouldAutoPrompt).toBe(true); + }); + }); +}); diff --git a/apps/desktop/src/key-management/biometrics/main-biometrics.service.ts b/apps/desktop/src/key-management/biometrics/main-biometrics.service.ts index 013e69f118c..dd2e15a2fe8 100644 --- a/apps/desktop/src/key-management/biometrics/main-biometrics.service.ts +++ b/apps/desktop/src/key-management/biometrics/main-biometrics.service.ts @@ -13,7 +13,7 @@ import { OsBiometricService } from "./os-biometrics.service"; export class MainBiometricsService extends DesktopBiometricsService { private osBiometricsService: OsBiometricService; - private clientKeyHalves = new Map(); + private clientKeyHalves = new Map(); private shouldAutoPrompt = true; constructor( @@ -104,7 +104,7 @@ export class MainBiometricsService extends DesktopBiometricsService { return await this.osBiometricsService.osBiometricsSetup(); } - async setClientKeyHalfForUser(userId: UserId, value: string): Promise { + async setClientKeyHalfForUser(userId: UserId, value: string | null): Promise { this.clientKeyHalves.set(userId, value); } @@ -116,7 +116,7 @@ export class MainBiometricsService extends DesktopBiometricsService { const biometricKey = await this.osBiometricsService.getBiometricKey( "Bitwarden_biometric", `${userId}_user_biometric`, - this.clientKeyHalves.get(userId), + this.clientKeyHalves.get(userId) ?? undefined, ); if (biometricKey == null) { return null; @@ -136,7 +136,7 @@ export class MainBiometricsService extends DesktopBiometricsService { service, storageKey, value, - this.clientKeyHalves.get(userId), + this.clientKeyHalves.get(userId) ?? undefined, ); } diff --git a/apps/desktop/src/key-management/biometrics/renderer-biometrics.service.ts b/apps/desktop/src/key-management/biometrics/renderer-biometrics.service.ts index a08e68b53f2..2a0b1282778 100644 --- a/apps/desktop/src/key-management/biometrics/renderer-biometrics.service.ts +++ b/apps/desktop/src/key-management/biometrics/renderer-biometrics.service.ts @@ -40,7 +40,7 @@ export class RendererBiometricsService extends DesktopBiometricsService { return await ipc.keyManagement.biometric.setupBiometrics(); } - async setClientKeyHalfForUser(userId: UserId, value: string): Promise { + async setClientKeyHalfForUser(userId: UserId, value: string | null): Promise { return await ipc.keyManagement.biometric.setClientKeyHalf(userId, value); } diff --git a/apps/desktop/src/key-management/preload.ts b/apps/desktop/src/key-management/preload.ts index b73542ca725..c955571697b 100644 --- a/apps/desktop/src/key-management/preload.ts +++ b/apps/desktop/src/key-management/preload.ts @@ -39,7 +39,7 @@ const biometric = { ipcRenderer.invoke("biometric", { action: BiometricAction.Setup, } satisfies BiometricMessage), - setClientKeyHalf: (userId: string, value: string): Promise => + setClientKeyHalf: (userId: string, value: string | null): Promise => ipcRenderer.invoke("biometric", { action: BiometricAction.SetClientKeyHalf, userId: userId, diff --git a/apps/desktop/src/platform/services/electron-key.service.ts b/apps/desktop/src/platform/services/electron-key.service.ts index 4031af2873c..dceb1dea08f 100644 --- a/apps/desktop/src/platform/services/electron-key.service.ts +++ b/apps/desktop/src/platform/services/electron-key.service.ts @@ -81,7 +81,7 @@ export class ElectronKeyService extends DefaultKeyService { // May resolve to null, in which case no client key have is required // TODO: Move to windows implementation const clientEncKeyHalf = await this.getBiometricEncryptionClientKeyHalf(userKey, userId); - await this.biometricService.setClientKeyHalfForUser(userId, clientEncKeyHalf as string); + await this.biometricService.setClientKeyHalfForUser(userId, clientEncKeyHalf); await this.biometricService.setBiometricProtectedUnlockKeyForUser(userId, userKey.keyB64); } diff --git a/apps/desktop/src/types/biometric-message.ts b/apps/desktop/src/types/biometric-message.ts index 7946280e9a6..f7a7ef0c507 100644 --- a/apps/desktop/src/types/biometric-message.ts +++ b/apps/desktop/src/types/biometric-message.ts @@ -15,9 +15,22 @@ export enum BiometricAction { SetShouldAutoprompt = "setShouldAutoprompt", } -export type BiometricMessage = { - action: BiometricAction; - key?: string; - userId?: string; - data?: any; -}; +export type BiometricMessage = + | { + action: BiometricAction.SetClientKeyHalf; + userId: string; + key: string | null; + } + | { + action: BiometricAction.SetKeyForUser; + userId: string; + key: string; + } + | { + action: Exclude< + BiometricAction, + BiometricAction.SetClientKeyHalf | BiometricAction.SetKeyForUser + >; + userId?: string; + data?: any; + };