diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.spec.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.spec.ts new file mode 100644 index 00000000000..07cbb680963 --- /dev/null +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.spec.ts @@ -0,0 +1,377 @@ +// Mock asUuid to return the input value for test consistency +jest.mock("@bitwarden/common/platform/abstractions/sdk/sdk.service", () => ({ + asUuid: (x: any) => x, +})); + +import { DestroyRef } from "@angular/core"; +import { FormBuilder } from "@angular/forms"; +import { Router } from "@angular/router"; +import { mock, MockProxy } from "jest-mock-extended"; +import { BehaviorSubject, of } from "rxjs"; + +import { + LoginEmailServiceAbstraction, + LogoutService, + UserDecryptionOptionsServiceAbstraction, +} from "@bitwarden/auth/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { PasswordResetEnrollmentServiceAbstraction } from "@bitwarden/common/auth/abstractions/password-reset-enrollment.service.abstraction"; +import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; +import { ClientType } from "@bitwarden/common/enums"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction"; +import { SecurityStateService } from "@bitwarden/common/key-management/security-state/abstractions/security-state.service"; +import { SignedSecurityState } from "@bitwarden/common/key-management/types"; +import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { RegisterSdkService } from "@bitwarden/common/platform/abstractions/sdk/register-sdk.service"; +import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { UserId } from "@bitwarden/common/types/guid"; +// eslint-disable-next-line no-restricted-imports +import { AnonLayoutWrapperDataService, DialogService, ToastService } from "@bitwarden/components"; +import { KeyService } from "@bitwarden/key-management"; + +import { LoginDecryptionOptionsComponent } from "./login-decryption-options.component"; +import { LoginDecryptionOptionsService } from "./login-decryption-options.service"; + +describe("LoginDecryptionOptionsComponent", () => { + let component: LoginDecryptionOptionsComponent; + let accountService: MockProxy; + let anonLayoutWrapperDataService: MockProxy; + let apiService: MockProxy; + let destroyRef: MockProxy; + let deviceTrustService: MockProxy; + let dialogService: MockProxy; + let formBuilder: FormBuilder; + let i18nService: MockProxy; + let keyService: MockProxy; + let loginDecryptionOptionsService: MockProxy; + let loginEmailService: MockProxy; + let messagingService: MockProxy; + let organizationApiService: MockProxy; + let passwordResetEnrollmentService: MockProxy; + let platformUtilsService: MockProxy; + let router: MockProxy; + let ssoLoginService: MockProxy; + let toastService: MockProxy; + let userDecryptionOptionsService: MockProxy; + let validationService: MockProxy; + let logoutService: MockProxy; + let registerSdkService: MockProxy; + let securityStateService: MockProxy; + let appIdService: MockProxy; + let configService: MockProxy; + let accountCryptographicStateService: MockProxy; + + const mockUserId = "user-id-123" as UserId; + const mockEmail = "test@example.com"; + const mockOrgId = "org-id-456"; + + beforeEach(() => { + accountService = mock(); + anonLayoutWrapperDataService = mock(); + apiService = mock(); + destroyRef = mock(); + deviceTrustService = mock(); + dialogService = mock(); + formBuilder = new FormBuilder(); + i18nService = mock(); + keyService = mock(); + loginDecryptionOptionsService = mock(); + loginEmailService = mock(); + messagingService = mock(); + organizationApiService = mock(); + passwordResetEnrollmentService = mock(); + platformUtilsService = mock(); + router = mock(); + ssoLoginService = mock(); + toastService = mock(); + userDecryptionOptionsService = mock(); + validationService = mock(); + logoutService = mock(); + registerSdkService = mock(); + securityStateService = mock(); + appIdService = mock(); + configService = mock(); + accountCryptographicStateService = mock(); + + // Setup default mocks + accountService.activeAccount$ = new BehaviorSubject({ + id: mockUserId, + email: mockEmail, + name: "Test User", + emailVerified: true, + creationDate: new Date().toISOString(), + }); + platformUtilsService.getClientType.mockReturnValue(ClientType.Browser); + deviceTrustService.getShouldTrustDevice.mockResolvedValue(true); + i18nService.t.mockImplementation((key: string) => key); + + component = new LoginDecryptionOptionsComponent( + accountService, + anonLayoutWrapperDataService, + apiService, + destroyRef, + deviceTrustService, + dialogService, + formBuilder, + i18nService, + keyService, + loginDecryptionOptionsService, + loginEmailService, + messagingService, + organizationApiService, + passwordResetEnrollmentService, + platformUtilsService, + router, + ssoLoginService, + toastService, + userDecryptionOptionsService, + validationService, + logoutService, + registerSdkService, + securityStateService, + appIdService, + configService, + accountCryptographicStateService, + ); + }); + + describe("createUser with feature flag enabled", () => { + let mockPostKeysForTdeRegistration: jest.Mock; + let mockRegistration: any; + let mockAuth: any; + let mockSdkValue: any; + let mockSdkRef: any; + let mockSdk: any; + let mockDeviceKey: string; + let mockDeviceKeyObj: SymmetricCryptoKey; + let mockUserKeyBytes: Uint8Array; + let mockPrivateKey: string; + let mockSignedPublicKey: string; + let mockSigningKey: string; + let mockSecurityState: SignedSecurityState; + + beforeEach(async () => { + // Mock asUuid to return the input value for test consistency + jest.mock("@bitwarden/common/platform/abstractions/sdk/sdk.service", () => ({ + asUuid: (x: any) => x, + })); + (Symbol as any).dispose = Symbol("dispose"); + + mockPrivateKey = "mock-private-key"; + mockSignedPublicKey = "mock-signed-public-key"; + mockSigningKey = "mock-signing-key"; + mockSecurityState = { + signature: "mock-signature", + payload: { + version: 2, + timestamp: Date.now(), + privateKeyHash: "mock-hash", + }, + } as any; + const deviceKeyBytes = new Uint8Array(32).fill(5); + mockDeviceKey = Buffer.from(deviceKeyBytes).toString("base64"); + mockDeviceKeyObj = SymmetricCryptoKey.fromString(mockDeviceKey); + mockUserKeyBytes = new Uint8Array(64); + + mockPostKeysForTdeRegistration = jest.fn().mockResolvedValue({ + account_cryptographic_state: { + V2: { + private_key: mockPrivateKey, + signed_public_key: mockSignedPublicKey, + signing_key: mockSigningKey, + security_state: mockSecurityState, + }, + }, + device_key: mockDeviceKey, + user_key: mockUserKeyBytes, + }); + + mockRegistration = { + post_keys_for_tde_registration: mockPostKeysForTdeRegistration, + }; + + mockAuth = { + registration: jest.fn().mockReturnValue(mockRegistration), + }; + + mockSdkValue = { + auth: jest.fn().mockReturnValue(mockAuth), + }; + + mockSdkRef = { + value: mockSdkValue, + [Symbol.dispose]: jest.fn(), + }; + + mockSdk = { + take: jest.fn().mockReturnValue(mockSdkRef), + }; + + registerSdkService.registerClient$ = jest.fn((userId: UserId) => of(mockSdk)) as any; + + // Setup for new user state + userDecryptionOptionsService.userDecryptionOptionsById$.mockReturnValue( + of({ + trustedDeviceOption: { + hasAdminApproval: false, + hasLoginApprovingDevice: false, + hasManageResetPasswordPermission: false, + isTdeOffboarding: false, + }, + hasMasterPassword: false, + keyConnectorOption: undefined, + }), + ); + + ssoLoginService.getActiveUserOrganizationSsoIdentifier.mockResolvedValue("org-identifier"); + organizationApiService.getAutoEnrollStatus.mockResolvedValue({ + id: mockOrgId, + resetPasswordEnabled: true, + } as any); + + // Initialize component to set up new user state + await component.ngOnInit(); + }); + + it("should use SDK v2 registration when feature flag is enabled", async () => { + // Arrange + configService.getFeatureFlag.mockResolvedValue(true); + loginDecryptionOptionsService.handleCreateUserSuccess.mockResolvedValue(undefined); + router.navigate.mockResolvedValue(true); + appIdService.getAppId.mockResolvedValue("mock-app-id"); + organizationApiService.getKeys.mockResolvedValue({ + publicKey: "mock-org-public-key", + privateKey: "mock-org-private-key", + } as any); + + // Act + await component["createUser"](); + + // Assert + expect(configService.getFeatureFlag).toHaveBeenCalledWith( + FeatureFlag.PM27279_V2RegistrationTdeJit, + ); + expect(appIdService.getAppId).toHaveBeenCalled(); + expect(organizationApiService.getKeys).toHaveBeenCalledWith(mockOrgId); + expect(registerSdkService.registerClient$).toHaveBeenCalledWith(mockUserId); + + // Verify SDK registration was called with correct parameters + expect(mockSdkValue.auth).toHaveBeenCalled(); + expect(mockAuth.registration).toHaveBeenCalled(); + expect(mockPostKeysForTdeRegistration).toHaveBeenCalledWith({ + org_id: mockOrgId, + org_public_key: "mock-org-public-key", + user_id: mockUserId, + device_identifier: "mock-app-id", + trust_device: true, + }); + + const expectedDeviceKey = mockDeviceKeyObj; + const expectedUserKey = new SymmetricCryptoKey(new Uint8Array(mockUserKeyBytes)); + + // Verify keys were set + expect(keyService.setPrivateKey).toHaveBeenCalledWith(mockPrivateKey, mockUserId); + expect(keyService.setSignedPublicKey).toHaveBeenCalledWith(mockSignedPublicKey, mockUserId); + expect(keyService.setUserSigningKey).toHaveBeenCalledWith(mockSigningKey, mockUserId); + expect(securityStateService.setAccountSecurityState).toHaveBeenCalledWith( + mockSecurityState, + mockUserId, + ); + expect(accountCryptographicStateService.setAccountCryptographicState).toHaveBeenCalledWith( + expect.objectContaining({ + V2: { + private_key: mockPrivateKey, + signed_public_key: mockSignedPublicKey, + signing_key: mockSigningKey, + security_state: mockSecurityState, + }, + }), + mockUserId, + ); + + expect(validationService.showError).not.toHaveBeenCalled(); + + // Verify device and user keys were persisted + expect(deviceTrustService.setDeviceKey).toHaveBeenCalledWith( + mockUserId, + expect.any(SymmetricCryptoKey), + ); + expect(keyService.setUserKey).toHaveBeenCalledWith( + expect.any(SymmetricCryptoKey), + mockUserId, + ); + + const [, deviceKeyArg] = deviceTrustService.setDeviceKey.mock.calls[0]; + const [userKeyArg] = keyService.setUserKey.mock.calls[0]; + + expect((deviceKeyArg as SymmetricCryptoKey).keyB64).toBe(expectedDeviceKey.keyB64); + expect((userKeyArg as SymmetricCryptoKey).keyB64).toBe(expectedUserKey.keyB64); + + // Verify success toast and navigation + expect(toastService.showToast).toHaveBeenCalledWith({ + variant: "success", + title: null, + message: "accountSuccessfullyCreated", + }); + expect(loginDecryptionOptionsService.handleCreateUserSuccess).toHaveBeenCalled(); + expect(router.navigate).toHaveBeenCalledWith(["/tabs/vault"]); + }); + + it("should use legacy registration when feature flag is disabled", async () => { + // Arrange + configService.getFeatureFlag.mockResolvedValue(false); + + const mockPublicKey = "mock-public-key"; + const mockPrivateKey = { + encryptedString: "mock-encrypted-private-key", + } as any; + + keyService.initAccount.mockResolvedValue({ + publicKey: mockPublicKey, + privateKey: mockPrivateKey, + } as any); + + apiService.postAccountKeys.mockResolvedValue(undefined); + passwordResetEnrollmentService.enroll.mockResolvedValue(undefined); + deviceTrustService.trustDevice.mockResolvedValue(undefined); + loginDecryptionOptionsService.handleCreateUserSuccess.mockResolvedValue(undefined); + router.navigate.mockResolvedValue(true); + + // Act + await component["createUser"](); + + // Assert + expect(configService.getFeatureFlag).toHaveBeenCalledWith( + FeatureFlag.PM27279_V2RegistrationTdeJit, + ); + expect(keyService.initAccount).toHaveBeenCalledWith(mockUserId); + expect(apiService.postAccountKeys).toHaveBeenCalledWith( + expect.objectContaining({ + publicKey: mockPublicKey, + encryptedPrivateKey: mockPrivateKey.encryptedString, + }), + ); + expect(passwordResetEnrollmentService.enroll).toHaveBeenCalledWith(mockOrgId); + expect(deviceTrustService.trustDevice).toHaveBeenCalledWith(mockUserId); + + // Verify success toast + expect(toastService.showToast).toHaveBeenCalledWith({ + variant: "success", + title: null, + message: "accountSuccessfullyCreated", + }); + + // Verify navigation + expect(loginDecryptionOptionsService.handleCreateUserSuccess).toHaveBeenCalled(); + expect(router.navigate).toHaveBeenCalledWith(["/tabs/vault"]); + }); + }); +}); diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts index fb07069998b..06263ef7371 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts @@ -5,7 +5,17 @@ import { Component, DestroyRef, OnInit } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormBuilder, FormControl, ReactiveFormsModule } from "@angular/forms"; import { Router } from "@angular/router"; -import { catchError, defer, firstValueFrom, from, map, of, switchMap, throwError } from "rxjs"; +import { + catchError, + concatMap, + defer, + firstValueFrom, + from, + map, + of, + switchMap, + throwError, +} from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { @@ -20,13 +30,27 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { PasswordResetEnrollmentServiceAbstraction } from "@bitwarden/common/auth/abstractions/password-reset-enrollment.service.abstraction"; import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; import { ClientType } from "@bitwarden/common/enums"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { AccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/account-cryptographic-state.service"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction"; +import { SecurityStateService } from "@bitwarden/common/key-management/security-state/abstractions/security-state.service"; +import { + SignedPublicKey, + SignedSecurityState, + WrappedSigningKey, +} from "@bitwarden/common/key-management/types"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; +import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { RegisterSdkService } from "@bitwarden/common/platform/abstractions/sdk/register-sdk.service"; +import { asUuid } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { UserId } from "@bitwarden/common/types/guid"; +import { DeviceKey, UserKey } from "@bitwarden/common/types/key"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports import { @@ -40,6 +64,7 @@ import { TypographyModule, } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; +import { OrganizationId as SdkOrganizationId, UserId as SdkUserId } from "@bitwarden/sdk-internal"; import { LoginDecryptionOptionsService } from "./login-decryption-options.service"; @@ -112,6 +137,11 @@ export class LoginDecryptionOptionsComponent implements OnInit { private userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction, private validationService: ValidationService, private logoutService: LogoutService, + private registerSdkService: RegisterSdkService, + private securityStateService: SecurityStateService, + private appIdService: AppIdService, + private configService: ConfigService, + private accountCryptographicStateService: AccountCryptographicStateService, ) { this.clientType = this.platformUtilsService.getClientType(); } @@ -251,9 +281,85 @@ export class LoginDecryptionOptionsComponent implements OnInit { } try { - const { publicKey, privateKey } = await this.keyService.initAccount(this.activeAccountId); - const keysRequest = new KeysRequest(publicKey, privateKey.encryptedString); - await this.apiService.postAccountKeys(keysRequest); + const useSdkV2Creation = await this.configService.getFeatureFlag( + FeatureFlag.PM27279_V2RegistrationTdeJit, + ); + if (useSdkV2Creation) { + const deviceIdentifier = await this.appIdService.getAppId(); + const userId = this.activeAccountId; + const organizationId = this.newUserOrgId; + + const orgKeyResponse = await this.organizationApiService.getKeys(organizationId); + const register_result = await firstValueFrom( + this.registerSdkService.registerClient$(userId).pipe( + concatMap(async (sdk) => { + if (!sdk) { + throw new Error("SDK not available"); + } + + using ref = sdk.take(); + return await ref.value + .auth() + .registration() + .post_keys_for_tde_registration({ + org_id: asUuid(organizationId), + org_public_key: orgKeyResponse.publicKey, + user_id: asUuid(userId), + device_identifier: deviceIdentifier, + trust_device: this.formGroup.value.rememberDevice, + }); + }), + ), + ); + // The keys returned here can only be v2 keys, since the SDK only implements returning V2 keys. + if ("V1" in register_result.account_cryptographic_state) { + throw new Error("Unexpected V1 account cryptographic state"); + } + + // Note: When SDK state management matures, these should be moved into post_keys_for_tde_registration + // Set account cryptography state + await this.accountCryptographicStateService.setAccountCryptographicState( + register_result.account_cryptographic_state, + userId, + ); + // Legacy individual states + await this.keyService.setPrivateKey( + register_result.account_cryptographic_state.V2.private_key, + userId, + ); + await this.keyService.setSignedPublicKey( + register_result.account_cryptographic_state.V2.signed_public_key as SignedPublicKey, + userId, + ); + await this.keyService.setUserSigningKey( + register_result.account_cryptographic_state.V2.signing_key as WrappedSigningKey, + userId, + ); + await this.securityStateService.setAccountSecurityState( + register_result.account_cryptographic_state.V2.security_state as SignedSecurityState, + userId, + ); + + // TDE unlock + await this.deviceTrustService.setDeviceKey( + userId, + SymmetricCryptoKey.fromString(register_result.device_key) as DeviceKey, + ); + + // Set user key - user is now unlocked + await this.keyService.setUserKey( + SymmetricCryptoKey.fromString(register_result.user_key) as UserKey, + userId, + ); + } else { + const { publicKey, privateKey } = await this.keyService.initAccount(this.activeAccountId); + const keysRequest = new KeysRequest(publicKey, privateKey.encryptedString); + await this.apiService.postAccountKeys(keysRequest); + await this.passwordResetEnrollmentService.enroll(this.newUserOrgId); + if (this.formGroup.value.rememberDevice) { + await this.deviceTrustService.trustDevice(this.activeAccountId); + } + } this.toastService.showToast({ variant: "success", @@ -261,12 +367,6 @@ export class LoginDecryptionOptionsComponent implements OnInit { message: this.i18nService.t("accountSuccessfullyCreated"), }); - await this.passwordResetEnrollmentService.enroll(this.newUserOrgId); - - if (this.formGroup.value.rememberDevice) { - await this.deviceTrustService.trustDevice(this.activeAccountId); - } - await this.loginDecryptionOptionsService.handleCreateUserSuccess(); if (this.clientType === ClientType.Desktop) { diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index f905c62288e..08155bf3af2 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -44,6 +44,7 @@ export enum FeatureFlag { NoLogoutOnKdfChange = "pm-23995-no-logout-on-kdf-change", DataRecoveryTool = "pm-28813-data-recovery-tool", ConsolidatedSessionTimeoutComponent = "pm-26056-consolidated-session-timeout-component", + PM27279_V2RegistrationTdeJit = "pm-27279-v2-registration-tde-jit", /* Tools */ DesktopSendUIRefresh = "desktop-send-ui-refresh", @@ -154,6 +155,7 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.NoLogoutOnKdfChange]: FALSE, [FeatureFlag.DataRecoveryTool]: FALSE, [FeatureFlag.ConsolidatedSessionTimeoutComponent]: FALSE, + [FeatureFlag.PM27279_V2RegistrationTdeJit]: FALSE, /* Platform */ [FeatureFlag.IpcChannelFramework]: FALSE, diff --git a/libs/common/src/key-management/device-trust/abstractions/device-trust.service.abstraction.ts b/libs/common/src/key-management/device-trust/abstractions/device-trust.service.abstraction.ts index 2bc99e5e5c2..ceff220fe42 100644 --- a/libs/common/src/key-management/device-trust/abstractions/device-trust.service.abstraction.ts +++ b/libs/common/src/key-management/device-trust/abstractions/device-trust.service.abstraction.ts @@ -39,6 +39,7 @@ export abstract class DeviceTrustServiceAbstraction { /** Retrieves the device key if it exists from state or secure storage if supported for the active user. */ abstract getDeviceKey(userId: UserId): Promise; + abstract setDeviceKey(userId: UserId, deviceKey: DeviceKey | null): Promise; abstract decryptUserKeyWithDeviceKey( userId: UserId, encryptedDevicePrivateKey: EncString, diff --git a/libs/common/src/key-management/device-trust/services/device-trust.service.implementation.ts b/libs/common/src/key-management/device-trust/services/device-trust.service.implementation.ts index 59bd7bc11f2..518d16781ab 100644 --- a/libs/common/src/key-management/device-trust/services/device-trust.service.implementation.ts +++ b/libs/common/src/key-management/device-trust/services/device-trust.service.implementation.ts @@ -356,7 +356,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction { } } - private async setDeviceKey(userId: UserId, deviceKey: DeviceKey | null): Promise { + async setDeviceKey(userId: UserId, deviceKey: DeviceKey | null): Promise { if (!userId) { throw new Error("UserId is required. Cannot set device key."); } diff --git a/package-lock.json b/package-lock.json index deb3a9f261c..014c291c38c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,8 +23,8 @@ "@angular/platform-browser": "20.3.15", "@angular/platform-browser-dynamic": "20.3.15", "@angular/router": "20.3.15", - "@bitwarden/commercial-sdk-internal": "0.2.0-main.433", - "@bitwarden/sdk-internal": "0.2.0-main.433", + "@bitwarden/commercial-sdk-internal": "0.2.0-main.439", + "@bitwarden/sdk-internal": "0.2.0-main.439", "@electron/fuses": "1.8.0", "@emotion/css": "11.13.5", "@koa/multer": "4.0.0", @@ -4973,9 +4973,9 @@ "link": true }, "node_modules/@bitwarden/commercial-sdk-internal": { - "version": "0.2.0-main.433", - "resolved": "https://registry.npmjs.org/@bitwarden/commercial-sdk-internal/-/commercial-sdk-internal-0.2.0-main.433.tgz", - "integrity": "sha512-/eFzw+BUHxAmT75kKUn1r9MFsJH/GZpc3ljkjNjAqtvb3L+fz8VTHTe7FoloSoZEnAnp8OWOZy7n4DavT/XDiw==", + "version": "0.2.0-main.439", + "resolved": "https://registry.npmjs.org/@bitwarden/commercial-sdk-internal/-/commercial-sdk-internal-0.2.0-main.439.tgz", + "integrity": "sha512-Wujtym00U7XMEsf9zJ3/0Ggw9WmMcIpE9hMtcLryloX182118vnzkEQbEldqtywpMHiDsD9VmP6RiZ725nnUIQ==", "license": "BITWARDEN SOFTWARE DEVELOPMENT KIT LICENSE AGREEMENT", "dependencies": { "type-fest": "^4.41.0" @@ -5078,9 +5078,9 @@ "link": true }, "node_modules/@bitwarden/sdk-internal": { - "version": "0.2.0-main.433", - "resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.433.tgz", - "integrity": "sha512-m2PnYR0ifF0BgZ63aAt8eag0v7LeEGTJ0sa7UMbTWLwmsNnHug4u7jxIJl0WaVILNeWWK8iD/WSiw3EJeb7Fmw==", + "version": "0.2.0-main.439", + "resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.439.tgz", + "integrity": "sha512-uvIS8erGmzgWCZom7Kt78C4n4tbjfZuTCn7+y2+E8BTtLBqIZNtl4kC0tNh8c4GUWsmoIYlbQyz+HymWQ7J+QA==", "license": "GPL-3.0", "dependencies": { "type-fest": "^4.41.0" diff --git a/package.json b/package.json index f4c484f1c61..29ee9683464 100644 --- a/package.json +++ b/package.json @@ -162,8 +162,8 @@ "@angular/platform-browser": "20.3.15", "@angular/platform-browser-dynamic": "20.3.15", "@angular/router": "20.3.15", - "@bitwarden/sdk-internal": "0.2.0-main.433", - "@bitwarden/commercial-sdk-internal": "0.2.0-main.433", + "@bitwarden/sdk-internal": "0.2.0-main.439", + "@bitwarden/commercial-sdk-internal": "0.2.0-main.439", "@electron/fuses": "1.8.0", "@emotion/css": "11.13.5", "@koa/multer": "4.0.0",