Skip to content

Commit

Permalink
[PM-3726] Force migration of legacy user's encryption key (#6195)
Browse files Browse the repository at this point in the history
* [PM-3726] migrate legacy user's encryption key

* [PM-3726] add 2fa support and pr feedback

* [PM-3726] revert launch.json & webpack.config changes

* [PM-3726] remove update key component
- also remove card in vault since legacy users can't login

* [PM-3726] Fix i18n & PR feedback

* [PM-3726] make standalone component

* [PM-3726] linter

* [PM-3726] missing await

* [PM-3726] logout legacy users with vault timeout to never

* [PM-3726] add await

* [PM-3726] skip auto key migration for legacy users

* [PM-3726] pr feedback

* [PM-3726] move check for web into migrate method

---------

Co-authored-by: Jared Snider <[email protected]>
(cherry picked from commit 8c06508)
  • Loading branch information
jlf0dev authored and trmartin4 committed Sep 21, 2023
1 parent f6f2e76 commit dec5aa7
Show file tree
Hide file tree
Showing 30 changed files with 834 additions and 273 deletions.
4 changes: 2 additions & 2 deletions apps/browser/src/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -771,8 +771,8 @@
"featureUnavailable": {
"message": "Feature unavailable"
},
"updateKey": {
"message": "You cannot use this feature until you update your encryption key."
"encryptionKeyMigrationRequired": {
"message": "Encryption key migration required. Please login through the web vault to update your encryption key."
},
"premiumMembership": {
"message": "Premium membership"
Expand Down
5 changes: 5 additions & 0 deletions apps/cli/src/auth/commands/login.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ export class LoginCommand {
new PasswordLogInCredentials(email, password, null, twoFactor)
);
}
if (response.requiresEncryptionKeyMigration) {
return Response.error(
"Encryption key migration required. Please login through the web vault to update your encryption key."
);
}
if (response.captchaSiteKey) {
const credentials = new PasswordLogInCredentials(email, password);
const handledResponse = await this.handleCaptchaRequired(twoFactor, credentials);
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/src/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -475,8 +475,8 @@
"maxFileSize": {
"message": "Maximum file size is 500 MB."
},
"updateKey": {
"message": "You cannot use this feature until you update your encryption key."
"encryptionKeyMigrationRequired": {
"message": "Encryption key migration required. Please login through the web vault to update your encryption key."
},
"editedFolder": {
"message": "Folder saved"
Expand Down
9 changes: 9 additions & 0 deletions apps/web/src/app/auth/login/login.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { LoginService } from "@bitwarden/common/auth/abstractions/login.service";
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
Expand Down Expand Up @@ -210,4 +211,12 @@ export class LoginComponent extends BaseLoginComponent implements OnInit, OnDest
}
await super.submit(false);
}

protected override handleMigrateEncryptionKey(result: AuthResult): boolean {
if (!result.requiresEncryptionKeyMigration) {
return false;
}
this.router.navigate(["migrate-legacy-encryption"]);
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<form [formGroup]="formGroup" [bitSubmit]="submit">
<div class="tw-mt-12 tw-flex tw-justify-center">
<div class="tw-max-w-xl">
<h1 bitTypography="h1" class="tw-mb-4 tw-text-center">{{ "updateEncryptionKey" | i18n }}</h1>
<div
class="tw-block tw-rounded tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-p-8"
>
<p>
{{ "updateEncryptionSchemeDesc" | i18n }}
<a
href="https://bitwarden.com/help/account-encryption-key/#rotate-your-encryption-key"
target="_blank"
rel="noopener"
>{{ "learnMore" | i18n }}</a
>
</p>
<bit-callout type="warning">{{ "updateEncryptionKeyWarning" | i18n }}</bit-callout>

<bit-form-field>
<bit-label>{{ "masterPass" | i18n }}</bit-label>
<input
id="masterPassword"
bitInput
type="password"
formControlName="masterPassword"
appAutofocus
/>
<button type="button" bitIconButton bitSuffix bitPasswordInputToggle></button>
</bit-form-field>
<button type="submit" bitButton bitFormButton buttonType="primary" block>
{{ "updateEncryptionKey" | i18n }}
</button>
</div>
</div>
</div>
</form>
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Component } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";

import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";

import { SharedModule } from "../../shared";

import { MigrateFromLegacyEncryptionService } from "./migrate-legacy-encryption.service";

// The master key was originally used to encrypt user data, before the user key was introduced.
// This component is used to migrate from the old encryption scheme to the new one.
@Component({
standalone: true,
imports: [SharedModule],
providers: [MigrateFromLegacyEncryptionService],
templateUrl: "migrate-legacy-encryption.component.html",
})
export class MigrateFromLegacyEncryptionComponent {
protected formGroup = new FormGroup({
masterPassword: new FormControl("", [Validators.required]),
});

constructor(
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
private migrationService: MigrateFromLegacyEncryptionService,
private cryptoService: CryptoService,
private messagingService: MessagingService,
private logService: LogService
) {}

submit = async () => {
this.formGroup.markAsTouched();

if (this.formGroup.invalid) {
return;
}

const hasUserKey = await this.cryptoService.hasUserKey();
if (hasUserKey) {
this.messagingService.send("logout");
throw new Error("User key already exists, cannot migrate legacy encryption.");
}

const masterPassword = this.formGroup.value.masterPassword;

try {
// Create new user key
const [newUserKey, masterKeyEncUserKey] = await this.migrationService.createNewUserKey(
masterPassword
);

// Update admin recover keys
await this.migrationService.updateAllAdminRecoveryKeys(masterPassword, newUserKey);

// Update emergency access
await this.migrationService.updateEmergencyAccesses(newUserKey);

// Update keys, folders, ciphers, and sends
await this.migrationService.updateKeysAndEncryptedData(
masterPassword,
newUserKey,
masterKeyEncUserKey
);

this.platformUtilsService.showToast(
"success",
this.i18nService.t("keyUpdated"),
this.i18nService.t("logBackInOthersToo"),
{ timeout: 15000 }
);
this.messagingService.send("logout");
} catch (e) {
this.logService.error(e);
throw e;
}
};
}
Loading

0 comments on commit dec5aa7

Please sign in to comment.