1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 08:13:42 +00:00

[PM-5537] Persist require password on startup through logout (#7825)

* Persist require password on startup through logout

* Test new methods
This commit is contained in:
Matt Gibson
2024-02-07 10:39:54 -05:00
committed by GitHub
parent 0eb9e760aa
commit 2ca34b46db
10 changed files with 161 additions and 121 deletions

View File

@@ -4,7 +4,7 @@ import { UserId } from "../../types/guid";
import { EncryptedString, EncString } from "../models/domain/enc-string";
import { ActiveUserState, StateProvider } from "../state";
import { ENCRYPTED_CLIENT_KEY_HALF } from "./biometric.state";
import { ENCRYPTED_CLIENT_KEY_HALF, REQUIRE_PASSWORD_ON_START } from "./biometric.state";
export abstract class BiometricStateService {
/**
@@ -21,27 +21,60 @@ export abstract class BiometricStateService {
*/
requirePasswordOnStart$: Observable<boolean>;
abstract setEncryptedClientKeyHalf(encryptedKeyHalf: EncString): Promise<void>;
/**
* Updates the require password on start state for the currently active user.
*
* If false, the encrypted client key half will be removed.
* @param value whether or not a password is required on first unlock after opening the application
*/
abstract setRequirePasswordOnStart(value: boolean): Promise<void>;
abstract setEncryptedClientKeyHalf(encryptedKeyHalf: EncString, userId?: UserId): Promise<void>;
abstract getEncryptedClientKeyHalf(userId: UserId): Promise<EncString>;
abstract getRequirePasswordOnStart(userId: UserId): Promise<boolean>;
abstract removeEncryptedClientKeyHalf(userId: UserId): Promise<void>;
}
export class DefaultBiometricStateService implements BiometricStateService {
private requirePasswordOnStartState: ActiveUserState<boolean>;
private encryptedClientKeyHalfState: ActiveUserState<EncryptedString | undefined>;
encryptedClientKeyHalf$: Observable<EncString | undefined>;
requirePasswordOnStart$: Observable<boolean>;
constructor(private stateProvider: StateProvider) {
this.requirePasswordOnStartState = this.stateProvider.getActive(REQUIRE_PASSWORD_ON_START);
this.requirePasswordOnStart$ = this.requirePasswordOnStartState.state$.pipe(
map((value) => !!value),
);
this.encryptedClientKeyHalfState = this.stateProvider.getActive(ENCRYPTED_CLIENT_KEY_HALF);
this.encryptedClientKeyHalf$ = this.encryptedClientKeyHalfState.state$.pipe(
map(encryptedClientKeyHalfToEncString),
);
this.requirePasswordOnStart$ = this.encryptedClientKeyHalf$.pipe(map((keyHalf) => !!keyHalf));
}
async setEncryptedClientKeyHalf(encryptedKeyHalf: EncString): Promise<void> {
await this.encryptedClientKeyHalfState.update(() => encryptedKeyHalf?.encryptedString ?? null);
async setRequirePasswordOnStart(value: boolean): Promise<void> {
let currentActiveId: UserId;
await this.requirePasswordOnStartState.update(
(_, [userId]) => {
currentActiveId = userId;
return value;
},
{
combineLatestWith: this.requirePasswordOnStartState.combinedState$,
},
);
if (!value) {
await this.removeEncryptedClientKeyHalf(currentActiveId);
}
}
async setEncryptedClientKeyHalf(encryptedKeyHalf: EncString, userId?: UserId): Promise<void> {
const value = encryptedKeyHalf?.encryptedString ?? null;
if (userId) {
await this.stateProvider.getUser(userId, ENCRYPTED_CLIENT_KEY_HALF).update(() => value);
} else {
await this.encryptedClientKeyHalfState.update(() => value);
}
}
async removeEncryptedClientKeyHalf(userId: UserId): Promise<void> {
@@ -49,10 +82,9 @@ export class DefaultBiometricStateService implements BiometricStateService {
}
async getRequirePasswordOnStart(userId: UserId): Promise<boolean> {
if (userId == null) {
return false;
}
return !!(await this.getEncryptedClientKeyHalf(userId));
return !!(await firstValueFrom(
this.stateProvider.getUser(userId, REQUIRE_PASSWORD_ON_START).state$,
));
}
async getEncryptedClientKeyHalf(userId: UserId): Promise<EncString> {