1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-17 09:59:41 +00:00

[PM-27239] Tde registration encryption v2 (#17831)

* tmp

* Implement TDE v2 registration via SDK

* Undo encstring test string change

* Add feature flag

* Add tests

* Continue tests

* Cleanup

* Cleanup

* run prettier

* Update to apply new sdk changes

* Fix build

* Update package lock

* Fix tests

---------

Co-authored-by: Bernd Schoolmann <quexten@fedora-2.fritz.box>
This commit is contained in:
Bernd Schoolmann
2025-12-23 16:27:25 +01:00
committed by GitHub
parent dc1ecaaaa2
commit 3fbb4aced9
7 changed files with 501 additions and 21 deletions

View File

@@ -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<AccountService>;
let anonLayoutWrapperDataService: MockProxy<AnonLayoutWrapperDataService>;
let apiService: MockProxy<ApiService>;
let destroyRef: MockProxy<DestroyRef>;
let deviceTrustService: MockProxy<DeviceTrustServiceAbstraction>;
let dialogService: MockProxy<DialogService>;
let formBuilder: FormBuilder;
let i18nService: MockProxy<I18nService>;
let keyService: MockProxy<KeyService>;
let loginDecryptionOptionsService: MockProxy<LoginDecryptionOptionsService>;
let loginEmailService: MockProxy<LoginEmailServiceAbstraction>;
let messagingService: MockProxy<MessagingService>;
let organizationApiService: MockProxy<OrganizationApiServiceAbstraction>;
let passwordResetEnrollmentService: MockProxy<PasswordResetEnrollmentServiceAbstraction>;
let platformUtilsService: MockProxy<PlatformUtilsService>;
let router: MockProxy<Router>;
let ssoLoginService: MockProxy<SsoLoginServiceAbstraction>;
let toastService: MockProxy<ToastService>;
let userDecryptionOptionsService: MockProxy<UserDecryptionOptionsServiceAbstraction>;
let validationService: MockProxy<ValidationService>;
let logoutService: MockProxy<LogoutService>;
let registerSdkService: MockProxy<RegisterSdkService>;
let securityStateService: MockProxy<SecurityStateService>;
let appIdService: MockProxy<AppIdService>;
let configService: MockProxy<ConfigService>;
let accountCryptographicStateService: MockProxy<any>;
const mockUserId = "user-id-123" as UserId;
const mockEmail = "test@example.com";
const mockOrgId = "org-id-456";
beforeEach(() => {
accountService = mock<AccountService>();
anonLayoutWrapperDataService = mock<AnonLayoutWrapperDataService>();
apiService = mock<ApiService>();
destroyRef = mock<DestroyRef>();
deviceTrustService = mock<DeviceTrustServiceAbstraction>();
dialogService = mock<DialogService>();
formBuilder = new FormBuilder();
i18nService = mock<I18nService>();
keyService = mock<KeyService>();
loginDecryptionOptionsService = mock<LoginDecryptionOptionsService>();
loginEmailService = mock<LoginEmailServiceAbstraction>();
messagingService = mock<MessagingService>();
organizationApiService = mock<OrganizationApiServiceAbstraction>();
passwordResetEnrollmentService = mock<PasswordResetEnrollmentServiceAbstraction>();
platformUtilsService = mock<PlatformUtilsService>();
router = mock<Router>();
ssoLoginService = mock<SsoLoginServiceAbstraction>();
toastService = mock<ToastService>();
userDecryptionOptionsService = mock<UserDecryptionOptionsServiceAbstraction>();
validationService = mock<ValidationService>();
logoutService = mock<LogoutService>();
registerSdkService = mock<RegisterSdkService>();
securityStateService = mock<SecurityStateService>();
appIdService = mock<AppIdService>();
configService = mock<ConfigService>();
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"]);
});
});
});

View File

@@ -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<SdkOrganizationId>(organizationId),
org_public_key: orgKeyResponse.publicKey,
user_id: asUuid<SdkUserId>(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) {

View File

@@ -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,

View File

@@ -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<DeviceKey | null>;
abstract setDeviceKey(userId: UserId, deviceKey: DeviceKey | null): Promise<void>;
abstract decryptUserKeyWithDeviceKey(
userId: UserId,
encryptedDevicePrivateKey: EncString,

View File

@@ -356,7 +356,7 @@ export class DeviceTrustService implements DeviceTrustServiceAbstraction {
}
}
private async setDeviceKey(userId: UserId, deviceKey: DeviceKey | null): Promise<void> {
async setDeviceKey(userId: UserId, deviceKey: DeviceKey | null): Promise<void> {
if (!userId) {
throw new Error("UserId is required. Cannot set device key.");
}

16
package-lock.json generated
View File

@@ -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"

View File

@@ -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",