1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-14 23:33:31 +00:00
Files
browser/apps/cli/src/auth/commands/unlock.command.ts
Jake Fink 9d10825dbd [PM-5362] Add MP Service (attempt #2) (#8619)
* create mp and kdf service

* update mp service interface to not rely on active user

* rename observable methods

* update crypto service with new MP service

* add master password service to login strategies
- make fake service for easier testing
- fix crypto service tests

* update auth service and finish strategies

* auth request refactors

* more service refactors and constructor updates

* setMasterKey refactors

* remove master key methods from crypto service

* remove master key and hash from state service

* missed fixes

* create migrations and fix references

* fix master key imports

* default force set password reason to none

* add password reset reason observable factory to service

* remove kdf changes and migrate only disk data

* update migration number

* fix sync service deps

* use disk for force set password state

* fix desktop migration

* fix sso test

* fix tests

* fix more tests

* fix even more tests

* fix even more tests

* fix cli

* remove kdf service abstraction

* add missing deps for browser

* fix merge conflicts

* clear reset password reason on lock or logout

* fix tests

* fix other tests

* add jsdocs to abstraction

* use state provider in crypto service

* inverse master password service factory

* add clearOn to master password service

* add parameter validation to master password service

* add component level userId

* add missed userId

* migrate key hash

* fix login strategy service

* delete crypto master key from account

* migrate master key encrypted user key

* rename key hash to master key hash

* use mp service for getMasterKeyEncryptedUserKey

* fix tests

* fix user key decryption logic

* add clear methods to mp service

* fix circular dep and encryption issue

* fix test

* remove extra account service call

* use EncString in state provider

* fix tests

* return to using encrypted string for serialization
2024-04-09 20:50:20 -04:00

145 lines
5.9 KiB
TypeScript

import { firstValueFrom } from "rxjs";
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 { KeyConnectorService } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService } from "@bitwarden/common/platform/abstractions/crypto.service";
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { HashPurpose } from "@bitwarden/common/platform/enums";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { ConvertToKeyConnectorCommand } from "../../commands/convert-to-key-connector.command";
import { Response } from "../../models/response";
import { MessageResponse } from "../../models/response/message.response";
import { CliUtils } from "../../utils";
export class UnlockCommand {
constructor(
private accountService: AccountService,
private masterPasswordService: InternalMasterPasswordServiceAbstraction,
private cryptoService: CryptoService,
private stateService: StateService,
private cryptoFunctionService: CryptoFunctionService,
private apiService: ApiService,
private logService: ConsoleLogService,
private keyConnectorService: KeyConnectorService,
private environmentService: EnvironmentService,
private syncService: SyncService,
private organizationApiService: OrganizationApiServiceAbstraction,
private logout: () => Promise<void>,
) {}
async run(password: string, cmdOptions: Record<string, any>) {
const normalizedOptions = new Options(cmdOptions);
const passwordResult = await CliUtils.getPassword(password, normalizedOptions, this.logService);
if (passwordResult instanceof Response) {
return passwordResult;
} else {
password = passwordResult;
}
await this.setNewSessionKey();
const email = await this.stateService.getEmail();
const kdf = await this.stateService.getKdfType();
const kdfConfig = await this.stateService.getKdfConfig();
const masterKey = await this.cryptoService.makeMasterKey(password, email, kdf, kdfConfig);
const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id;
const storedMasterKeyHash = await firstValueFrom(
this.masterPasswordService.masterKeyHash$(userId),
);
let passwordValid = false;
if (masterKey != null) {
if (storedMasterKeyHash != null) {
passwordValid = await this.cryptoService.compareAndUpdateKeyHash(password, masterKey);
} else {
const serverKeyHash = await this.cryptoService.hashMasterKey(
password,
masterKey,
HashPurpose.ServerAuthorization,
);
const request = new SecretVerificationRequest();
request.masterPasswordHash = serverKeyHash;
try {
await this.apiService.postAccountVerifyPassword(request);
passwordValid = true;
const localKeyHash = await this.cryptoService.hashMasterKey(
password,
masterKey,
HashPurpose.LocalAuthorization,
);
await this.masterPasswordService.setMasterKeyHash(localKeyHash, userId);
} catch {
// Ignore
}
}
}
if (passwordValid) {
await this.masterPasswordService.setMasterKey(masterKey, userId);
const userKey = await this.cryptoService.decryptUserKeyWithMasterKey(masterKey);
await this.cryptoService.setUserKey(userKey);
if (await this.keyConnectorService.getConvertAccountRequired()) {
const convertToKeyConnectorCommand = new ConvertToKeyConnectorCommand(
this.keyConnectorService,
this.environmentService,
this.syncService,
this.organizationApiService,
this.logout,
);
const convertResponse = await convertToKeyConnectorCommand.run();
if (!convertResponse.success) {
return convertResponse;
}
}
return this.successResponse();
} else {
return Response.error("Invalid master password.");
}
}
private async setNewSessionKey() {
const key = await this.cryptoFunctionService.randomBytes(64);
process.env.BW_SESSION = Utils.fromBufferToB64(key);
}
private async successResponse() {
const res = new MessageResponse(
"Your vault is now unlocked!",
"\n" +
"To unlock your vault, set your session key to the `BW_SESSION` environment variable. ex:\n" +
'$ export BW_SESSION="' +
process.env.BW_SESSION +
'"\n' +
'> $env:BW_SESSION="' +
process.env.BW_SESSION +
'"\n\n' +
"You can also pass the session key to any command with the `--session` option. ex:\n" +
"$ bw list items --session " +
process.env.BW_SESSION,
);
res.raw = process.env.BW_SESSION;
return Response.success(res);
}
}
class Options {
passwordEnv: string;
passwordFile: string;
constructor(passedOptions: Record<string, any>) {
this.passwordEnv = passedOptions?.passwordenv || passedOptions?.passwordEnv;
this.passwordFile = passedOptions?.passwordfile || passedOptions?.passwordFile;
}
}