1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-25 00:53:22 +00:00

[PM-30144] Implement client-side user-key-rotation-service (#18285)

* Implement client-side user-key-rotation-service

* Feature flag

* Add tests

* Fix flag name

* Fix build

* Prettier

* Small clean-up

* Codeowners order cleanup

* Fix eslint issue

* Update sdk to 550

* Cleanup & fix incompatibilities

* Prettier
This commit is contained in:
Bernd Schoolmann
2026-02-20 15:28:24 +01:00
committed by GitHub
parent 40c8139e1c
commit bb110122a5
22 changed files with 674 additions and 1 deletions

View File

@@ -57,6 +57,7 @@ import {
KeyRotationTrustInfoComponent,
} from "@bitwarden/key-management-ui";
import { BitwardenClient, PureCrypto } from "@bitwarden/sdk-internal";
import { UserKeyRotationServiceAbstraction } from "@bitwarden/user-crypto-management";
import { OrganizationUserResetPasswordService } from "../../admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service";
import { WebauthnLoginAdminService } from "../../auth";
@@ -287,6 +288,7 @@ describe("KeyRotationService", () => {
let mockSdkClientFactory: MockProxy<SdkClientFactory>;
let mockSecurityStateService: MockProxy<SecurityStateService>;
let mockMasterPasswordService: MockProxy<MasterPasswordServiceAbstraction>;
let mockSdkUserKeyRotationService: MockProxy<UserKeyRotationServiceAbstraction>;
const mockUser = {
id: "mockUserId" as UserId,
@@ -348,6 +350,7 @@ describe("KeyRotationService", () => {
mockDialogService = mock<DialogService>();
mockCryptoFunctionService = mock<CryptoFunctionService>();
mockKdfConfigService = mock<KdfConfigService>();
mockSdkUserKeyRotationService = mock<UserKeyRotationServiceAbstraction>();
mockSdkClientFactory = mock<SdkClientFactory>();
mockSdkClientFactory.createSdkClient.mockResolvedValue({
crypto: () => {
@@ -358,6 +361,7 @@ describe("KeyRotationService", () => {
} as any;
},
} as BitwardenClient);
mockSecurityStateService = mock<SecurityStateService>();
mockMasterPasswordService = mock<MasterPasswordServiceAbstraction>();
@@ -384,6 +388,7 @@ describe("KeyRotationService", () => {
mockSdkClientFactory,
mockSecurityStateService,
mockMasterPasswordService,
mockSdkUserKeyRotationService,
);
});
@@ -509,7 +514,12 @@ describe("KeyRotationService", () => {
);
mockKeyService.userSigningKey$.mockReturnValue(new BehaviorSubject(null));
mockSecurityStateService.accountSecurityState$.mockReturnValue(new BehaviorSubject(null));
mockConfigService.getFeatureFlag.mockResolvedValue(true);
mockConfigService.getFeatureFlag.mockImplementation(async (flag: FeatureFlag) => {
if (flag === FeatureFlag.EnrollAeadOnKeyRotation) {
return true;
}
return false;
});
const spy = jest.spyOn(keyRotationService, "getRotatedAccountKeysFlagged").mockResolvedValue({
userKey: TEST_VECTOR_USER_KEY_V2,

View File

@@ -39,6 +39,7 @@ import {
KeyRotationTrustInfoComponent,
} from "@bitwarden/key-management-ui";
import { PureCrypto, TokenProvider } from "@bitwarden/sdk-internal";
import { UserKeyRotationServiceAbstraction } from "@bitwarden/user-crypto-management";
import { OrganizationUserResetPasswordService } from "../../admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service";
import { WebauthnLoginAdminService } from "../../auth/core";
@@ -101,6 +102,7 @@ export class UserKeyRotationService {
private sdkClientFactory: SdkClientFactory,
private securityStateService: SecurityStateService,
private masterPasswordService: MasterPasswordServiceAbstraction,
private sdkUserKeyRotationService: UserKeyRotationServiceAbstraction,
) {}
/**
@@ -116,6 +118,28 @@ export class UserKeyRotationService {
user: Account,
newMasterPasswordHint?: string,
): Promise<void> {
const useSdkKeyRotation = await this.configService.getFeatureFlag(FeatureFlag.SdkKeyRotation);
if (useSdkKeyRotation) {
this.logService.info(
"[UserKey Rotation] Using SDK-based key rotation service from user-crypto-management",
);
await this.sdkUserKeyRotationService.changePasswordAndRotateUserKey(
currentMasterPassword,
newMasterPassword,
newMasterPasswordHint,
asUuid(user.id),
);
this.toastService.showToast({
variant: "success",
title: this.i18nService.t("rotationCompletedTitle"),
message: this.i18nService.t("rotationCompletedDesc"),
timeout: 15000,
});
await this.logoutService.logout(user.id);
return;
}
// Key-rotation uses the SDK, so we need to ensure that the SDK is loaded / the WASM initialized.
await SdkLoadService.Ready;