1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-21 20:04:02 +00:00

[PM-18026] Implement forced, automatic KDF upgrades (#15937)

* Implement automatic kdf upgrades

* Fix kdf config not being updated

* Update legacy kdf state on master password unlock sync

* Fix cli build

* Fix

* Deduplicate prompts

* Fix dismiss time

* Fix default kdf setting

* Fix build

* Undo changes

* Fix test

* Fix prettier

* Fix test

* Update libs/angular/src/key-management/encrypted-migration/encrypted-migrations-scheduler.service.ts

Co-authored-by: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com>

* Update libs/common/src/key-management/master-password/abstractions/master-password.service.abstraction.ts

Co-authored-by: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com>

* Update libs/angular/src/key-management/encrypted-migration/encrypted-migrations-scheduler.service.ts

Co-authored-by: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com>

* Only sync when there is at least one migration

* Relative imports

* Add tech debt comment

* Resolve inconsistent prefix

* Clean up

* Update docs

* Use default PBKDF2 iteratinos instead of custom threshold

* Undo type check

* Fix build

* Add comment

* Cleanup

* Cleanup

* Address component feedback

* Use isnullorwhitespace

* Fix tests

* Allow migration only on vault

* Fix tests

* Run prettier

* Fix tests

* Prevent await race condition

* Fix min and default values in kdf migration

* Run sync only when a migration was run

* Update libs/common/src/key-management/encrypted-migrator/default-encrypted-migrator.ts

Co-authored-by: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com>

* Fix link not being blue

* Fix later button on browser

---------

Co-authored-by: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com>
This commit is contained in:
Bernd Schoolmann
2025-12-03 19:04:18 +01:00
committed by jaasen-livefront
parent 28ac2d2f86
commit 37b8df2e18
48 changed files with 1471 additions and 31 deletions

View File

@@ -1,6 +1,7 @@
import { MockProxy, mock } from "jest-mock-extended";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { EncryptedMigrator } from "@bitwarden/common/key-management/encrypted-migrator/encrypted-migrator.abstraction";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { SyncService } from "@bitwarden/common/platform/sync";
import { UserId } from "@bitwarden/common/types/guid";
@@ -19,6 +20,7 @@ describe("DefaultLoginSuccessHandlerService", () => {
let ssoLoginService: MockProxy<SsoLoginServiceAbstraction>;
let syncService: MockProxy<SyncService>;
let userAsymmetricKeysRegenerationService: MockProxy<UserAsymmetricKeysRegenerationService>;
let encryptedMigrator: MockProxy<EncryptedMigrator>;
let logService: MockProxy<LogService>;
const userId = "USER_ID" as UserId;
@@ -30,6 +32,7 @@ describe("DefaultLoginSuccessHandlerService", () => {
ssoLoginService = mock<SsoLoginServiceAbstraction>();
syncService = mock<SyncService>();
userAsymmetricKeysRegenerationService = mock<UserAsymmetricKeysRegenerationService>();
encryptedMigrator = mock<EncryptedMigrator>();
logService = mock<LogService>();
service = new DefaultLoginSuccessHandlerService(
@@ -38,6 +41,7 @@ describe("DefaultLoginSuccessHandlerService", () => {
ssoLoginService,
syncService,
userAsymmetricKeysRegenerationService,
encryptedMigrator,
logService,
);
@@ -50,7 +54,7 @@ describe("DefaultLoginSuccessHandlerService", () => {
describe("run", () => {
it("should call required services on successful login", async () => {
await service.run(userId);
await service.run(userId, null);
expect(syncService.fullSync).toHaveBeenCalledWith(true, { skipTokenRefresh: true });
expect(userAsymmetricKeysRegenerationService.regenerateIfNeeded).toHaveBeenCalledWith(userId);
@@ -58,7 +62,7 @@ describe("DefaultLoginSuccessHandlerService", () => {
});
it("should get SSO email", async () => {
await service.run(userId);
await service.run(userId, null);
expect(ssoLoginService.getSsoEmail).toHaveBeenCalled();
});
@@ -68,8 +72,8 @@ describe("DefaultLoginSuccessHandlerService", () => {
ssoLoginService.getSsoEmail.mockResolvedValue(null);
});
it("should log error and return early", async () => {
await service.run(userId);
it("should not check SSO requirements", async () => {
await service.run(userId, null);
expect(logService.debug).toHaveBeenCalledWith("SSO login email not found.");
expect(ssoLoginService.updateSsoRequiredCache).not.toHaveBeenCalled();
@@ -82,7 +86,7 @@ describe("DefaultLoginSuccessHandlerService", () => {
});
it("should call updateSsoRequiredCache() and clearSsoEmail()", async () => {
await service.run(userId);
await service.run(userId, null);
expect(ssoLoginService.updateSsoRequiredCache).toHaveBeenCalledWith(testEmail, userId);
expect(ssoLoginService.clearSsoEmail).toHaveBeenCalled();

View File

@@ -1,4 +1,5 @@
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { EncryptedMigrator } from "@bitwarden/common/key-management/encrypted-migrator/encrypted-migrator.abstraction";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { SyncService } from "@bitwarden/common/platform/sync";
import { UserId } from "@bitwarden/common/types/guid";
@@ -15,12 +16,19 @@ export class DefaultLoginSuccessHandlerService implements LoginSuccessHandlerSer
private ssoLoginService: SsoLoginServiceAbstraction,
private syncService: SyncService,
private userAsymmetricKeysRegenerationService: UserAsymmetricKeysRegenerationService,
private encryptedMigrator: EncryptedMigrator,
private logService: LogService,
) {}
async run(userId: UserId): Promise<void> {
async run(userId: UserId, masterPassword: string | null): Promise<void> {
await this.syncService.fullSync(true, { skipTokenRefresh: true });
await this.userAsymmetricKeysRegenerationService.regenerateIfNeeded(userId);
await this.loginEmailService.clearLoginEmail();
try {
await this.encryptedMigrator.runMigrations(userId, masterPassword);
} catch {
// Don't block login success on migration failure
}
const ssoLoginEmail = await this.ssoLoginService.getSsoEmail();