From 75d06d2e2aa6d2e41703ce5c282bb7a70ec84a56 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Tue, 20 Jan 2026 17:49:43 +0100 Subject: [PATCH] Remove abstraction --- libs/unlock/src/abstractions/index.ts | 1 - .../unlock/src/abstractions/unlock.service.ts | 19 -- libs/unlock/src/default-unlock.service.ts | 201 +++++++++++++++++ libs/unlock/src/index.ts | 6 +- libs/unlock/src/unlock.service.ts | 212 ++---------------- 5 files changed, 219 insertions(+), 220 deletions(-) delete mode 100644 libs/unlock/src/abstractions/index.ts delete mode 100644 libs/unlock/src/abstractions/unlock.service.ts create mode 100644 libs/unlock/src/default-unlock.service.ts diff --git a/libs/unlock/src/abstractions/index.ts b/libs/unlock/src/abstractions/index.ts deleted file mode 100644 index 9393b2c8475..00000000000 --- a/libs/unlock/src/abstractions/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { UnlockService } from "./unlock.service"; diff --git a/libs/unlock/src/abstractions/unlock.service.ts b/libs/unlock/src/abstractions/unlock.service.ts deleted file mode 100644 index 13a07fc8779..00000000000 --- a/libs/unlock/src/abstractions/unlock.service.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { UserId } from "@bitwarden/common/types/guid"; - -import { PinLockType } from "@bitwarden/common/key-management/pin/pin-lock-type"; - -/** - * Service for unlocking a user's account with various methods. - */ -export abstract class UnlockService { - /** - * Unlocks the user's account using their PIN. - * - * @param userId - The user's id - * @param pin - The user's PIN - * @param pinLockType - The type of PIN lock (PERSISTENT or EPHEMERAL) - * @throws If the SDK is not available - * @throws If the PIN is invalid or decryption fails - */ - abstract unlockWithPin(userId: UserId, pin: string, pinLockType: PinLockType): Promise; -} diff --git a/libs/unlock/src/default-unlock.service.ts b/libs/unlock/src/default-unlock.service.ts new file mode 100644 index 00000000000..7f710ae5ff8 --- /dev/null +++ b/libs/unlock/src/default-unlock.service.ts @@ -0,0 +1,201 @@ +import { first, firstValueFrom, map } from "rxjs"; + +import { AccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/account-cryptographic-state.service"; +import { PinLockType } from "@bitwarden/common/key-management/pin/pin-lock-type"; +import { PinStateServiceAbstraction } from "@bitwarden/common/key-management/pin/pin-state.service.abstraction"; +import { RegisterSdkService } from "@bitwarden/common/platform/abstractions/sdk/register-sdk.service"; +import { UserId } from "@bitwarden/common/types/guid"; + +import { UnlockService } from "./unlock.service"; +import { KdfConfig, KdfConfigService } from "@bitwarden/key-management"; +import { EncString, Kdf, MasterPasswordUnlockData, PasswordProtectedKeyEnvelope, UnsignedSharedKey, WrappedAccountCryptographicState } from "@bitwarden/sdk-internal"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { + asUuid, +} from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { DeviceKey } from "@bitwarden/common/types/key"; + +export class DefaultUnlockService implements UnlockService { + constructor( + private registerSdkService: RegisterSdkService, + private accountCryptographicStateService: AccountCryptographicStateService, + private pinStateService: PinStateServiceAbstraction, + private kdfService: KdfConfigService, + private accountService: AccountService, + private masterPasswordService: InternalMasterPasswordServiceAbstraction, + private apiService: ApiService, + ) { } + + private async getAccountCryptographicState(userId: UserId): Promise { + return firstValueFrom( + this.accountCryptographicStateService.accountCryptographicState$(userId), + ); + } + + private async getKdfParams(userId: UserId): Promise { + return firstValueFrom( + this.kdfService.getKdfConfig$(userId).pipe( + map((config: KdfConfig) => { + return config.toSdkConfig(); + }), + ), + ); + } + + private async getEmail(userId: UserId): Promise { + return await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.email)), + ); + } + + private async getPinProtectedUserKeyEnvelope(userId: UserId): Promise { + const pinLockType = await this.pinStateService.getPinLockType(userId); + return this.pinStateService.getPinProtectedUserKeyEnvelope( + userId, + pinLockType, + ); + } + + private async getMasterPasswordUnlockData(userId: UserId): Promise { + const unlockData = await firstValueFrom(this.masterPasswordService.masterPasswordUnlockData$(userId)); + return unlockData.toSdk(); + } + + async unlockWithDeviceKey(userId: UserId, + encryptedDevicePrivateKey: EncString, + encryptedUserKey: UnsignedSharedKey, + deviceKey: DeviceKey, + ): Promise { + await firstValueFrom( + this.registerSdkService.registerClient$(userId).pipe( + map(async (sdk) => { + if (!sdk) { + throw new Error("SDK not available"); + } + using ref = sdk.take(); + return ref.value.crypto().initialize_user_crypto({ + userId: asUuid(userId), + kdfParams: await this.getKdfParams(userId), + email: await this.getEmail(userId), + accountCryptographicState: await this.getAccountCryptographicState(userId), + method: { + deviceKey: { + device_key: deviceKey.toBase64(), + protected_device_private_key: encryptedDevicePrivateKey, + device_protected_user_key: encryptedUserKey, + }, + }, + }); + }), + ), + ); + } + + async unlockWithAuthRequest(userId: UserId, privateKey: string, protectedUserKey: UnsignedSharedKey): Promise { + await firstValueFrom( + this.registerSdkService.registerClient$(userId).pipe( + map(async (sdk) => { + if (!sdk) { + throw new Error("SDK not available"); + } + using ref = sdk.take(); + return ref.value.crypto().initialize_user_crypto({ + userId: asUuid(userId), + kdfParams: await this.getKdfParams(userId), + email: await this.getEmail(userId), + accountCryptographicState: await this.getAccountCryptographicState(userId), + method: { + authRequest: { + request_private_key: privateKey, + method: { + userKey: { + protected_user_key: protectedUserKey, + } + } + }, + }, + }); + }), + ), + ); + } + + async unlockWithKeyConnector(userId: UserId, keyConnectorUrl: string): Promise { + const keyConnectorKey = (await this.apiService.getMasterKeyFromKeyConnector(keyConnectorUrl)).key; + const keyConnectorKeyWrappedUserKey = await this.masterPasswordService.getMasterKeyEncryptedUserKey(userId); + await firstValueFrom( + this.registerSdkService.registerClient$(userId).pipe( + map(async (sdk) => { + if (!sdk) { + throw new Error("SDK not available"); + } + using ref = sdk.take(); + return ref.value.crypto().initialize_user_crypto({ + userId: asUuid(userId), + kdfParams: await this.getKdfParams(userId), + email: await this.getEmail(userId), + accountCryptographicState: await this.getAccountCryptographicState(userId), + method: { + keyConnector: { + master_key: keyConnectorKey, + user_key: keyConnectorKeyWrappedUserKey.toSdk(), + }, + }, + }); + }), + ), + ); + } + + async unlockWithPin(userId: UserId, pin: string): Promise { + await firstValueFrom( + this.registerSdkService.registerClient$(userId).pipe( + map(async (sdk) => { + if (!sdk) { + throw new Error("SDK not available"); + } + using ref = sdk.take(); + return ref.value.crypto().initialize_user_crypto({ + userId: asUuid(userId), + kdfParams: await this.getKdfParams(userId), + email: await this.getEmail(userId), + accountCryptographicState: await this.getAccountCryptographicState(userId), + method: { + pinEnvelope: { + pin: pin, + pin_protected_user_key_envelope: await this.getPinProtectedUserKeyEnvelope(userId), + }, + }, + }); + }), + ), + ); + } + + async unlockWithMasterPassword(userId: UserId, masterPassword: string): Promise { + await firstValueFrom( + this.registerSdkService.registerClient$(userId).pipe( + map(async (sdk) => { + if (!sdk) { + throw new Error("SDK not available"); + } + using ref = sdk.take(); + return ref.value.crypto().initialize_user_crypto({ + userId: asUuid(userId), + kdfParams: await this.getKdfParams(userId), + email: await this.getEmail(userId), + accountCryptographicState: await this.getAccountCryptographicState(userId), + method: { + masterPasswordUnlock: { + password: masterPassword, + master_password_unlock: await this.getMasterPasswordUnlockData(userId), + }, + }, + }); + }), + ), + ); + } +} diff --git a/libs/unlock/src/index.ts b/libs/unlock/src/index.ts index 8cddc0545a2..c566b5be8f6 100644 --- a/libs/unlock/src/index.ts +++ b/libs/unlock/src/index.ts @@ -1,5 +1,5 @@ -export { UnlockService } from "./abstractions/unlock.service"; -export { DefaultUnlockService } from "./unlock.service"; +export { UnlockService } from "./unlock.service"; +export { DefaultUnlockService } from "./default-unlock.service"; // Re-export abstractions -export * from "./abstractions"; + diff --git a/libs/unlock/src/unlock.service.ts b/libs/unlock/src/unlock.service.ts index 23b178b213f..13a07fc8779 100644 --- a/libs/unlock/src/unlock.service.ts +++ b/libs/unlock/src/unlock.service.ts @@ -1,201 +1,19 @@ -import { first, firstValueFrom, map } from "rxjs"; - -import { AccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/account-cryptographic-state.service"; -import { PinLockType } from "@bitwarden/common/key-management/pin/pin-lock-type"; -import { PinStateServiceAbstraction } from "@bitwarden/common/key-management/pin/pin-state.service.abstraction"; -import { RegisterSdkService } from "@bitwarden/common/platform/abstractions/sdk/register-sdk.service"; import { UserId } from "@bitwarden/common/types/guid"; -import { UnlockService } from "./abstractions/unlock.service"; -import { KdfConfig, KdfConfigService } from "@bitwarden/key-management"; -import { EncString, Kdf, MasterPasswordUnlockData, PasswordProtectedKeyEnvelope, UnsignedSharedKey, WrappedAccountCryptographicState } from "@bitwarden/sdk-internal"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; -import { - asUuid, -} from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { DeviceKey } from "@bitwarden/common/types/key"; +import { PinLockType } from "@bitwarden/common/key-management/pin/pin-lock-type"; -export class DefaultUnlockService implements UnlockService { - constructor( - private registerSdkService: RegisterSdkService, - private accountCryptographicStateService: AccountCryptographicStateService, - private pinStateService: PinStateServiceAbstraction, - private kdfService: KdfConfigService, - private accountService: AccountService, - private masterPasswordService: InternalMasterPasswordServiceAbstraction, - private apiService: ApiService, - ) { } - - private async getAccountCryptographicState(userId: UserId): Promise { - return firstValueFrom( - this.accountCryptographicStateService.accountCryptographicState$(userId), - ); - } - - private async getKdfParams(userId: UserId): Promise { - return firstValueFrom( - this.kdfService.getKdfConfig$(userId).pipe( - map((config: KdfConfig) => { - return config.toSdkConfig(); - }), - ), - ); - } - - private async getEmail(userId: UserId): Promise { - return await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.email)), - ); - } - - private async getPinProtectedUserKeyEnvelope(userId: UserId): Promise { - const pinLockType = await this.pinStateService.getPinLockType(userId); - return this.pinStateService.getPinProtectedUserKeyEnvelope( - userId, - pinLockType, - ); - } - - private async getMasterPasswordUnlockData(userId: UserId): Promise { - const unlockData = await firstValueFrom(this.masterPasswordService.masterPasswordUnlockData$(userId)); - return unlockData.toSdk(); - } - - async unlockWithDeviceKey(userId: UserId, - encryptedDevicePrivateKey: EncString, - encryptedUserKey: UnsignedSharedKey, - deviceKey: DeviceKey, - ): Promise { - await firstValueFrom( - this.registerSdkService.registerClient$(userId).pipe( - map(async (sdk) => { - if (!sdk) { - throw new Error("SDK not available"); - } - using ref = sdk.take(); - return ref.value.crypto().initialize_user_crypto({ - userId: asUuid(userId), - kdfParams: await this.getKdfParams(userId), - email: await this.getEmail(userId), - accountCryptographicState: await this.getAccountCryptographicState(userId), - method: { - deviceKey: { - device_key: deviceKey.toBase64(), - protected_device_private_key: encryptedDevicePrivateKey, - device_protected_user_key: encryptedUserKey, - }, - }, - }); - }), - ), - ); - } - - async unlockWithAuthRequest(userId: UserId, privateKey: string, protectedUserKey: UnsignedSharedKey): Promise { - await firstValueFrom( - this.registerSdkService.registerClient$(userId).pipe( - map(async (sdk) => { - if (!sdk) { - throw new Error("SDK not available"); - } - using ref = sdk.take(); - return ref.value.crypto().initialize_user_crypto({ - userId: asUuid(userId), - kdfParams: await this.getKdfParams(userId), - email: await this.getEmail(userId), - accountCryptographicState: await this.getAccountCryptographicState(userId), - method: { - authRequest: { - request_private_key: privateKey, - method: { - userKey: { - protected_user_key: protectedUserKey, - } - } - }, - }, - }); - }), - ), - ); - } - - async unlockWithKeyConnector(userId: UserId, keyConnectorUrl: string): Promise { - const keyConnectorKey = (await this.apiService.getMasterKeyFromKeyConnector(keyConnectorUrl)).key; - const keyConnectorKeyWrappedUserKey = await this.masterPasswordService.getMasterKeyEncryptedUserKey(userId); - await firstValueFrom( - this.registerSdkService.registerClient$(userId).pipe( - map(async (sdk) => { - if (!sdk) { - throw new Error("SDK not available"); - } - using ref = sdk.take(); - return ref.value.crypto().initialize_user_crypto({ - userId: asUuid(userId), - kdfParams: await this.getKdfParams(userId), - email: await this.getEmail(userId), - accountCryptographicState: await this.getAccountCryptographicState(userId), - method: { - keyConnector: { - master_key: keyConnectorKey, - user_key: keyConnectorKeyWrappedUserKey.toSdk(), - }, - }, - }); - }), - ), - ); - } - - async unlockWithPin(userId: UserId, pin: string): Promise { - await firstValueFrom( - this.registerSdkService.registerClient$(userId).pipe( - map(async (sdk) => { - if (!sdk) { - throw new Error("SDK not available"); - } - using ref = sdk.take(); - return ref.value.crypto().initialize_user_crypto({ - userId: asUuid(userId), - kdfParams: await this.getKdfParams(userId), - email: await this.getEmail(userId), - accountCryptographicState: await this.getAccountCryptographicState(userId), - method: { - pinEnvelope: { - pin: pin, - pin_protected_user_key_envelope: await this.getPinProtectedUserKeyEnvelope(userId), - }, - }, - }); - }), - ), - ); - } - - async unlockWithMasterPassword(userId: UserId, masterPassword: string): Promise { - await firstValueFrom( - this.registerSdkService.registerClient$(userId).pipe( - map(async (sdk) => { - if (!sdk) { - throw new Error("SDK not available"); - } - using ref = sdk.take(); - return ref.value.crypto().initialize_user_crypto({ - userId: asUuid(userId), - kdfParams: await this.getKdfParams(userId), - email: await this.getEmail(userId), - accountCryptographicState: await this.getAccountCryptographicState(userId), - method: { - masterPasswordUnlock: { - password: masterPassword, - master_password_unlock: await this.getMasterPasswordUnlockData(userId), - }, - }, - }); - }), - ), - ); - } +/** + * Service for unlocking a user's account with various methods. + */ +export abstract class UnlockService { + /** + * Unlocks the user's account using their PIN. + * + * @param userId - The user's id + * @param pin - The user's PIN + * @param pinLockType - The type of PIN lock (PERSISTENT or EPHEMERAL) + * @throws If the SDK is not available + * @throws If the PIN is invalid or decryption fails + */ + abstract unlockWithPin(userId: UserId, pin: string, pinLockType: PinLockType): Promise; }