1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-10 05:30:01 +00:00

Merge branch 'main' into auth/pm-9115/implement-view-data-persistence-in-2FA-flows

This commit is contained in:
Alec Rippberger
2025-03-18 15:46:38 -05:00
committed by GitHub
54 changed files with 290 additions and 231 deletions

3
.github/codecov.yml vendored
View File

@@ -12,7 +12,7 @@ component_management:
paths:
- apps/browser/src/key-management/biometrics/**
- apps/cli/src/key-management/cli-biometrics-service.ts
- apps/desktop/destkop_native/core/src/biometric/**
- apps/desktop/desktop_native/core/src/biometric/**
- apps/desktop/src/key-management/biometrics/**
- apps/desktop/src/services/biometric-message-handler.service.ts
- apps/web/src/app/key-management/web-biometric.service.ts
@@ -24,6 +24,7 @@ component_management:
- apps/desktop/src/key-management/lock/**
- apps/web/src/app/key-management/lock/**
- libs/key-management-ui/src/lock/**
- libs/common/src/key-management/device-trust/**
- component_id: key-management-ipc
name: Key Management - IPC
paths:

View File

@@ -1,5 +1,5 @@
import { css } from "@emotion/css";
import { html } from "lit";
import { html, nothing } from "lit";
import { Theme } from "@bitwarden/common/platform/enums";
@@ -8,7 +8,6 @@ import {
NotificationTypes,
} from "../../../notification/abstractions/notification-bar";
import { spacing, themes } from "../constants/styles";
import { ActionRow } from "../rows/action-row";
import { ButtonRow } from "../rows/button-row";
export function NotificationFooter({
@@ -23,18 +22,13 @@ export function NotificationFooter({
theme: Theme;
}) {
const isChangeNotification = notificationType === NotificationTypes.Change;
const saveNewItemText = i18n.saveAsNewLoginAction;
const buttonText = i18n.saveAction;
return html`
<div class=${notificationFooterStyles({ theme })}>
${isChangeNotification
? ActionRow({
itemText: saveNewItemText,
handleAction: handleSaveAction,
theme,
})
: ButtonRow({ theme, buttonAction: handleSaveAction, buttonText })}
${!isChangeNotification
? ButtonRow({ theme, buttonAction: handleSaveAction, buttonText })
: nothing}
</div>
`;
}
@@ -46,5 +40,6 @@ const notificationFooterStyles = ({ theme }: { theme: Theme }) => css`
:last-child {
border-radius: 0 0 ${spacing["4"]} ${spacing["4"]};
padding-bottom: ${spacing[4]};
}
`;

View File

@@ -57,6 +57,7 @@ const buttonRowStyles = css`
width: 100%;
max-height: 52px;
white-space: nowrap;
padding-top: ${spacing[1]};
> button {
max-width: min-content;

View File

@@ -76,5 +76,8 @@
>
{{ "save" | i18n }}
</button>
<button (click)="goBack()" bitButton type="button" buttonType="secondary">
{{ "cancel" | i18n }}
</button>
</popup-footer>
</popup-page>

View File

@@ -39,6 +39,7 @@ import { PopOutComponent } from "../../../platform/popup/components/pop-out.comp
import { PopupFooterComponent } from "../../../platform/popup/layout/popup-footer.component";
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
import { PopupRouterCacheService } from "../../../platform/popup/view-cache/popup-router-cache.service";
@Component({
selector: "app-blocked-domains",
@@ -88,6 +89,7 @@ export class BlockedDomainsComponent implements AfterViewInit, OnDestroy {
private i18nService: I18nService,
private toastService: ToastService,
private formBuilder: FormBuilder,
private popupRouterCacheService: PopupRouterCacheService,
) {}
get domainForms() {
@@ -220,6 +222,10 @@ export class BlockedDomainsComponent implements AfterViewInit, OnDestroy {
}
}
async goBack() {
await this.popupRouterCacheService.back();
}
trackByFunction(index: number, _: string) {
return index;
}

View File

@@ -72,5 +72,8 @@
>
{{ "save" | i18n }}
</button>
<button (click)="goBack()" bitButton type="button" buttonType="secondary">
{{ "cancel" | i18n }}
</button>
</popup-footer>
</popup-page>

View File

@@ -40,6 +40,7 @@ import { PopOutComponent } from "../../../platform/popup/components/pop-out.comp
import { PopupFooterComponent } from "../../../platform/popup/layout/popup-footer.component";
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
import { PopupRouterCacheService } from "../../../platform/popup/view-cache/popup-router-cache.service";
@Component({
selector: "app-excluded-domains",
@@ -90,6 +91,7 @@ export class ExcludedDomainsComponent implements AfterViewInit, OnDestroy {
private i18nService: I18nService,
private toastService: ToastService,
private formBuilder: FormBuilder,
private popupRouterCacheService: PopupRouterCacheService,
) {
this.accountSwitcherEnabled = enableAccountSwitching();
}
@@ -150,6 +152,10 @@ export class ExcludedDomainsComponent implements AfterViewInit, OnDestroy {
await this.fieldChange();
}
async goBack() {
await this.popupRouterCacheService.back();
}
async fieldChange() {
if (this.dataIsPristine) {
this.dataIsPristine = false;

View File

@@ -1,7 +1,16 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { filter, firstValueFrom, merge, Observable, ReplaySubject, scan, startWith } from "rxjs";
import { pairwise } from "rxjs/operators";
import {
filter,
firstValueFrom,
merge,
Observable,
ReplaySubject,
scan,
startWith,
timer,
} from "rxjs";
import { map, pairwise, share, takeUntil } from "rxjs/operators";
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
@@ -146,7 +155,19 @@ export default class AutofillService implements AutofillServiceInterface {
pageDetailsFallback$.next([]);
}
return merge(pageDetailsFromTab$, pageDetailsFallback$);
// Share the pageDetailsFromTab$ observable so that multiple subscribers don't trigger multiple executions.
const sharedPageDetailsFromTab$ = pageDetailsFromTab$.pipe(share());
// Create a timeout observable that emits an empty array if pageDetailsFromTab$ hasn't emitted within 1 second.
const pageDetailsTimeout$ = timer(1000).pipe(
map(() => []),
takeUntil(sharedPageDetailsFromTab$),
);
// Merge the responses so that if pageDetailsFromTab$ emits, that value is used.
// Otherwise, if it doesn't emit in time, the timeout observable emits an empty array.
// Also, pageDetailsFallback$ will emit in error cases.
return merge(sharedPageDetailsFromTab$, pageDetailsFallback$, pageDetailsTimeout$);
}
/**

View File

@@ -32,7 +32,6 @@ import { ProviderService } from "@bitwarden/common/admin-console/services/provid
import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
@@ -44,7 +43,6 @@ import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/for
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
import { DeviceTrustService } from "@bitwarden/common/auth/services/device-trust.service.implementation";
import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation";
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
import { SsoLoginService } from "@bitwarden/common/auth/services/sso-login.service";
@@ -78,6 +76,8 @@ import { BulkEncryptServiceImplementation } from "@bitwarden/common/key-manageme
import { EncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/encrypt.service.implementation";
import { FallbackBulkEncryptService } from "@bitwarden/common/key-management/crypto/services/fallback-bulk-encrypt.service";
import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/multithread-encrypt.service.implementation";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { DeviceTrustService } from "@bitwarden/common/key-management/device-trust/services/device-trust.service.implementation";
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/services/key-connector.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
@@ -267,6 +267,7 @@ import { OffscreenDocumentService } from "../platform/offscreen-document/abstrac
import { DefaultOffscreenDocumentService } from "../platform/offscreen-document/offscreen-document.service";
import { BrowserTaskSchedulerService } from "../platform/services/abstractions/browser-task-scheduler.service";
import { BrowserEnvironmentService } from "../platform/services/browser-environment.service";
import BrowserInitialInstallService from "../platform/services/browser-initial-install.service";
import BrowserLocalStorageService from "../platform/services/browser-local-storage.service";
import BrowserMemoryStorageService from "../platform/services/browser-memory-storage.service";
import { BrowserScriptInjectorService } from "../platform/services/browser-script-injector.service";
@@ -390,6 +391,7 @@ export default class MainBackground {
kdfConfigService: KdfConfigService;
offscreenDocumentService: OffscreenDocumentService;
syncServiceListener: SyncServiceListener;
browserInitialInstallService: BrowserInitialInstallService;
webPushConnectionService: WorkerWebPushConnectionService | UnsupportedWebPushConnectionService;
themeStateService: DefaultThemeStateService;
@@ -1043,6 +1045,8 @@ export default class MainBackground {
this.organizationVaultExportService,
);
this.browserInitialInstallService = new BrowserInitialInstallService(this.stateProvider);
if (BrowserApi.isManifestVersion(3)) {
const registration = (self as unknown as { registration: ServiceWorkerRegistration })
?.registration;
@@ -1146,6 +1150,7 @@ export default class MainBackground {
this.accountService,
lockService,
this.billingAccountProfileStateService,
this.browserInitialInstallService,
);
this.nativeMessagingBackground = new NativeMessagingBackground(
this.keyService,

View File

@@ -28,6 +28,7 @@ import { LockedVaultPendingNotificationsData } from "../autofill/background/abst
import { AutofillService } from "../autofill/services/abstractions/autofill.service";
import { BrowserApi } from "../platform/browser/browser-api";
import { BrowserEnvironmentService } from "../platform/services/browser-environment.service";
import BrowserInitialInstallService from "../platform/services/browser-initial-install.service";
import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service";
import MainBackground from "./main.background";
@@ -53,6 +54,7 @@ export default class RuntimeBackground {
private accountService: AccountService,
private readonly lockService: LockService,
private billingAccountProfileStateService: BillingAccountProfileStateService,
private browserInitialInstallService: BrowserInitialInstallService,
) {
// onInstalled listener must be wired up before anything else, so we do it in the ctor
chrome.runtime.onInstalled.addListener((details: any) => {
@@ -382,7 +384,10 @@ export default class RuntimeBackground {
void this.autofillService.loadAutofillScriptsOnInstall();
if (this.onInstalledReason != null) {
if (this.onInstalledReason === "install") {
if (
this.onInstalledReason === "install" &&
!(await firstValueFrom(this.browserInitialInstallService.extensionInstalled$))
) {
if (!devFlagEnabled("skipWelcomeOnInstall")) {
void BrowserApi.createNewTab("https://bitwarden.com/browser-start/");
}
@@ -394,6 +399,7 @@ export default class RuntimeBackground {
if (await this.environmentService.hasManagedEnvironment()) {
await this.environmentService.setUrlsToManagedEnvironment();
}
await this.browserInitialInstallService.setExtensionInstalled(true);
}
this.onInstalledReason = null;

View File

@@ -0,0 +1,31 @@
import { Observable, map } from "rxjs";
import {
GlobalState,
EXTENSION_INITIAL_INSTALL_DISK,
KeyDefinition,
StateProvider,
} from "@bitwarden/common/platform/state";
const EXTENSION_INSTALLED = new KeyDefinition<boolean>(
EXTENSION_INITIAL_INSTALL_DISK,
"extensionInstalled",
{
deserializer: (obj) => obj,
},
);
export default class BrowserInitialInstallService {
private extensionInstalled: GlobalState<boolean> =
this.stateProvider.getGlobal(EXTENSION_INSTALLED);
readonly extensionInstalled$: Observable<boolean> = this.extensionInstalled.state$.pipe(
map((x) => x ?? false),
);
constructor(private stateProvider: StateProvider) {}
async setExtensionInstalled(value: boolean) {
await this.extensionInstalled.update(() => value);
}
}

View File

@@ -2,7 +2,7 @@
<popup-header
slot="header"
[pageTitle]="headerText"
[backAction]="handleBackButton.bind(this)"
[backAction]="handleBackButton"
showBackButton
>
<app-pop-out slot="end" />
@@ -27,6 +27,10 @@
{{ "save" | i18n }}
</button>
<button (click)="handleBackButton()" bitButton type="button" buttonType="secondary">
{{ "cancel" | i18n }}
</button>
<button
slot="end"
*ngIf="canDeleteCipher$ | async"

View File

@@ -212,7 +212,7 @@ export class AddEditV2Component implements OnInit {
/**
* Handle back button
*/
async handleBackButton() {
handleBackButton = async () => {
if (this.inFido2PopoutWindow) {
this.popupCloseWarningService.disable();
BrowserFido2UserInterfaceSession.abortPopout(this.fido2PopoutSessionData.sessionId);
@@ -225,7 +225,7 @@ export class AddEditV2Component implements OnInit {
}
await this.popupRouterCacheService.back();
}
};
async onCipherSaved(cipher: CipherView) {
if (BrowserPopupUtils.inPopout(window)) {

View File

@@ -20,5 +20,8 @@
>
{{ "upload" | i18n }}
</button>
<button (click)="navigateBack()" bitButton type="button" buttonType="secondary">
{{ "cancel" | i18n }}
</button>
</popup-footer>
</popup-page>

View File

@@ -68,7 +68,7 @@
"chalk": "4.1.2",
"commander": "11.1.0",
"form-data": "4.0.1",
"https-proxy-agent": "7.0.5",
"https-proxy-agent": "7.0.6",
"inquirer": "8.2.6",
"jsdom": "26.0.0",
"jszip": "3.10.1",

View File

@@ -34,7 +34,6 @@ import { ProviderApiService } from "@bitwarden/common/admin-console/services/pro
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { MasterPasswordApiService as MasterPasswordApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction";
import {
@@ -43,7 +42,6 @@ import {
} from "@bitwarden/common/auth/services/account.service";
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
import { DeviceTrustService } from "@bitwarden/common/auth/services/device-trust.service.implementation";
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
import { MasterPasswordApiService } from "@bitwarden/common/auth/services/master-password/master-password-api.service.implementation";
import { TokenService } from "@bitwarden/common/auth/services/token.service";
@@ -63,6 +61,8 @@ import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/bill
import { ClientType } from "@bitwarden/common/enums";
import { EncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/encrypt.service.implementation";
import { FallbackBulkEncryptService } from "@bitwarden/common/key-management/crypto/services/fallback-bulk-encrypt.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { DeviceTrustService } from "@bitwarden/common/key-management/device-trust/services/device-trust.service.implementation";
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/services/key-connector.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import { MasterPasswordService } from "@bitwarden/common/key-management/master-password/services/master-password.service";

View File

@@ -493,12 +493,14 @@ export class VaultComponent implements OnInit, OnDestroy {
}
async savedCipher(cipher: CipherView) {
this.cipherId = cipher.id;
this.cipherId = null;
this.action = "view";
await this.vaultItemsComponent.refresh();
this.cipherId = cipher.id;
await this.cipherService.clearCache(this.activeUserId);
await this.viewComponent.load();
await this.vaultItemsComponent.load(this.activeFilter.buildFilter());
this.go();
await this.vaultItemsComponent.refresh();
}
async deletedCipher(cipher: CipherView) {
@@ -572,6 +574,8 @@ export class VaultComponent implements OnInit, OnDestroy {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.viewCipher(cipher);
await this.vaultItemsComponent.refresh();
await this.cipherService.clearCache(this.activeUserId);
await this.vaultItemsComponent.load(this.activeFilter.buildFilter());
});
// eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe
this.modal.onClosed.subscribe(async () => {

View File

@@ -1,4 +1,4 @@
FROM bitwarden/server
FROM ghcr.io/bitwarden/server
LABEL com.bitwarden.product="bitwarden"

View File

@@ -9,7 +9,6 @@ import {
} from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
import { TwoFactorRecoveryRequest } from "@bitwarden/common/auth/models/request/two-factor-recovery.request";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -85,15 +84,14 @@ describe("RecoverTwoFactorComponent", () => {
describe("handleRecoveryLogin", () => {
it("should log in successfully and navigate to the two-factor settings page", async () => {
// Arrange
const request = new TwoFactorRecoveryRequest();
request.recoveryCode = "testRecoveryCode";
request.email = "test@example.com";
const email = "test@example.com";
const recoveryCode = "testRecoveryCode";
const authResult = new AuthResult();
mockLoginStrategyService.logIn.mockResolvedValue(authResult);
// Act
await component["handleRecoveryLogin"](request);
await component["loginWithRecoveryCode"](email, recoveryCode);
// Assert
expect(mockLoginStrategyService.logIn).toHaveBeenCalledWith(
@@ -112,15 +110,14 @@ describe("RecoverTwoFactorComponent", () => {
it("should handle login errors and redirect to login page", async () => {
// Arrange
const request = new TwoFactorRecoveryRequest();
request.recoveryCode = "testRecoveryCode";
request.email = "test@example.com";
const email = "test@example.com";
const recoveryCode = "testRecoveryCode";
const error = new Error("Login failed");
mockLoginStrategyService.logIn.mockRejectedValue(error);
// Act
await component["handleRecoveryLogin"](request);
await component["loginWithRecoveryCode"](email, recoveryCode);
// Assert
expect(mockLogService.error).toHaveBeenCalledWith(
@@ -128,7 +125,7 @@ describe("RecoverTwoFactorComponent", () => {
error.message,
);
expect(mockRouter.navigate).toHaveBeenCalledWith(["/login"], {
queryParams: { email: request.email },
queryParams: { email: email },
});
});
});

View File

@@ -7,17 +7,11 @@ import {
PasswordLoginCredentials,
LoginSuccessHandlerService,
} from "@bitwarden/auth/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request";
import { TwoFactorRecoveryRequest } from "@bitwarden/common/auth/models/request/two-factor-recovery.request";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { ToastService } from "@bitwarden/components";
import { KeyService } from "@bitwarden/key-management";
import { NewDeviceVerificationNoticeService } from "@bitwarden/vault";
@Component({
@@ -36,32 +30,18 @@ export class RecoverTwoFactorComponent implements OnInit {
*/
recoveryCodeMessage = "";
/**
* Whether the recovery code login feature flag is enabled
*/
private recoveryCodeLoginFeatureFlagEnabled = false;
constructor(
private router: Router,
private apiService: ApiService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private keyService: KeyService,
private loginStrategyService: LoginStrategyServiceAbstraction,
private toastService: ToastService,
private configService: ConfigService,
private loginSuccessHandlerService: LoginSuccessHandlerService,
private logService: LogService,
private newDeviceVerificationNoticeService: NewDeviceVerificationNoticeService,
) {}
async ngOnInit() {
this.recoveryCodeLoginFeatureFlagEnabled = await this.configService.getFeatureFlag(
FeatureFlag.RecoveryCodeLogin,
);
this.recoveryCodeMessage = this.recoveryCodeLoginFeatureFlagEnabled
? this.i18nService.t("logInBelowUsingYourSingleUseRecoveryCode")
: this.i18nService.t("recoverAccountTwoStepDesc");
this.recoveryCodeMessage = this.i18nService.t("logInBelowUsingYourSingleUseRecoveryCode");
}
get email(): string {
@@ -85,38 +65,25 @@ export class RecoverTwoFactorComponent implements OnInit {
return;
}
const request = new TwoFactorRecoveryRequest();
request.recoveryCode = this.recoveryCode.replace(/\s/g, "").toLowerCase();
request.email = this.email.trim().toLowerCase();
const key = await this.loginStrategyService.makePreloginKey(this.masterPassword, request.email);
request.masterPasswordHash = await this.keyService.hashMasterKey(this.masterPassword, key);
const email = this.email.trim().toLowerCase();
const recoveryCode = this.recoveryCode.replace(/\s/g, "").toLowerCase();
if (this.recoveryCodeLoginFeatureFlagEnabled) {
await this.handleRecoveryLogin(request);
} else {
await this.apiService.postTwoFactorRecover(request);
this.toastService.showToast({
variant: "success",
title: "",
message: this.i18nService.t("twoStepRecoverDisabled"),
});
await this.router.navigate(["/"]);
}
await this.loginWithRecoveryCode(email, recoveryCode);
};
/**
* Handles the login process after a successful account recovery.
*/
private async handleRecoveryLogin(request: TwoFactorRecoveryRequest) {
private async loginWithRecoveryCode(email: string, recoveryCode: string) {
// Build two-factor request to pass into PasswordLoginCredentials request using the 2FA recovery code and RecoveryCode type
const twoFactorRequest: TokenTwoFactorRequest = {
provider: TwoFactorProviderType.RecoveryCode,
token: request.recoveryCode,
token: recoveryCode,
remember: false,
};
const credentials = new PasswordLoginCredentials(
request.email,
email,
this.masterPassword,
"",
twoFactorRequest,
@@ -148,7 +115,7 @@ export class RecoverTwoFactorComponent implements OnInit {
} catch (error) {
// If login errors, redirect to login page per product. Don't show error
this.logService.error("Error logging in automatically: ", (error as Error).message);
await this.router.navigate(["/login"], { queryParams: { email: request.email } });
await this.router.navigate(["/login"], { queryParams: { email: email } });
}
}
}

View File

@@ -29,7 +29,6 @@ import { TwoFactorProviders } from "@bitwarden/common/auth/services/two-factor.s
import { AuthResponse } from "@bitwarden/common/auth/types/auth-response";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { ProductTierType } from "@bitwarden/common/billing/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
@@ -85,12 +84,7 @@ export class TwoFactorSetupComponent implements OnInit, OnDestroy {
}
async ngOnInit() {
const recoveryCodeLoginFeatureFlagEnabled = await this.configService.getFeatureFlag(
FeatureFlag.RecoveryCodeLogin,
);
this.recoveryCodeWarningMessage = recoveryCodeLoginFeatureFlagEnabled
? this.i18nService.t("yourSingleUseRecoveryCode")
: this.i18nService.t("twoStepLoginRecoveryWarning");
this.recoveryCodeWarningMessage = this.i18nService.t("yourSingleUseRecoveryCode");
for (const key in TwoFactorProviders) {
// eslint-disable-next-line

View File

@@ -2,10 +2,10 @@ import { mock, MockProxy } from "jest-mock-extended";
import { BehaviorSubject } from "rxjs";
import { OrganizationUserResetPasswordWithIdRequest } from "@bitwarden/admin-console/common";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { WebauthnRotateCredentialRequest } from "@bitwarden/common/auth/models/request/webauthn-rotate-credential.request";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { EncString } from "@bitwarden/common/platform/models/domain/enc-string";

View File

@@ -2,11 +2,11 @@ import { Injectable } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
import { MasterPasswordVerification } from "@bitwarden/common/auth/types/verification";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { EncryptedString } from "@bitwarden/common/platform/models/domain/enc-string";
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";

View File

@@ -233,10 +233,10 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy {
* A user may restore items if they have delete permissions and the item is in the trash.
*/
protected async canUserRestore() {
const featureFlagEnabled = await firstValueFrom(this.limitItemDeletion$);
return this.isTrashFilter && this.cipher?.isDeleted && featureFlagEnabled
? this.cipher?.permissions.restore
: this.canDelete;
if (await firstValueFrom(this.limitItemDeletion$)) {
return this.isTrashFilter && this.cipher?.isDeleted && this.cipher?.permissions.restore;
}
return this.isTrashFilter && this.cipher?.isDeleted && this.canDelete;
}
protected showRestore: boolean;

View File

@@ -219,23 +219,24 @@ export class VaultHeaderComponent implements OnInit {
}
async addCollection(): Promise<void> {
const organization = this.organizations?.find(
(org) => org.productTierType === ProductTierType.Free,
);
const isBreadcrumbEventLogsEnabled = await firstValueFrom(
this.configService.getFeatureFlag$(FeatureFlag.PM12276_BreadcrumbEventLogs),
);
if (
this.organizations.length == 1 &&
organization.productTierType === ProductTierType.Free &&
isBreadcrumbEventLogsEnabled
) {
const collections = await this.collectionAdminService.getAll(organization.id);
if (collections.length === organization.maxCollections) {
await this.showFreeOrgUpgradeDialog(organization);
return;
if (isBreadcrumbEventLogsEnabled) {
const organization = this.organizations?.find(
(org) => org.productTierType === ProductTierType.Free,
);
if (this.organizations?.length == 1 && !!organization) {
const collections = await this.collectionAdminService.getAll(organization.id);
if (collections.length === organization.maxCollections) {
await this.showFreeOrgUpgradeDialog(organization);
return;
}
}
}
this.onAddCollection.emit();
}

View File

@@ -11,10 +11,10 @@ import {
AccountService,
} from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { ClientType } from "@bitwarden/common/enums";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { VaultTimeoutSettingsService } from "@bitwarden/common/key-management/vault-timeout";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";

View File

@@ -9,10 +9,10 @@ import { firstValueFrom } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { ClientType } from "@bitwarden/common/enums";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { VaultTimeoutSettingsService } from "@bitwarden/common/key-management/vault-timeout";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";

View File

@@ -3,8 +3,8 @@ import { CanActivateFn, Router } from "@angular/router";
import { firstValueFrom } from "rxjs";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { KeyService } from "@bitwarden/key-management";

View File

@@ -8,8 +8,8 @@ import {
import { firstValueFrom } from "rxjs";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { KeyService } from "@bitwarden/key-management";

View File

@@ -7,8 +7,8 @@ import { BehaviorSubject } from "rxjs";
import { EmptyComponent } from "@bitwarden/angular/platform/guard/feature-flag.guard.spec";
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { UserId } from "@bitwarden/common/types/guid";
import { KeyService } from "@bitwarden/key-management";

View File

@@ -4,8 +4,8 @@ import { firstValueFrom } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { KeyService } from "@bitwarden/key-management";

View File

@@ -1,7 +1,7 @@
import { merge, Observable, tap } from "rxjs";
import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ToastService } from "@bitwarden/components";

View File

@@ -2,7 +2,7 @@ import { mock, MockProxy } from "jest-mock-extended";
import { EMPTY, of } from "rxjs";
import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ToastService } from "@bitwarden/components";

View File

@@ -87,7 +87,6 @@ import {
import { AnonymousHubService as AnonymousHubServiceAbstraction } from "@bitwarden/common/auth/abstractions/anonymous-hub.service";
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
import { MasterPasswordApiService as MasterPasswordApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction";
@@ -105,7 +104,6 @@ import { AccountServiceImplementation } from "@bitwarden/common/auth/services/ac
import { AnonymousHubService } from "@bitwarden/common/auth/services/anonymous-hub.service";
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
import { DeviceTrustService } from "@bitwarden/common/auth/services/device-trust.service.implementation";
import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation";
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
import { MasterPasswordApiService } from "@bitwarden/common/auth/services/master-password/master-password-api.service.implementation";
@@ -148,6 +146,8 @@ import { BulkEncryptService } from "@bitwarden/common/key-management/crypto/abst
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { BulkEncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/bulk-encrypt.service.implementation";
import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/multithread-encrypt.service.implementation";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { DeviceTrustService } from "@bitwarden/common/key-management/device-trust/services/device-trust.service.implementation";
import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/services/key-connector.service";
import {

View File

@@ -1,6 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
import { Directive, EventEmitter, Input, Output } from "@angular/core";
import { CollectionView } from "@bitwarden/admin-console/common";
import { ITreeNodeObject } from "@bitwarden/common/vault/models/domain/tree-node";
@@ -10,7 +10,7 @@ import { TopLevelTreeNode } from "../models/top-level-tree-node.model";
import { VaultFilter } from "../models/vault-filter.model";
@Directive()
export class CollectionFilterComponent implements OnInit {
export class CollectionFilterComponent {
@Input() hide = false;
@Input() collapsedFilterNodes: Set<string>;
@Input() collectionNodes: DynamicTreeNode<CollectionView>;
@@ -51,13 +51,4 @@ export class CollectionFilterComponent implements OnInit {
async toggleCollapse(node: ITreeNodeObject) {
this.onNodeCollapseStateChange.emit(node);
}
ngOnInit() {
// Populate the set with all node IDs so all nodes are collapsed initially.
if (this.collectionNodes?.fullList) {
this.collectionNodes.fullList.forEach((node) => {
this.collapsedFilterNodes.add(node.id);
});
}
}
}

View File

@@ -16,10 +16,10 @@ import {
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 { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { PasswordResetEnrollmentServiceAbstraction } from "@bitwarden/common/auth/abstractions/password-reset-enrollment.service.abstraction";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { ClientType } from "@bitwarden/common/enums";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { KeysRequest } from "@bitwarden/common/models/request/keys.request";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";

View File

@@ -17,7 +17,6 @@ import {
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.service";
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { AuthRequestType } from "@bitwarden/common/auth/enums/auth-request-type";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { AdminAuthRequestStorable } from "@bitwarden/common/auth/models/domain/admin-auth-req-storable";
@@ -26,6 +25,7 @@ import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/for
import { AuthRequest } from "@bitwarden/common/auth/models/request/auth.request";
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
import { ClientType, HttpStatusCode } from "@bitwarden/common/enums";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { AppIdService } from "@bitwarden/common/platform/abstractions/app-id.service";
import { CryptoFunctionService } from "@bitwarden/common/platform/abstractions/crypto-function.service";

View File

@@ -2,12 +2,12 @@ import { mock, MockProxy } from "jest-mock-extended";
import { BehaviorSubject } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { FakeMasterPasswordService } from "@bitwarden/common/key-management/master-password/services/fake-master-password.service";
import {
VaultTimeoutAction,

View File

@@ -3,11 +3,11 @@
import { firstValueFrom, Observable, map, BehaviorSubject } from "rxjs";
import { Jsonify } from "type-fest";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
import { PasswordTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/password-token.request";
import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request";
import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { UserId } from "@bitwarden/common/types/guid";
import { AuthRequestLoginCredentials } from "../models/domain/login-credentials";

View File

@@ -2,7 +2,6 @@ import { mock, MockProxy } from "jest-mock-extended";
import { BehaviorSubject } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
import { AdminAuthRequestStorable } from "@bitwarden/common/auth/models/domain/admin-auth-req-storable";
@@ -11,6 +10,7 @@ import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/id
import { IUserDecryptionOptionsServerResponse } from "@bitwarden/common/auth/models/response/user-decryption-options/user-decryption-options.response";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
import { FakeMasterPasswordService } from "@bitwarden/common/key-management/master-password/services/fake-master-password.service";
import {

View File

@@ -3,12 +3,12 @@
import { firstValueFrom, Observable, map, BehaviorSubject } from "rxjs";
import { Jsonify } from "type-fest";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
import { SsoTokenRequest } from "@bitwarden/common/auth/models/request/identity-token/sso-token.request";
import { AuthRequestResponse } from "@bitwarden/common/auth/models/response/auth-request.response";
import { IdentityTokenResponse } from "@bitwarden/common/auth/models/response/identity-token.response";
import { HttpStatusCode } from "@bitwarden/common/enums";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";

View File

@@ -3,7 +3,6 @@ import { BehaviorSubject } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
import { TwoFactorProviderType } from "@bitwarden/common/auth/enums/two-factor-provider-type";
@@ -14,6 +13,7 @@ import { IdentityTwoFactorResponse } from "@bitwarden/common/auth/models/respons
import { PreloginResponse } from "@bitwarden/common/auth/models/response/prelogin.response";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
import { FakeMasterPasswordService } from "@bitwarden/common/key-management/master-password/services/fake-master-password.service";
import {

View File

@@ -12,7 +12,6 @@ import {
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service";
import { AuthenticationType } from "@bitwarden/common/auth/enums/authentication-type";
@@ -20,6 +19,7 @@ import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result";
import { TokenTwoFactorRequest } from "@bitwarden/common/auth/models/request/identity-token/token-two-factor.request";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import { VaultTimeoutSettingsService } from "@bitwarden/common/key-management/vault-timeout";

View File

@@ -51,7 +51,6 @@ import { PasswordlessAuthRequest } from "../auth/models/request/passwordless-aut
import { SecretVerificationRequest } from "../auth/models/request/secret-verification.request";
import { TwoFactorEmailRequest } from "../auth/models/request/two-factor-email.request";
import { TwoFactorProviderRequest } from "../auth/models/request/two-factor-provider.request";
import { TwoFactorRecoveryRequest } from "../auth/models/request/two-factor-recovery.request";
import { UpdateProfileRequest } from "../auth/models/request/update-profile.request";
import { UpdateTwoFactorAuthenticatorRequest } from "../auth/models/request/update-two-factor-authenticator.request";
import { UpdateTwoFactorDuoRequest } from "../auth/models/request/update-two-factor-duo.request";
@@ -344,7 +343,6 @@ export abstract class ApiService {
organizationId: string,
request: TwoFactorProviderRequest,
) => Promise<TwoFactorProviderResponse>;
postTwoFactorRecover: (request: TwoFactorRecoveryRequest) => Promise<any>;
postTwoFactorEmailSetup: (request: TwoFactorEmailRequest) => Promise<any>;
postTwoFactorEmail: (request: TwoFactorEmailRequest) => Promise<any>;
getDeviceVerificationSettings: () => Promise<DeviceVerificationResponse>;

View File

@@ -1,8 +0,0 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { SecretVerificationRequest } from "./secret-verification.request";
export class TwoFactorRecoveryRequest extends SecretVerificationRequest {
recoveryCode: string;
email: string;
}

View File

@@ -44,7 +44,6 @@ export enum FeatureFlag {
ResellerManagedOrgAlert = "PM-15814-alert-owners-of-reseller-managed-orgs",
AccountDeprovisioningBanner = "pm-17120-account-deprovisioning-admin-console-banner",
PM15179_AddExistingOrgsFromProviderPortal = "pm-15179-add-existing-orgs-from-provider-portal",
RecoveryCodeLogin = "pm-17128-recovery-code-login",
PM12276_BreadcrumbEventLogs = "pm-12276-breadcrumbing-for-business-features",
PM9115_TwoFactorFormPersistence = "pm-9115-two-factor-form-persistence",
PM18794_ProviderPaymentMethod = "pm-18794-provider-payment-method",
@@ -102,7 +101,6 @@ export const DefaultFeatureFlagValue = {
[FeatureFlag.ResellerManagedOrgAlert]: FALSE,
[FeatureFlag.AccountDeprovisioningBanner]: FALSE,
[FeatureFlag.PM15179_AddExistingOrgsFromProviderPortal]: FALSE,
[FeatureFlag.RecoveryCodeLogin]: FALSE,
[FeatureFlag.PM12276_BreadcrumbEventLogs]: FALSE,
[FeatureFlag.PM9115_TwoFactorFormPersistence]: FALSE,
[FeatureFlag.PM18794_ProviderPaymentMethod]: FALSE,

View File

@@ -2,11 +2,10 @@
// @ts-strict-ignore
import { Observable } from "rxjs";
import { EncString } from "../../platform/models/domain/enc-string";
import { UserId } from "../../types/guid";
import { DeviceKey, UserKey } from "../../types/key";
import { DeviceResponse } from "./devices/responses/device.response";
import { DeviceResponse } from "../../../auth/abstractions/devices/responses/device.response";
import { EncString } from "../../../platform/models/domain/enc-string";
import { UserId } from "../../../types/guid";
import { DeviceKey, UserKey } from "../../../types/key";
export abstract class DeviceTrustServiceAbstraction {
/**

View File

@@ -5,30 +5,30 @@ import { firstValueFrom, map, Observable, Subject } from "rxjs";
import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common";
import { KeyService } from "@bitwarden/key-management";
import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service";
import { AppIdService } from "../../platform/abstractions/app-id.service";
import { ConfigService } from "../../platform/abstractions/config/config.service";
import { CryptoFunctionService } from "../../platform/abstractions/crypto-function.service";
import { I18nService } from "../../platform/abstractions/i18n.service";
import { KeyGenerationService } from "../../platform/abstractions/key-generation.service";
import { LogService } from "../../platform/abstractions/log.service";
import { PlatformUtilsService } from "../../platform/abstractions/platform-utils.service";
import { AbstractStorageService } from "../../platform/abstractions/storage.service";
import { StorageLocation } from "../../platform/enums";
import { EncString } from "../../platform/models/domain/enc-string";
import { StorageOptions } from "../../platform/models/domain/storage-options";
import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key";
import { DEVICE_TRUST_DISK_LOCAL, StateProvider, UserKeyDefinition } from "../../platform/state";
import { UserId } from "../../types/guid";
import { UserKey, DeviceKey } from "../../types/key";
import { DeviceTrustServiceAbstraction } from "../abstractions/device-trust.service.abstraction";
import { DeviceResponse } from "../abstractions/devices/responses/device.response";
import { DevicesApiServiceAbstraction } from "../abstractions/devices-api.service.abstraction";
import { SecretVerificationRequest } from "../models/request/secret-verification.request";
import { DeviceResponse } from "../../../auth/abstractions/devices/responses/device.response";
import { DevicesApiServiceAbstraction } from "../../../auth/abstractions/devices-api.service.abstraction";
import { SecretVerificationRequest } from "../../../auth/models/request/secret-verification.request";
import {
DeviceKeysUpdateRequest,
UpdateDevicesTrustRequest,
} from "../models/request/update-devices-trust.request";
} from "../../../auth/models/request/update-devices-trust.request";
import { AppIdService } from "../../../platform/abstractions/app-id.service";
import { ConfigService } from "../../../platform/abstractions/config/config.service";
import { CryptoFunctionService } from "../../../platform/abstractions/crypto-function.service";
import { I18nService } from "../../../platform/abstractions/i18n.service";
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
import { LogService } from "../../../platform/abstractions/log.service";
import { PlatformUtilsService } from "../../../platform/abstractions/platform-utils.service";
import { AbstractStorageService } from "../../../platform/abstractions/storage.service";
import { StorageLocation } from "../../../platform/enums";
import { EncString } from "../../../platform/models/domain/enc-string";
import { StorageOptions } from "../../../platform/models/domain/storage-options";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { DEVICE_TRUST_DISK_LOCAL, StateProvider, UserKeyDefinition } from "../../../platform/state";
import { UserId } from "../../../types/guid";
import { UserKey, DeviceKey } from "../../../types/key";
import { EncryptService } from "../../crypto/abstractions/encrypt.service";
import { DeviceTrustServiceAbstraction } from "../abstractions/device-trust.service.abstraction";
/** Uses disk storage so that the device key can persist after log out and tab removal. */
export const DEVICE_KEY = new UserKeyDefinition<DeviceKey | null>(

View File

@@ -3,38 +3,38 @@
import { matches, mock } from "jest-mock-extended";
import { BehaviorSubject, firstValueFrom, of } from "rxjs";
import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common";
import {
UserDecryptionOptionsServiceAbstraction,
UserDecryptionOptions,
} from "@bitwarden/auth/common";
import { KeyService } from "@bitwarden/key-management";
// FIXME: remove `src` and fix import
// eslint-disable-next-line no-restricted-imports
import { UserDecryptionOptions } from "../../../../auth/src/common/models/domain/user-decryption-options";
import { FakeAccountService, mockAccountServiceWith } from "../../../spec/fake-account-service";
import { FakeActiveUserState } from "../../../spec/fake-state";
import { FakeStateProvider } from "../../../spec/fake-state-provider";
import { DeviceType } from "../../enums";
import { EncryptService } from "../../key-management/crypto/abstractions/encrypt.service";
import { AppIdService } from "../../platform/abstractions/app-id.service";
import { ConfigService } from "../../platform/abstractions/config/config.service";
import { CryptoFunctionService } from "../../platform/abstractions/crypto-function.service";
import { I18nService } from "../../platform/abstractions/i18n.service";
import { KeyGenerationService } from "../../platform/abstractions/key-generation.service";
import { LogService } from "../../platform/abstractions/log.service";
import { PlatformUtilsService } from "../../platform/abstractions/platform-utils.service";
import { AbstractStorageService } from "../../platform/abstractions/storage.service";
import { StorageLocation } from "../../platform/enums";
import { EncryptionType } from "../../platform/enums/encryption-type.enum";
import { Utils } from "../../platform/misc/utils";
import { EncString } from "../../platform/models/domain/enc-string";
import { StorageOptions } from "../../platform/models/domain/storage-options";
import { SymmetricCryptoKey } from "../../platform/models/domain/symmetric-crypto-key";
import { CsprngArray } from "../../types/csprng";
import { UserId } from "../../types/guid";
import { DeviceKey, UserKey } from "../../types/key";
import { DeviceResponse } from "../abstractions/devices/responses/device.response";
import { DevicesApiServiceAbstraction } from "../abstractions/devices-api.service.abstraction";
import { UpdateDevicesTrustRequest } from "../models/request/update-devices-trust.request";
import { ProtectedDeviceResponse } from "../models/response/protected-device.response";
import { FakeAccountService, mockAccountServiceWith } from "../../../../spec/fake-account-service";
import { FakeActiveUserState } from "../../../../spec/fake-state";
import { FakeStateProvider } from "../../../../spec/fake-state-provider";
import { DeviceResponse } from "../../../auth/abstractions/devices/responses/device.response";
import { DevicesApiServiceAbstraction } from "../../../auth/abstractions/devices-api.service.abstraction";
import { UpdateDevicesTrustRequest } from "../../../auth/models/request/update-devices-trust.request";
import { ProtectedDeviceResponse } from "../../../auth/models/response/protected-device.response";
import { DeviceType } from "../../../enums";
import { AppIdService } from "../../../platform/abstractions/app-id.service";
import { ConfigService } from "../../../platform/abstractions/config/config.service";
import { CryptoFunctionService } from "../../../platform/abstractions/crypto-function.service";
import { I18nService } from "../../../platform/abstractions/i18n.service";
import { KeyGenerationService } from "../../../platform/abstractions/key-generation.service";
import { LogService } from "../../../platform/abstractions/log.service";
import { PlatformUtilsService } from "../../../platform/abstractions/platform-utils.service";
import { AbstractStorageService } from "../../../platform/abstractions/storage.service";
import { StorageLocation } from "../../../platform/enums";
import { EncryptionType } from "../../../platform/enums/encryption-type.enum";
import { Utils } from "../../../platform/misc/utils";
import { EncString } from "../../../platform/models/domain/enc-string";
import { StorageOptions } from "../../../platform/models/domain/storage-options";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { CsprngArray } from "../../../types/csprng";
import { UserId } from "../../../types/guid";
import { DeviceKey, UserKey } from "../../../types/key";
import { EncryptService } from "../../crypto/abstractions/encrypt.service";
import {
SHOULD_TRUST_DEVICE,

View File

@@ -131,6 +131,10 @@ export const THEMING_DISK = new StateDefinition("theming", "disk", { web: "disk-
export const TRANSLATION_DISK = new StateDefinition("translation", "disk", { web: "disk-local" });
export const ANIMATION_DISK = new StateDefinition("animation", "disk");
export const TASK_SCHEDULER_DISK = new StateDefinition("taskScheduler", "disk");
export const EXTENSION_INITIAL_INSTALL_DISK = new StateDefinition(
"extensionInitialInstall",
"disk",
);
// Design System

View File

@@ -59,7 +59,6 @@ import { PasswordlessAuthRequest } from "../auth/models/request/passwordless-aut
import { SecretVerificationRequest } from "../auth/models/request/secret-verification.request";
import { TwoFactorEmailRequest } from "../auth/models/request/two-factor-email.request";
import { TwoFactorProviderRequest } from "../auth/models/request/two-factor-provider.request";
import { TwoFactorRecoveryRequest } from "../auth/models/request/two-factor-recovery.request";
import { UpdateProfileRequest } from "../auth/models/request/update-profile.request";
import { UpdateTwoFactorAuthenticatorRequest } from "../auth/models/request/update-two-factor-authenticator.request";
import { UpdateTwoFactorDuoRequest } from "../auth/models/request/update-two-factor-duo.request";
@@ -1064,10 +1063,6 @@ export class ApiService implements ApiServiceAbstraction {
return new TwoFactorProviderResponse(r);
}
postTwoFactorRecover(request: TwoFactorRecoveryRequest): Promise<any> {
return this.send("POST", "/two-factor/recover", request, false, false);
}
postTwoFactorEmailSetup(request: TwoFactorEmailRequest): Promise<any> {
return this.send("POST", "/two-factor/send-email", request, true, false);
}

View File

@@ -19,7 +19,6 @@ import { PinServiceAbstraction } from "@bitwarden/auth/common";
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { VerificationType } from "@bitwarden/common/auth/enums/verification-type";
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
@@ -28,6 +27,7 @@ import {
MasterPasswordVerificationResponse,
} from "@bitwarden/common/auth/types/verification";
import { ClientType, DeviceType } from "@bitwarden/common/enums";
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction";
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";

82
package-lock.json generated
View File

@@ -24,7 +24,7 @@
"@angular/platform-browser": "18.2.13",
"@angular/platform-browser-dynamic": "18.2.13",
"@angular/router": "18.2.13",
"@bitwarden/sdk-internal": "0.2.0-main.107",
"@bitwarden/sdk-internal": "0.2.0-main.124",
"@electron/fuses": "1.8.0",
"@emotion/css": "11.13.5",
"@koa/multer": "3.0.2",
@@ -43,7 +43,7 @@
"commander": "11.1.0",
"core-js": "3.40.0",
"form-data": "4.0.1",
"https-proxy-agent": "7.0.5",
"https-proxy-agent": "7.0.6",
"inquirer": "8.2.6",
"jquery": "3.7.1",
"jsdom": "26.0.0",
@@ -204,7 +204,7 @@
"chalk": "4.1.2",
"commander": "11.1.0",
"form-data": "4.0.1",
"https-proxy-agent": "7.0.5",
"https-proxy-agent": "7.0.6",
"inquirer": "8.2.6",
"jsdom": "26.0.0",
"jszip": "3.10.1",
@@ -759,6 +759,16 @@
"@types/send": "*"
}
},
"node_modules/@angular-devkit/build-angular/node_modules/agent-base": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/@angular-devkit/build-angular/node_modules/babel-loader": {
"version": "9.1.3",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz",
@@ -880,6 +890,20 @@
"node": ">= 6"
}
},
"node_modules/@angular-devkit/build-angular/node_modules/https-proxy-agent": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
"integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.0.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@angular-devkit/build-angular/node_modules/immutable": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz",
@@ -2090,6 +2114,16 @@
"semver": "bin/semver.js"
}
},
"node_modules/@angular/build/node_modules/agent-base": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/@angular/build/node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
@@ -2135,6 +2169,20 @@
"node": ">= 6"
}
},
"node_modules/@angular/build/node_modules/https-proxy-agent": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
"integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
"dev": true,
"license": "MIT",
"dependencies": {
"agent-base": "^7.0.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/@angular/build/node_modules/immutable": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz",
@@ -4650,10 +4698,9 @@
"link": true
},
"node_modules/@bitwarden/sdk-internal": {
"version": "0.2.0-main.107",
"resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.107.tgz",
"integrity": "sha512-xpOF6NAS0/em3jFBv4FI1ASy1Nuc7I1v41TVmG56wS+80y+NH1RnfGjp+a+XiO7Xxh3jssrxmjzihJjWQQA0rg==",
"license": "GPL-3.0"
"version": "0.2.0-main.124",
"resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.124.tgz",
"integrity": "sha512-7F+DlPFng/thT4EVIQk2tRC7kff6G2B7alHAIxBdioJc9vE64Z5R5pviUyMZzqLnA5e9y8EnQdtWsQzUkHxisQ=="
},
"node_modules/@bitwarden/send-ui": {
"resolved": "libs/tools/send/send-ui",
@@ -21272,12 +21319,12 @@
}
},
"node_modules/https-proxy-agent": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
"integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.0.2",
"agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
@@ -24154,19 +24201,6 @@
"node": ">= 14"
}
},
"node_modules/jsdom/node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/jsdom/node_modules/tough-cookie": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.1.tgz",

View File

@@ -154,7 +154,7 @@
"@angular/platform-browser": "18.2.13",
"@angular/platform-browser-dynamic": "18.2.13",
"@angular/router": "18.2.13",
"@bitwarden/sdk-internal": "0.2.0-main.107",
"@bitwarden/sdk-internal": "0.2.0-main.124",
"@electron/fuses": "1.8.0",
"@emotion/css": "11.13.5",
"@koa/multer": "3.0.2",
@@ -173,7 +173,7 @@
"commander": "11.1.0",
"core-js": "3.40.0",
"form-data": "4.0.1",
"https-proxy-agent": "7.0.5",
"https-proxy-agent": "7.0.6",
"inquirer": "8.2.6",
"jquery": "3.7.1",
"jsdom": "26.0.0",