1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-08 12:40:26 +00:00
This commit is contained in:
Bernd Schoolmann
2025-05-17 18:43:11 +02:00
parent 6e4e82fafe
commit a735faeac7
8 changed files with 85 additions and 29 deletions

View File

@@ -2162,7 +2162,16 @@
"message": "Too many invalid PIN entry attempts. Logging out."
},
"syncUnlockWithDesktop": {
"message": "Synchronize unlock state with desktop app."
"message": "Unlock with desktop integration"
},
"lockScreenSynchronizedNote": {
"message": "This user's account is synchronized with the desktop app."
},
"lockScreenContinueInDesktop": {
"message": "Continue in desktop app"
},
"lockScreenDesktopNotRunning": {
"message": "This user's account is synchronized with the desktop app, but the desktop app is not running."
},
"unlockWithBiometrics": {
"message": "Unlock with biometrics"

View File

@@ -11,7 +11,7 @@
<h2 bitTypography="h6">{{ "unlockMethods" | i18n }}</h2>
</bit-section-header>
<bit-card>
<bit-form-control [disableMargin]="this.form.value.syncUnlockWithDesktop">
<bit-form-control>
<input
bitCheckbox
id="syncUnlockWithDesktop"
@@ -24,7 +24,6 @@
</bit-form-control>
<bit-form-control
[disableMargin]="!((pinEnabled$ | async) || this.form.value.pin)"
*ngIf="!this.form.value.syncUnlockWithDesktop"
>
<input bitCheckbox id="biometric" type="checkbox" formControlName="biometric" />
<bit-label for="biometric" class="tw-whitespace-normal">{{
@@ -37,7 +36,7 @@
<bit-form-control
class="tw-pl-5"
[disableMargin]="!((pinEnabled$ | async) || this.form.value.pin)"
*ngIf="this.form.value.biometric && !this.form.value.syncUnlockWithDesktop"
*ngIf="this.form.value.biometric"
>
<input
bitCheckbox
@@ -52,7 +51,7 @@
<bit-form-control
[disableMargin]="!(this.form.value.pin && showMasterPasswordOnClientRestartOption)"
*ngIf="
((pinEnabled$ | async) || this.form.value.pin) && !this.form.value.syncUnlockWithDesktop
((pinEnabled$ | async) || this.form.value.pin)
"
>
<input bitCheckbox id="pin" type="checkbox" formControlName="pin" />
@@ -63,8 +62,7 @@
disableMargin
*ngIf="
this.form.value.pin &&
showMasterPasswordOnClientRestartOption &&
!this.form.value.syncUnlockWithDesktop
showMasterPasswordOnClientRestartOption
"
>
<input
@@ -89,12 +87,12 @@
<auth-vault-timeout-input
[vaultTimeoutOptions]="vaultTimeoutOptions"
[formControl]="form.controls.vaultTimeout"
ngDefaultControl
*ngIf="!this.form.value.syncUnlockWithDesktop"
[disabled]="form.controls.syncUnlockWithDesktop.value"
ngDefaultControl
>
</auth-vault-timeout-input>
<bit-form-field disableMargin *ngIf="!this.form.value.syncUnlockWithDesktop">
<bit-form-field disableMargin>
<bit-label for="vaultTimeoutAction">{{ "vaultTimeoutAction1" | i18n }}</bit-label>
<bit-select id="vaultTimeoutAction" formControlName="vaultTimeoutAction">
<bit-option
@@ -111,14 +109,11 @@
</bit-form-field>
<bit-hint
*ngIf="hasVaultTimeoutPolicy && !this.form.value.syncUnlockWithDesktop"
*ngIf="hasVaultTimeoutPolicy"
class="tw-mt-4"
>
{{ "vaultTimeoutPolicyAffectingOptions" | i18n }}
</bit-hint>
<bit-hint *ngIf="this.form.value.syncUnlockWithDesktop">
The desktop app's vault timeout settings will be used to lock the vault on this device.
</bit-hint>
</bit-card>
</bit-section>

View File

@@ -9,7 +9,6 @@ import {
combineLatest,
concatMap,
distinctUntilChanged,
filter,
firstValueFrom,
map,
Observable,
@@ -250,11 +249,35 @@ export class AccountSecurityComponent implements OnInit, OnDestroy {
takeUntil(this.destroy$),
)
.subscribe();
this.syncedUnlockStateService.syncedUnlockEnabled$
.pipe(
map((enabled) => {
if (enabled) {
this.form.controls.pin.disable({ emitEvent: false });
this.form.controls.pinLockWithMasterPassword.disable({ emitEvent: false });
this.form.controls.biometric.disable({ emitEvent: false });
this.form.controls.enableAutoBiometricsPrompt.disable({ emitEvent: false });
this.form.controls.vaultTimeoutAction.disable({ emitEvent: false });
} else {
this.form.controls.pin.enable({ emitEvent: false });
this.form.controls.pinLockWithMasterPassword.enable({ emitEvent: false });
this.form.controls.biometric.enable({ emitEvent: false });
this.form.controls.enableAutoBiometricsPrompt.enable({ emitEvent: false });
this.form.controls.vaultTimeoutAction.enable({ emitEvent: false });
}
}),
takeUntil(this.destroy$),
)
.subscribe();
timer(0, 1000)
.pipe(
filter(() => !this.form.controls.syncUnlockWithDesktop.value),
switchMap(async () => {
if (this.form.controls.syncUnlockWithDesktop.value) {
this.form.controls.biometric.disable({ emitEvent: false });
return;
}
const status = await this.biometricsService.getBiometricsStatusForUser(activeAccount.id);
const biometricSettingAvailable = await this.biometricsService.canEnableBiometricUnlock();
if (!biometricSettingAvailable) {
@@ -394,6 +417,11 @@ export class AccountSecurityComponent implements OnInit, OnDestroy {
takeUntil(this.destroy$),
)
.subscribe(([availableActions, policy]) => {
if (this.form.controls.syncUnlockWithDesktop.value) {
this.form.controls.vaultTimeoutAction.disable({ emitEvent: false });
return;
}
if (policy?.data?.action || availableActions.length <= 1) {
this.form.controls.vaultTimeoutAction.disable({ emitEvent: false });
} else {
@@ -403,6 +431,10 @@ export class AccountSecurityComponent implements OnInit, OnDestroy {
}
async saveVaultTimeout(previousValue: VaultTimeout, newValue: VaultTimeout) {
if (newValue == null) {
return;
}
if (newValue === VaultTimeoutStringType.Never) {
const confirmed = await this.dialogService.openSimpleDialog({
title: { key: "warning" },

View File

@@ -5,7 +5,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { SyncedUnlockService } from "@bitwarden/common/key-management/synced-unlock/abstractions/synced-unlock.service";
import { VaultTimeoutService } from "@bitwarden/common/key-management/vault-timeout";
import { VaultTimeoutService } from "@bitwarden/common/key-management/vault-timeout/services/vault-timeout.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key";
import { UserId } from "@bitwarden/common/types/guid";
@@ -33,7 +33,10 @@ export class BackgroundSyncedUnlockService extends SyncedUnlockService {
timer(0, 1000)
.pipe(
concatMap(async () => {
if (this.nativeMessagingBackground().connected) {
const isConnected = await this.isConnected();
// Needed to resolve dependency cycle
this.vaultTimeoutService.setDesktopAppConnected(isConnected);
if (isConnected) {
if (!(await firstValueFrom(this.syncedUnlockStateService.syncedUnlockEnabled$))) {
return;
}

View File

@@ -14,7 +14,7 @@ import {
ValidationErrors,
Validator,
} from "@angular/forms";
import { filter, map, Observable, Subject, switchMap, takeUntil } from "rxjs";
import { filter, map, Observable, Subject, switchMap, take, takeUntil } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
@@ -110,6 +110,7 @@ export class VaultTimeoutInputComponent
});
@Input() vaultTimeoutOptions: VaultTimeoutOption[];
@Input() disabled: boolean;
vaultTimeoutPolicy: Policy;
vaultTimeoutPolicyHours: number;
@@ -189,6 +190,12 @@ export class VaultTimeoutInputComponent
}
ngOnChanges() {
if (this.disabled) {
this.form.disable();
} else {
this.form.enable();
}
if (
!this.vaultTimeoutOptions.find((p) => p.value === VaultTimeoutInputComponent.CUSTOM_VALUE)
) {

View File

@@ -21,12 +21,14 @@ import { UserId } from "../../../types/guid";
import { CipherService } from "../../../vault/abstractions/cipher.service";
import { FolderService } from "../../../vault/abstractions/folder/folder.service.abstraction";
import { InternalMasterPasswordServiceAbstraction } from "../../master-password/abstractions/master-password.service.abstraction";
import { SyncedUnlockService } from "../../synced-unlock/abstractions/synced-unlock.service";
import { VaultTimeoutSettingsService } from "../abstractions/vault-timeout-settings.service";
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "../abstractions/vault-timeout.service";
import { VaultTimeoutAction } from "../enums/vault-timeout-action.enum";
export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
private inited = false;
private isDesktopAppConnected = false;
constructor(
private accountService: AccountService,
@@ -44,7 +46,7 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
private taskSchedulerService: TaskSchedulerService,
protected logService: LogService,
private biometricService: BiometricsService,
private syncedUnlockService: SyncedUnlockStateServiceAbstraction,
private syncedUnlockStateService: SyncedUnlockStateServiceAbstraction,
private lockedCallback: (userId?: string) => Promise<void> = null,
private loggedOutCallback: (
logoutReason: LogoutReason,
@@ -76,10 +78,16 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction {
);
}
// This is needed to prevent dependency cycle between vault timeout and synced unlock service
setDesktopAppConnected(isConnected: boolean): void {
this.isDesktopAppConnected = isConnected;
}
async checkVaultTimeout(): Promise<void> {
if (
(await firstValueFrom(this.syncedUnlockService.syncedUnlockEnabled$)) &&
this.platformUtilsService.getClientType() === ClientType.Browser
(await firstValueFrom(this.syncedUnlockStateService.syncedUnlockEnabled$)) &&
this.platformUtilsService.getClientType() === ClientType.Browser &&
this.isDesktopAppConnected
) {
return;
}

View File

@@ -8,18 +8,17 @@
<ng-container *ngIf="!showLocalUnlockOptions">
<form [bitSubmit]="submit" [formGroup]="desktopUnlockFormGroup">
<div class="tw-flex tw-flex-col tw-space-y-3">
<bit-hint class="tw-text-center"
>This user's account is synchronized with the desktop app.</bit-hint
>
<bit-hint class="tw-text-center">{{ "lockScreenSynchronizedNote" | i18n }}</bit-hint
>
<button type="submit" bitButton bitFormButton buttonType="primary" block>
Continue in desktop app
{{ "lockScreenContinueInDesktop" | i18n }}
</button>
</div>
</form>
</ng-container>
<ng-container *ngIf="showLocalUnlockOptions">
<bit-hint class="tw-text-center" *ngIf="unlockViaDesktop">
This account has unlock synchronization enabled, but the desktop app is not running.
<bit-hint class="tw-text-center tw-mb-3" *ngIf="unlockViaDesktop">
{{ "lockScreenDesktopNotRunning" | i18n }}
</bit-hint>
<!-- Biometrics Unlock -->

View File

@@ -138,7 +138,7 @@ export class LockComponent implements OnInit, OnDestroy {
unlockViaDesktop = false;
isDesktopOpen = false;
showLocalUnlockOptions = false;
showLocalUnlockOptions = true;
desktopUnlockFormGroup: FormGroup = new FormGroup({});
constructor(
@@ -189,6 +189,9 @@ export class LockComponent implements OnInit, OnDestroy {
await this.desktopOnInit();
} else if (this.clientType === ClientType.Browser) {
this.biometricUnlockBtnText = this.lockComponentService.getBiometricsUnlockBtnText();
if (await firstValueFrom(this.syncedUnlockStateService.syncedUnlockEnabled$)) {
this.showLocalUnlockOptions = false;
}
}
}