1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-14 10:26:19 +01:00
bitwarden-browser/apps/browser/src/background/main.background.ts
Jared Snider a66e224d32
Auth/PM-7072 - Token Service - Access Token Secure Storage Refactor (#8412)
* PM-5263 - TokenSvc - WIP on access token secure storage refactor

* PM-5263 - Add key generation svc to token svc.

* PM-5263 - TokenSvc - more progress on encrypt access token work.

* PM-5263 - TokenSvc TODO cleanup

* PM-5263 - TokenSvc - rename

* PM-5263 - TokenSvc - decryptAccess token must return null as that is a valid case.

* PM-5263 - Add EncryptSvc dep to TokenSvc

* PM-5263 - Add secure storage to token service

* PM-5263 - TokenSvc - (1) Finish implementing accessTokenKey stored in secure storage + encrypted access token stored on disk  (2) Remove no longer necessary migration flag as the presence of the accessTokenKey now serves the same purpose.

Co-authored-by: Jake Fink <jfink@bitwarden.com>

* PM-5263 - TokenSvc - (1) Tweak return structure of decryptAccessToken to be more debuggable (2) Add TODO to add more error handling.

* PM-5263 - TODO: update tests

* PM-5263 - add temp logs

* PM-5263 - TokenSvc - remove logs now that I don't need them.

* fix tests for access token

* PM-5263 - TokenSvc test cleanup - small tweaks / cleanup

* PM-5263 - TokenService - per PR feedback from Justin - add error message to error message if possible.

Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>

---------

Co-authored-by: Jake Fink <jfink@bitwarden.com>
Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
2024-03-26 18:41:14 -04:00

1291 lines
54 KiB
TypeScript

import { firstValueFrom } from "rxjs";
import {
PinCryptoServiceAbstraction,
PinCryptoService,
LoginStrategyServiceAbstraction,
LoginStrategyService,
InternalUserDecryptionOptionsServiceAbstraction,
UserDecryptionOptionsService,
AuthRequestServiceAbstraction,
AuthRequestService,
} from "@bitwarden/auth/common";
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service";
import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service";
import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service";
import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service";
import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service";
import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction";
import { InternalPolicyService as InternalPolicyServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { ProviderService as ProviderServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/provider.service";
import { OrganizationService } from "@bitwarden/common/admin-console/services/organization/organization.service";
import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service";
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service";
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.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 { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarden/common/auth/abstractions/key-connector.service";
import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction";
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/auth/abstractions/token.service";
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
import { UserVerificationApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification-api.service.abstraction";
import { UserVerificationService as UserVerificationServiceAbstraction } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
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 { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.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 { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
import { SsoLoginService } from "@bitwarden/common/auth/services/sso-login.service";
import { TokenService } from "@bitwarden/common/auth/services/token.service";
import { TwoFactorService } from "@bitwarden/common/auth/services/two-factor.service";
import { UserVerificationApiService } from "@bitwarden/common/auth/services/user-verification/user-verification-api.service";
import { UserVerificationService } from "@bitwarden/common/auth/services/user-verification/user-verification.service";
import {
AutofillSettingsServiceAbstraction,
AutofillSettingsService,
} from "@bitwarden/common/autofill/services/autofill-settings.service";
import {
BadgeSettingsServiceAbstraction,
BadgeSettingsService,
} from "@bitwarden/common/autofill/services/badge-settings.service";
import {
DomainSettingsService,
DefaultDomainSettingsService,
} from "@bitwarden/common/autofill/services/domain-settings.service";
import {
UserNotificationSettingsService,
UserNotificationSettingsServiceAbstraction,
} from "@bitwarden/common/autofill/services/user-notification-settings.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service";
import { AppIdService as AppIdServiceAbstraction } from "@bitwarden/common/platform/abstractions/app-id.service";
import { ConfigApiServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config-api.service.abstraction";
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { FileUploadService as FileUploadServiceAbstraction } from "@bitwarden/common/platform/abstractions/file-upload/file-upload.service";
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service";
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service";
import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service";
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import {
AbstractMemoryStorageService,
AbstractStorageService,
ObservableStorageService,
} from "@bitwarden/common/platform/abstractions/storage.service";
import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/platform/abstractions/system.service";
import {
BiometricStateService,
DefaultBiometricStateService,
} from "@bitwarden/common/platform/biometrics/biometric-state.service";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
import { GlobalState } from "@bitwarden/common/platform/models/domain/global-state";
import { AppIdService } from "@bitwarden/common/platform/services/app-id.service";
import { ConfigApiService } from "@bitwarden/common/platform/services/config/config-api.service";
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
import { ContainerService } from "@bitwarden/common/platform/services/container.service";
import { EncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/encrypt.service.implementation";
import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/platform/services/cryptography/multithread-encrypt.service.implementation";
import { FileUploadService } from "@bitwarden/common/platform/services/file-upload/file-upload.service";
import { KeyGenerationService } from "@bitwarden/common/platform/services/key-generation.service";
import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner";
import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
import { SystemService } from "@bitwarden/common/platform/services/system.service";
import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service";
import {
ActiveUserStateProvider,
DerivedStateProvider,
GlobalStateProvider,
SingleUserStateProvider,
StateEventRunnerService,
StateProvider,
} from "@bitwarden/common/platform/state";
/* eslint-disable import/no-restricted-paths -- We need the implementation to inject, but generally these should not be accessed */
import { DefaultActiveUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-active-user-state.provider";
import { DefaultGlobalStateProvider } from "@bitwarden/common/platform/state/implementations/default-global-state.provider";
import { DefaultSingleUserStateProvider } from "@bitwarden/common/platform/state/implementations/default-single-user-state.provider";
import { DefaultStateProvider } from "@bitwarden/common/platform/state/implementations/default-state.provider";
import { StateEventRegistrarService } from "@bitwarden/common/platform/state/state-event-registrar.service";
/* eslint-enable import/no-restricted-paths */
import { DefaultThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
import { ApiService } from "@bitwarden/common/services/api.service";
import { AuditService } from "@bitwarden/common/services/audit.service";
import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service";
import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service";
import { NotificationsService } from "@bitwarden/common/services/notifications.service";
import { SearchService } from "@bitwarden/common/services/search.service";
import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service";
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/src/auth/abstractions/avatar.service";
import {
PasswordGenerationService,
PasswordGenerationServiceAbstraction,
} from "@bitwarden/common/tools/generator/password";
import {
UsernameGenerationService,
UsernameGenerationServiceAbstraction,
} from "@bitwarden/common/tools/generator/username";
import {
PasswordStrengthService,
PasswordStrengthServiceAbstraction,
} from "@bitwarden/common/tools/password-strength";
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service";
import { SendApiService as SendApiServiceAbstraction } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
import { InternalSendService as InternalSendServiceAbstraction } from "@bitwarden/common/tools/send/services/send.service.abstraction";
import { UserId } from "@bitwarden/common/types/guid";
import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService as CollectionServiceAbstraction } from "@bitwarden/common/vault/abstractions/collection.service";
import { Fido2AuthenticatorService as Fido2AuthenticatorServiceAbstraction } from "@bitwarden/common/vault/abstractions/fido2/fido2-authenticator.service.abstraction";
import { Fido2ClientService as Fido2ClientServiceAbstraction } from "@bitwarden/common/vault/abstractions/fido2/fido2-client.service.abstraction";
import { Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction } from "@bitwarden/common/vault/abstractions/fido2/fido2-user-interface.service.abstraction";
import { CipherFileUploadService as CipherFileUploadServiceAbstraction } from "@bitwarden/common/vault/abstractions/file-upload/cipher-file-upload.service";
import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction";
import { InternalFolderService as InternalFolderServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { SyncNotifierService as SyncNotifierServiceAbstraction } from "@bitwarden/common/vault/abstractions/sync/sync-notifier.service.abstraction";
import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/vault/abstractions/totp.service";
import { VaultSettingsService as VaultSettingsServiceAbstraction } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { CipherService } from "@bitwarden/common/vault/services/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/services/collection.service";
import { Fido2AuthenticatorService } from "@bitwarden/common/vault/services/fido2/fido2-authenticator.service";
import { Fido2ClientService } from "@bitwarden/common/vault/services/fido2/fido2-client.service";
import { CipherFileUploadService } from "@bitwarden/common/vault/services/file-upload/cipher-file-upload.service";
import { FolderApiService } from "@bitwarden/common/vault/services/folder/folder-api.service";
import { FolderService } from "@bitwarden/common/vault/services/folder/folder.service";
import { SyncNotifierService } from "@bitwarden/common/vault/services/sync/sync-notifier.service";
import { SyncService } from "@bitwarden/common/vault/services/sync/sync.service";
import { TotpService } from "@bitwarden/common/vault/services/totp.service";
import { VaultSettingsService } from "@bitwarden/common/vault/services/vault-settings/vault-settings.service";
import {
ImportApiService,
ImportApiServiceAbstraction,
ImportService,
ImportServiceAbstraction,
} from "@bitwarden/importer/core";
import {
IndividualVaultExportService,
IndividualVaultExportServiceAbstraction,
OrganizationVaultExportService,
OrganizationVaultExportServiceAbstraction,
VaultExportService,
VaultExportServiceAbstraction,
} from "@bitwarden/vault-export-core";
import ContextMenusBackground from "../autofill/background/context-menus.background";
import NotificationBackground from "../autofill/background/notification.background";
import OverlayBackground from "../autofill/background/overlay.background";
import TabsBackground from "../autofill/background/tabs.background";
import WebRequestBackground from "../autofill/background/web-request.background";
import { CipherContextMenuHandler } from "../autofill/browser/cipher-context-menu-handler";
import { ContextMenuClickedHandler } from "../autofill/browser/context-menu-clicked-handler";
import { MainContextMenuHandler } from "../autofill/browser/main-context-menu-handler";
import { AutofillService as AutofillServiceAbstraction } from "../autofill/services/abstractions/autofill.service";
import AutofillService from "../autofill/services/autofill.service";
import { SafariApp } from "../browser/safariApp";
import { Account } from "../models/account";
import { BrowserApi } from "../platform/browser/browser-api";
import { flagEnabled } from "../platform/flags";
import { UpdateBadge } from "../platform/listeners/update-badge";
import { BrowserStateService as StateServiceAbstraction } from "../platform/services/abstractions/browser-state.service";
import { BrowserConfigService } from "../platform/services/browser-config.service";
import { BrowserCryptoService } from "../platform/services/browser-crypto.service";
import { BrowserEnvironmentService } from "../platform/services/browser-environment.service";
import BrowserLocalStorageService from "../platform/services/browser-local-storage.service";
import BrowserMessagingPrivateModeBackgroundService from "../platform/services/browser-messaging-private-mode-background.service";
import BrowserMessagingService from "../platform/services/browser-messaging.service";
import { BrowserStateService } from "../platform/services/browser-state.service";
import I18nService from "../platform/services/i18n.service";
import { LocalBackedSessionStorageService } from "../platform/services/local-backed-session-storage.service";
import { BackgroundPlatformUtilsService } from "../platform/services/platform-utils/background-platform-utils.service";
import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service";
import { BackgroundDerivedStateProvider } from "../platform/state/background-derived-state.provider";
import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service";
import { BrowserSendService } from "../services/browser-send.service";
import VaultTimeoutService from "../services/vault-timeout/vault-timeout.service";
import FilelessImporterBackground from "../tools/background/fileless-importer.background";
import { BrowserFido2UserInterfaceService } from "../vault/fido2/browser-fido2-user-interface.service";
import { Fido2Service as Fido2ServiceAbstraction } from "../vault/services/abstractions/fido2.service";
import Fido2Service from "../vault/services/fido2.service";
import { VaultFilterService } from "../vault/services/vault-filter.service";
import CommandsBackground from "./commands.background";
import IdleBackground from "./idle.background";
import { NativeMessagingBackground } from "./nativeMessaging.background";
import RuntimeBackground from "./runtime.background";
export default class MainBackground {
messagingService: MessagingServiceAbstraction;
storageService: AbstractStorageService;
secureStorageService: AbstractStorageService;
memoryStorageService: AbstractMemoryStorageService;
memoryStorageForStateProviders: AbstractMemoryStorageService & ObservableStorageService;
i18nService: I18nServiceAbstraction;
platformUtilsService: PlatformUtilsServiceAbstraction;
logService: LogServiceAbstraction;
keyGenerationService: KeyGenerationServiceAbstraction;
cryptoService: CryptoServiceAbstraction;
cryptoFunctionService: CryptoFunctionServiceAbstraction;
tokenService: TokenServiceAbstraction;
appIdService: AppIdServiceAbstraction;
apiService: ApiServiceAbstraction;
environmentService: BrowserEnvironmentService;
cipherService: CipherServiceAbstraction;
folderService: InternalFolderServiceAbstraction;
userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction;
collectionService: CollectionServiceAbstraction;
vaultTimeoutService: VaultTimeoutService;
vaultTimeoutSettingsService: VaultTimeoutSettingsServiceAbstraction;
syncService: SyncServiceAbstraction;
passwordGenerationService: PasswordGenerationServiceAbstraction;
passwordStrengthService: PasswordStrengthServiceAbstraction;
totpService: TotpServiceAbstraction;
autofillService: AutofillServiceAbstraction;
containerService: ContainerService;
auditService: AuditServiceAbstraction;
authService: AuthServiceAbstraction;
loginStrategyService: LoginStrategyServiceAbstraction;
importApiService: ImportApiServiceAbstraction;
importService: ImportServiceAbstraction;
exportService: VaultExportServiceAbstraction;
searchService: SearchServiceAbstraction;
notificationsService: NotificationsServiceAbstraction;
stateService: StateServiceAbstraction;
userNotificationSettingsService: UserNotificationSettingsServiceAbstraction;
autofillSettingsService: AutofillSettingsServiceAbstraction;
badgeSettingsService: BadgeSettingsServiceAbstraction;
domainSettingsService: DomainSettingsService;
systemService: SystemServiceAbstraction;
eventCollectionService: EventCollectionServiceAbstraction;
eventUploadService: EventUploadServiceAbstraction;
policyService: InternalPolicyServiceAbstraction;
sendService: InternalSendServiceAbstraction;
fileUploadService: FileUploadServiceAbstraction;
cipherFileUploadService: CipherFileUploadServiceAbstraction;
organizationService: InternalOrganizationServiceAbstraction;
providerService: ProviderServiceAbstraction;
keyConnectorService: KeyConnectorServiceAbstraction;
userVerificationService: UserVerificationServiceAbstraction;
twoFactorService: TwoFactorServiceAbstraction;
vaultFilterService: VaultFilterService;
usernameGenerationService: UsernameGenerationServiceAbstraction;
encryptService: EncryptService;
folderApiService: FolderApiServiceAbstraction;
policyApiService: PolicyApiServiceAbstraction;
sendApiService: SendApiServiceAbstraction;
userVerificationApiService: UserVerificationApiServiceAbstraction;
syncNotifierService: SyncNotifierServiceAbstraction;
fido2UserInterfaceService: Fido2UserInterfaceServiceAbstraction;
fido2AuthenticatorService: Fido2AuthenticatorServiceAbstraction;
fido2ClientService: Fido2ClientServiceAbstraction;
avatarService: AvatarServiceAbstraction;
mainContextMenuHandler: MainContextMenuHandler;
cipherContextMenuHandler: CipherContextMenuHandler;
configService: BrowserConfigService;
configApiService: ConfigApiServiceAbstraction;
devicesApiService: DevicesApiServiceAbstraction;
devicesService: DevicesServiceAbstraction;
deviceTrustCryptoService: DeviceTrustCryptoServiceAbstraction;
authRequestService: AuthRequestServiceAbstraction;
accountService: AccountServiceAbstraction;
globalStateProvider: GlobalStateProvider;
pinCryptoService: PinCryptoServiceAbstraction;
singleUserStateProvider: SingleUserStateProvider;
activeUserStateProvider: ActiveUserStateProvider;
derivedStateProvider: DerivedStateProvider;
stateProvider: StateProvider;
fido2Service: Fido2ServiceAbstraction;
individualVaultExportService: IndividualVaultExportServiceAbstraction;
organizationVaultExportService: OrganizationVaultExportServiceAbstraction;
vaultSettingsService: VaultSettingsServiceAbstraction;
biometricStateService: BiometricStateService;
stateEventRunnerService: StateEventRunnerService;
ssoLoginService: SsoLoginServiceAbstraction;
billingAccountProfileStateService: BillingAccountProfileStateService;
onUpdatedRan: boolean;
onReplacedRan: boolean;
loginToAutoFill: CipherView = null;
private commandsBackground: CommandsBackground;
private contextMenusBackground: ContextMenusBackground;
private idleBackground: IdleBackground;
private notificationBackground: NotificationBackground;
private overlayBackground: OverlayBackground;
private filelessImporterBackground: FilelessImporterBackground;
private runtimeBackground: RuntimeBackground;
private tabsBackground: TabsBackground;
private webRequestBackground: WebRequestBackground;
private syncTimeout: any;
private isSafari: boolean;
private nativeMessagingBackground: NativeMessagingBackground;
popupOnlyContext: boolean;
constructor(public isPrivateMode: boolean = false) {
this.popupOnlyContext = isPrivateMode || BrowserApi.isManifestVersion(3);
// Services
const lockedCallback = async (userId?: string) => {
if (this.notificationsService != null) {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.notificationsService.updateConnection(false);
}
await this.refreshBadge();
await this.refreshMenu(true);
if (this.systemService != null) {
await this.systemService.clearPendingClipboard();
await this.systemService.startProcessReload(this.authService);
}
};
const logoutCallback = async (expired: boolean, userId?: UserId) =>
await this.logout(expired, userId);
this.messagingService = this.popupOnlyContext
? new BrowserMessagingPrivateModeBackgroundService()
: new BrowserMessagingService();
this.logService = new ConsoleLogService(false);
this.cryptoFunctionService = new WebCryptoFunctionService(self);
this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService);
this.storageService = new BrowserLocalStorageService();
this.secureStorageService = this.storageService; // secure storage is not supported in browsers, so we use local storage and warn users when it is used
this.memoryStorageService = BrowserApi.isManifestVersion(3)
? new LocalBackedSessionStorageService(
new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
this.keyGenerationService,
)
: new MemoryStorageService();
this.memoryStorageForStateProviders = BrowserApi.isManifestVersion(3)
? new LocalBackedSessionStorageService(
new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, false),
this.keyGenerationService,
)
: new BackgroundMemoryStorageService();
const storageServiceProvider = new StorageServiceProvider(
this.storageService as BrowserLocalStorageService,
this.memoryStorageForStateProviders,
);
this.globalStateProvider = new DefaultGlobalStateProvider(storageServiceProvider);
const stateEventRegistrarService = new StateEventRegistrarService(
this.globalStateProvider,
storageServiceProvider,
);
this.stateEventRunnerService = new StateEventRunnerService(
this.globalStateProvider,
storageServiceProvider,
);
this.encryptService = flagEnabled("multithreadDecryption")
? new MultithreadEncryptServiceImplementation(
this.cryptoFunctionService,
this.logService,
true,
)
: new EncryptServiceImplementation(this.cryptoFunctionService, this.logService, true);
this.singleUserStateProvider = new DefaultSingleUserStateProvider(
storageServiceProvider,
stateEventRegistrarService,
);
this.accountService = new AccountServiceImplementation(
this.messagingService,
this.logService,
this.globalStateProvider,
);
this.activeUserStateProvider = new DefaultActiveUserStateProvider(
this.accountService,
this.singleUserStateProvider,
);
this.derivedStateProvider = new BackgroundDerivedStateProvider(
this.memoryStorageForStateProviders,
);
this.stateProvider = new DefaultStateProvider(
this.activeUserStateProvider,
this.singleUserStateProvider,
this.globalStateProvider,
this.derivedStateProvider,
);
this.environmentService = new BrowserEnvironmentService(
this.logService,
this.stateProvider,
this.accountService,
);
this.biometricStateService = new DefaultBiometricStateService(this.stateProvider);
this.userNotificationSettingsService = new UserNotificationSettingsService(this.stateProvider);
this.platformUtilsService = new BackgroundPlatformUtilsService(
this.messagingService,
(clipboardValue, clearMs) => this.clearClipboard(clipboardValue, clearMs),
async () => this.biometricUnlock(),
self,
);
this.tokenService = new TokenService(
this.singleUserStateProvider,
this.globalStateProvider,
this.platformUtilsService.supportsSecureStorage(),
this.secureStorageService,
this.keyGenerationService,
this.encryptService,
this.logService,
);
const migrationRunner = new MigrationRunner(
this.storageService,
this.logService,
new MigrationBuilderService(),
);
this.stateService = new BrowserStateService(
this.storageService,
this.secureStorageService,
this.memoryStorageService,
this.logService,
new StateFactory(GlobalState, Account),
this.accountService,
this.environmentService,
this.tokenService,
migrationRunner,
);
const themeStateService = new DefaultThemeStateService(this.globalStateProvider);
this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
this.cryptoService = new BrowserCryptoService(
this.keyGenerationService,
this.cryptoFunctionService,
this.encryptService,
this.platformUtilsService,
this.logService,
this.stateService,
this.accountService,
this.stateProvider,
this.biometricStateService,
);
this.appIdService = new AppIdService(this.globalStateProvider);
this.apiService = new ApiService(
this.tokenService,
this.platformUtilsService,
this.environmentService,
this.appIdService,
this.stateService,
(expired: boolean) => this.logout(expired),
);
this.domainSettingsService = new DefaultDomainSettingsService(this.stateProvider);
this.fileUploadService = new FileUploadService(this.logService);
this.cipherFileUploadService = new CipherFileUploadService(
this.apiService,
this.fileUploadService,
);
this.searchService = new SearchService(this.logService, this.i18nService);
this.collectionService = new CollectionService(
this.cryptoService,
this.i18nService,
this.stateProvider,
);
this.syncNotifierService = new SyncNotifierService();
this.organizationService = new OrganizationService(this.stateProvider);
this.policyService = new PolicyService(this.stateProvider, this.organizationService);
this.autofillSettingsService = new AutofillSettingsService(
this.stateProvider,
this.policyService,
);
this.badgeSettingsService = new BadgeSettingsService(this.stateProvider);
this.policyApiService = new PolicyApiService(this.policyService, this.apiService);
this.keyConnectorService = new KeyConnectorService(
this.stateService,
this.cryptoService,
this.apiService,
this.tokenService,
this.logService,
this.organizationService,
this.keyGenerationService,
logoutCallback,
);
this.passwordStrengthService = new PasswordStrengthService();
this.passwordGenerationService = new PasswordGenerationService(
this.cryptoService,
this.policyService,
this.stateService,
);
this.twoFactorService = new TwoFactorService(this.i18nService, this.platformUtilsService);
// eslint-disable-next-line
const that = this;
const backgroundMessagingService = new (class extends MessagingServiceAbstraction {
// AuthService should send the messages to the background not popup.
send = (subscriber: string, arg: any = {}) => {
const message = Object.assign({}, { command: subscriber }, arg);
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
that.runtimeBackground.processMessage(message, that as any);
};
})();
this.userDecryptionOptionsService = new UserDecryptionOptionsService(this.stateProvider);
this.devicesApiService = new DevicesApiServiceImplementation(this.apiService);
this.deviceTrustCryptoService = new DeviceTrustCryptoService(
this.keyGenerationService,
this.cryptoFunctionService,
this.cryptoService,
this.encryptService,
this.stateService,
this.appIdService,
this.devicesApiService,
this.i18nService,
this.platformUtilsService,
this.userDecryptionOptionsService,
);
this.devicesService = new DevicesServiceImplementation(this.devicesApiService);
this.authRequestService = new AuthRequestService(
this.appIdService,
this.cryptoService,
this.apiService,
this.stateService,
);
this.authService = new AuthService(
this.accountService,
backgroundMessagingService,
this.cryptoService,
this.apiService,
this.stateService,
);
this.billingAccountProfileStateService = new DefaultBillingAccountProfileStateService(
this.stateProvider,
);
this.loginStrategyService = new LoginStrategyService(
this.cryptoService,
this.apiService,
this.tokenService,
this.appIdService,
this.platformUtilsService,
backgroundMessagingService,
this.logService,
this.keyConnectorService,
this.environmentService,
this.stateService,
this.twoFactorService,
this.i18nService,
this.encryptService,
this.passwordStrengthService,
this.policyService,
this.deviceTrustCryptoService,
this.authRequestService,
this.userDecryptionOptionsService,
this.globalStateProvider,
this.billingAccountProfileStateService,
);
this.ssoLoginService = new SsoLoginService(this.stateProvider);
this.userVerificationApiService = new UserVerificationApiService(this.apiService);
this.configApiService = new ConfigApiService(this.apiService, this.authService);
this.configService = new BrowserConfigService(
this.stateService,
this.configApiService,
this.authService,
this.environmentService,
this.logService,
this.stateProvider,
true,
);
this.cipherService = new CipherService(
this.cryptoService,
this.domainSettingsService,
this.apiService,
this.i18nService,
this.searchService,
this.stateService,
this.autofillSettingsService,
this.encryptService,
this.cipherFileUploadService,
this.configService,
);
this.folderService = new FolderService(
this.cryptoService,
this.i18nService,
this.cipherService,
this.stateService,
this.stateProvider,
);
this.folderApiService = new FolderApiService(this.folderService, this.apiService);
this.vaultTimeoutSettingsService = new VaultTimeoutSettingsService(
this.userDecryptionOptionsService,
this.cryptoService,
this.tokenService,
this.policyService,
this.stateService,
this.biometricStateService,
);
this.pinCryptoService = new PinCryptoService(
this.stateService,
this.cryptoService,
this.vaultTimeoutSettingsService,
this.logService,
);
this.userVerificationService = new UserVerificationService(
this.stateService,
this.cryptoService,
this.i18nService,
this.userVerificationApiService,
this.userDecryptionOptionsService,
this.pinCryptoService,
this.logService,
this.vaultTimeoutSettingsService,
this.platformUtilsService,
);
this.vaultFilterService = new VaultFilterService(
this.organizationService,
this.folderService,
this.cipherService,
this.collectionService,
this.policyService,
this.stateProvider,
this.accountService,
);
this.vaultSettingsService = new VaultSettingsService(this.stateProvider);
this.vaultTimeoutService = new VaultTimeoutService(
this.cipherService,
this.folderService,
this.collectionService,
this.cryptoService,
this.platformUtilsService,
this.messagingService,
this.searchService,
this.stateService,
this.authService,
this.vaultTimeoutSettingsService,
this.stateEventRunnerService,
lockedCallback,
logoutCallback,
);
this.containerService = new ContainerService(this.cryptoService, this.encryptService);
this.sendService = new BrowserSendService(
this.cryptoService,
this.i18nService,
this.keyGenerationService,
this.stateService,
);
this.sendApiService = new SendApiService(
this.apiService,
this.fileUploadService,
this.sendService,
);
this.avatarService = new AvatarService(this.apiService, this.stateProvider);
this.providerService = new ProviderService(this.stateProvider);
this.syncService = new SyncService(
this.apiService,
this.domainSettingsService,
this.folderService,
this.cipherService,
this.cryptoService,
this.collectionService,
this.messagingService,
this.policyService,
this.sendService,
this.logService,
this.keyConnectorService,
this.stateService,
this.providerService,
this.folderApiService,
this.organizationService,
this.sendApiService,
this.userDecryptionOptionsService,
this.avatarService,
logoutCallback,
this.billingAccountProfileStateService,
);
this.eventUploadService = new EventUploadService(
this.apiService,
this.stateProvider,
this.logService,
this.accountService,
);
this.eventCollectionService = new EventCollectionService(
this.cipherService,
this.stateProvider,
this.organizationService,
this.eventUploadService,
this.accountService,
);
this.totpService = new TotpService(this.cryptoFunctionService, this.logService);
this.autofillService = new AutofillService(
this.cipherService,
this.stateService,
this.autofillSettingsService,
this.totpService,
this.eventCollectionService,
this.logService,
this.domainSettingsService,
this.userVerificationService,
this.billingAccountProfileStateService,
);
this.auditService = new AuditService(this.cryptoFunctionService, this.apiService);
this.importApiService = new ImportApiService(this.apiService);
this.importService = new ImportService(
this.cipherService,
this.folderService,
this.importApiService,
this.i18nService,
this.collectionService,
this.cryptoService,
);
this.individualVaultExportService = new IndividualVaultExportService(
this.folderService,
this.cipherService,
this.cryptoService,
this.cryptoFunctionService,
this.stateService,
);
this.organizationVaultExportService = new OrganizationVaultExportService(
this.cipherService,
this.apiService,
this.cryptoService,
this.cryptoFunctionService,
this.stateService,
this.collectionService,
);
this.exportService = new VaultExportService(
this.individualVaultExportService,
this.organizationVaultExportService,
);
this.notificationsService = new NotificationsService(
this.logService,
this.syncService,
this.appIdService,
this.apiService,
this.environmentService,
logoutCallback,
this.stateService,
this.authService,
this.messagingService,
);
this.fido2Service = new Fido2Service();
this.fido2UserInterfaceService = new BrowserFido2UserInterfaceService(this.authService);
this.fido2AuthenticatorService = new Fido2AuthenticatorService(
this.cipherService,
this.fido2UserInterfaceService,
this.syncService,
this.logService,
);
this.fido2ClientService = new Fido2ClientService(
this.fido2AuthenticatorService,
this.configService,
this.authService,
this.vaultSettingsService,
this.domainSettingsService,
this.logService,
);
const systemUtilsServiceReloadCallback = () => {
const forceWindowReload =
this.platformUtilsService.isSafari() ||
this.platformUtilsService.isFirefox() ||
this.platformUtilsService.isOpera();
BrowserApi.reloadExtension(forceWindowReload ? self : null);
return Promise.resolve();
};
this.systemService = new SystemService(
this.messagingService,
this.platformUtilsService,
systemUtilsServiceReloadCallback,
this.stateService,
this.autofillSettingsService,
this.vaultTimeoutSettingsService,
this.biometricStateService,
);
// Other fields
this.isSafari = this.platformUtilsService.isSafari();
// Background
this.runtimeBackground = new RuntimeBackground(
this,
this.autofillService,
this.platformUtilsService as BrowserPlatformUtilsService,
this.i18nService,
this.notificationsService,
this.stateService,
this.autofillSettingsService,
this.systemService,
this.environmentService,
this.messagingService,
this.logService,
this.configService,
this.fido2Service,
);
this.nativeMessagingBackground = new NativeMessagingBackground(
this.cryptoService,
this.cryptoFunctionService,
this.runtimeBackground,
this.messagingService,
this.appIdService,
this.platformUtilsService,
this.stateService,
this.logService,
this.authService,
this.biometricStateService,
);
this.commandsBackground = new CommandsBackground(
this,
this.passwordGenerationService,
this.platformUtilsService,
this.vaultTimeoutService,
this.authService,
);
this.notificationBackground = new NotificationBackground(
this.autofillService,
this.cipherService,
this.authService,
this.policyService,
this.folderService,
this.stateService,
this.userNotificationSettingsService,
this.domainSettingsService,
this.environmentService,
this.logService,
themeStateService,
);
this.overlayBackground = new OverlayBackground(
this.cipherService,
this.autofillService,
this.authService,
this.environmentService,
this.domainSettingsService,
this.stateService,
this.autofillSettingsService,
this.i18nService,
this.platformUtilsService,
themeStateService,
);
this.filelessImporterBackground = new FilelessImporterBackground(
this.configService,
this.authService,
this.policyService,
this.notificationBackground,
this.importService,
this.syncService,
);
this.tabsBackground = new TabsBackground(
this,
this.notificationBackground,
this.overlayBackground,
);
if (!this.popupOnlyContext) {
const contextMenuClickedHandler = new ContextMenuClickedHandler(
(options) => this.platformUtilsService.copyToClipboard(options.text),
async (_tab) => {
const options = (await this.passwordGenerationService.getOptions())?.[0] ?? {};
const password = await this.passwordGenerationService.generatePassword(options);
this.platformUtilsService.copyToClipboard(password);
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.passwordGenerationService.addHistory(password);
},
async (tab, cipher) => {
this.loginToAutoFill = cipher;
if (tab == null) {
return;
}
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
BrowserApi.tabSendMessage(tab, {
command: "collectPageDetails",
tab: tab,
sender: "contextMenu",
});
},
this.authService,
this.cipherService,
this.stateService,
this.totpService,
this.eventCollectionService,
this.userVerificationService,
);
this.contextMenusBackground = new ContextMenusBackground(contextMenuClickedHandler);
}
this.idleBackground = new IdleBackground(
this.vaultTimeoutService,
this.stateService,
this.notificationsService,
this.accountService,
);
this.webRequestBackground = new WebRequestBackground(
this.platformUtilsService,
this.cipherService,
this.authService,
);
this.usernameGenerationService = new UsernameGenerationService(
this.cryptoService,
this.stateService,
this.apiService,
);
if (!this.popupOnlyContext) {
this.mainContextMenuHandler = new MainContextMenuHandler(
this.stateService,
this.autofillSettingsService,
this.i18nService,
this.logService,
this.billingAccountProfileStateService,
);
this.cipherContextMenuHandler = new CipherContextMenuHandler(
this.mainContextMenuHandler,
this.authService,
this.cipherService,
);
}
}
async bootstrap() {
this.containerService.attachToGlobal(window);
await this.stateService.init();
await this.vaultTimeoutService.init(true);
await (this.i18nService as I18nService).init();
await (this.eventUploadService as EventUploadService).init(true);
await this.runtimeBackground.init();
await this.notificationBackground.init();
this.filelessImporterBackground.init();
await this.commandsBackground.init();
this.configService.init();
this.twoFactorService.init();
await this.overlayBackground.init();
await this.tabsBackground.init();
if (!this.popupOnlyContext) {
this.contextMenusBackground?.init();
}
await this.idleBackground.init();
await this.webRequestBackground.init();
await this.fido2Service.init();
if (this.platformUtilsService.isFirefox() && !this.isPrivateMode) {
// Set Private Mode windows to the default icon - they do not share state with the background page
const privateWindows = await BrowserApi.getPrivateModeWindows();
privateWindows.forEach(async (win) => {
await new UpdateBadge(self).setBadgeIcon("", win.id);
});
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
BrowserApi.onWindowCreated(async (win) => {
if (win.incognito) {
await new UpdateBadge(self).setBadgeIcon("", win.id);
}
});
}
return new Promise<void>((resolve) => {
setTimeout(async () => {
if (!this.isPrivateMode) {
await this.refreshBadge();
}
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.fullSync(true);
setTimeout(() => this.notificationsService.init(), 2500);
resolve();
}, 500);
});
}
async refreshBadge() {
await new UpdateBadge(self).run({ existingServices: this as any });
}
async refreshMenu(forLocked = false) {
if (!chrome.windows || !chrome.contextMenus) {
return;
}
await MainContextMenuHandler.removeAll();
if (forLocked) {
await this.mainContextMenuHandler?.noAccess();
this.onUpdatedRan = this.onReplacedRan = false;
return;
}
await this.mainContextMenuHandler?.init();
const tab = await BrowserApi.getTabFromCurrentWindow();
if (tab) {
await this.cipherContextMenuHandler?.update(tab.url);
this.onUpdatedRan = this.onReplacedRan = false;
}
}
/**
* Switch accounts to indicated userId -- null is no active user
*/
async switchAccount(userId: UserId) {
try {
await this.stateService.setActiveUser(userId);
if (userId == null) {
await this.stateService.setRememberedEmail(null);
await this.refreshBadge();
await this.refreshMenu();
await this.overlayBackground.updateOverlayCiphers();
return;
}
const status = await this.authService.getAuthStatus(userId);
const forcePasswordReset =
(await this.stateService.getForceSetPasswordReason({ userId: userId })) !=
ForceSetPasswordReason.None;
await this.systemService.clearPendingClipboard();
await this.notificationsService.updateConnection(false);
if (status === AuthenticationStatus.Locked) {
this.messagingService.send("locked", { userId: userId });
} else if (forcePasswordReset) {
this.messagingService.send("update-temp-password", { userId: userId });
} else {
this.messagingService.send("unlocked", { userId: userId });
await this.refreshBadge();
await this.refreshMenu();
await this.overlayBackground.updateOverlayCiphers();
await this.syncService.fullSync(false);
}
} finally {
this.messagingService.send("switchAccountFinish", { userId: userId });
}
}
async logout(expired: boolean, userId?: UserId) {
userId ??= (await firstValueFrom(this.accountService.activeAccount$))?.id;
await this.eventUploadService.uploadEvents(userId as UserId);
await Promise.all([
this.syncService.setLastSync(new Date(0), userId),
this.cryptoService.clearKeys(userId),
this.cipherService.clear(userId),
this.folderService.clear(userId),
this.collectionService.clear(userId),
this.policyService.clear(userId),
this.passwordGenerationService.clear(userId),
this.vaultTimeoutSettingsService.clear(userId),
this.keyConnectorService.clear(),
this.vaultFilterService.clear(),
this.biometricStateService.logout(userId),
this.providerService.save(null, userId),
/* We intentionally do not clear:
* - autofillSettingsService
* - badgeSettingsService
* - userNotificationSettingsService
*/
]);
//Needs to be checked before state is cleaned
const needStorageReseed = await this.needsStorageReseed();
const currentUserId = await this.stateService.getUserId();
const newActiveUser = await this.stateService.clean({ userId: userId });
if (userId == null || userId === currentUserId) {
this.searchService.clearIndex();
}
await this.stateEventRunnerService.handleEvent("logout", currentUserId as UserId);
if (newActiveUser != null) {
// we have a new active user, do not continue tearing down application
await this.switchAccount(newActiveUser as UserId);
this.messagingService.send("switchAccountFinish");
} else {
this.messagingService.send("doneLoggingOut", { expired: expired, userId: userId });
}
if (needStorageReseed) {
await this.reseedStorage();
}
if (BrowserApi.isManifestVersion(3)) {
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
BrowserApi.sendMessage("updateBadge");
}
await this.refreshBadge();
await this.mainContextMenuHandler.noAccess();
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.notificationsService.updateConnection(false);
await this.systemService.clearPendingClipboard();
await this.systemService.startProcessReload(this.authService);
}
private async needsStorageReseed(): Promise<boolean> {
const currentVaultTimeout = await this.stateService.getVaultTimeout();
return currentVaultTimeout == null ? false : true;
}
async collectPageDetailsForContentScript(tab: any, sender: string, frameId: number = null) {
if (tab == null || !tab.id) {
return;
}
const options: any = {};
if (frameId != null) {
options.frameId = frameId;
}
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
// eslint-disable-next-line @typescript-eslint/no-floating-promises
BrowserApi.tabSendMessage(
tab,
{
command: "collectPageDetails",
tab: tab,
sender: sender,
},
options,
);
}
async openPopup() {
// Chrome APIs cannot open popup
// TODO: Do we need to open this popup?
if (!this.isSafari) {
return;
}
await SafariApp.sendMessageToApp("showPopover", null, true);
}
async reseedStorage() {
if (
!this.platformUtilsService.isChrome() &&
!this.platformUtilsService.isVivaldi() &&
!this.platformUtilsService.isOpera()
) {
return;
}
const getStorage = (): Promise<any> =>
new Promise((resolve) => {
chrome.storage.local.get(null, (o: any) => resolve(o));
});
const clearStorage = (): Promise<void> =>
new Promise((resolve) => {
chrome.storage.local.clear(() => resolve());
});
const storage = await getStorage();
await clearStorage();
for (const key in storage) {
// eslint-disable-next-line
if (!storage.hasOwnProperty(key)) {
continue;
}
await this.storageService.save(key, storage[key]);
}
}
async clearClipboard(clipboardValue: string, clearMs: number) {
if (this.systemService != null) {
await this.systemService.clearClipboard(clipboardValue, clearMs);
}
}
async biometricUnlock(): Promise<boolean> {
if (this.nativeMessagingBackground == null) {
return false;
}
const responsePromise = this.nativeMessagingBackground.getResponse();
await this.nativeMessagingBackground.send({ command: "biometricUnlock" });
const response = await responsePromise;
return response.response === "unlocked";
}
private async fullSync(override = false) {
const syncInternal = 6 * 60 * 60 * 1000; // 6 hours
const lastSync = await this.syncService.getLastSync();
let lastSyncAgo = syncInternal + 1;
if (lastSync != null) {
lastSyncAgo = new Date().getTime() - lastSync.getTime();
}
if (override || lastSyncAgo >= syncInternal) {
await this.syncService.fullSync(override);
this.scheduleNextSync();
} else {
this.scheduleNextSync();
}
}
private scheduleNextSync() {
if (this.syncTimeout) {
clearTimeout(this.syncTimeout);
}
this.syncTimeout = setTimeout(async () => await this.fullSync(), 5 * 60 * 1000); // check every 5 minutes
}
}