mirror of
https://github.com/bitwarden/browser
synced 2025-12-14 23:33:31 +00:00
* 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
145 lines
5.9 KiB
TypeScript
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;
|
|
}
|
|
}
|