mirror of
https://github.com/bitwarden/browser
synced 2026-02-11 14:04:03 +00:00
Add SecureStorageService
This commit is contained in:
@@ -274,6 +274,7 @@ import CommandsBackground from "./commands.background";
|
||||
import IdleBackground from "./idle.background";
|
||||
import { NativeMessagingBackground } from "./nativeMessaging.background";
|
||||
import RuntimeBackground from "./runtime.background";
|
||||
import { UnsupportedSecureStorageService } from "@bitwarden/common/platform/storage/secure-storage.service";
|
||||
|
||||
export default class MainBackground {
|
||||
messagingService: MessageSender;
|
||||
@@ -585,11 +586,12 @@ export default class MainBackground {
|
||||
|
||||
this.userNotificationSettingsService = new UserNotificationSettingsService(this.stateProvider);
|
||||
|
||||
const secureStorage = new UnsupportedSecureStorageService("no-browser-api");
|
||||
|
||||
this.tokenService = new TokenService(
|
||||
this.singleUserStateProvider,
|
||||
this.globalStateProvider,
|
||||
this.platformUtilsService,
|
||||
this.secureStorageService,
|
||||
secureStorage,
|
||||
this.keyGenerationService,
|
||||
this.encryptService,
|
||||
this.logService,
|
||||
|
||||
@@ -159,6 +159,10 @@ import { VaultFilterService } from "../../vault/services/vault-filter.service";
|
||||
import { DebounceNavigationService } from "./debounce-navigation.service";
|
||||
import { InitService } from "./init.service";
|
||||
import { PopupCloseWarningService } from "./popup-close-warning.service";
|
||||
import {
|
||||
SecureStorageService,
|
||||
UnsupportedSecureStorageService,
|
||||
} from "@bitwarden/common/platform/storage/secure-storage.service";
|
||||
|
||||
const OBSERVABLE_LARGE_OBJECT_MEMORY_STORAGE = new SafeInjectionToken<
|
||||
AbstractStorageService & ObservableStorageService
|
||||
@@ -596,6 +600,11 @@ const safeProviders: SafeProvider[] = [
|
||||
useClass: ExtensionLoginDecryptionOptionsService,
|
||||
deps: [MessagingServiceAbstraction, Router],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: SecureStorageService,
|
||||
useFactory: () => new UnsupportedSecureStorageService("no-browser-api"),
|
||||
deps: [],
|
||||
}),
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -173,6 +173,7 @@ import { I18nService } from "../platform/services/i18n.service";
|
||||
import { LowdbStorageService } from "../platform/services/lowdb-storage.service";
|
||||
import { NodeApiService } from "../platform/services/node-api.service";
|
||||
import { NodeEnvSecureStorageService } from "../platform/services/node-env-secure-storage.service";
|
||||
import { UnsupportedSecureStorageService } from "@bitwarden/common/platform/storage/secure-storage.service";
|
||||
|
||||
// Polyfills
|
||||
global.DOMParser = new jsdom.JSDOM().window.DOMParser;
|
||||
@@ -372,11 +373,14 @@ export class ServiceContainer {
|
||||
|
||||
this.keyGenerationService = new KeyGenerationService(this.cryptoFunctionService);
|
||||
|
||||
// TODO: CLI _DOES_ have a secure storage implementation but we report it as not supported
|
||||
// in PlatformUtilsService.supportsSecureStorage
|
||||
const secureStorage = new UnsupportedSecureStorageService("i-dont-know");
|
||||
|
||||
this.tokenService = new TokenService(
|
||||
this.singleUserStateProvider,
|
||||
this.globalStateProvider,
|
||||
this.platformUtilsService,
|
||||
this.secureStorageService,
|
||||
secureStorage,
|
||||
this.keyGenerationService,
|
||||
this.encryptService,
|
||||
this.logService,
|
||||
|
||||
@@ -127,6 +127,11 @@ import { DesktopThemeStateService } from "./desktop-theme.service";
|
||||
import { InitService } from "./init.service";
|
||||
import { NativeMessagingManifestService } from "./native-messaging-manifest.service";
|
||||
import { RendererCryptoFunctionService } from "./renderer-crypto-function.service";
|
||||
import {
|
||||
SecureStorageService,
|
||||
SupportedSecureStorageService,
|
||||
} from "@bitwarden/common/platform/storage/secure-storage.service";
|
||||
import { PortableSecureStorageService } from "../../platform/services/portable-secure-storage.service";
|
||||
|
||||
const RELOAD_CALLBACK = new SafeInjectionToken<() => any>("RELOAD_CALLBACK");
|
||||
|
||||
@@ -397,6 +402,17 @@ const safeProviders: SafeProvider[] = [
|
||||
useClass: DesktopLoginApprovalComponentService,
|
||||
deps: [I18nServiceAbstraction],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: SecureStorageService,
|
||||
useFactory: (secureStorage: AbstractStorageService) => {
|
||||
if (ipc.platform.isWindowsPortable) {
|
||||
return new PortableSecureStorageService(secureStorage);
|
||||
}
|
||||
|
||||
return new SupportedSecureStorageService(secureStorage);
|
||||
},
|
||||
deps: [SECURE_STORAGE],
|
||||
}),
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Observable, of } from "rxjs";
|
||||
|
||||
import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
|
||||
import {
|
||||
SecureStorageService,
|
||||
SupportStatus,
|
||||
} from "@bitwarden/common/platform/storage/secure-storage.service";
|
||||
|
||||
export class PortableSecureStorageService implements SecureStorageService {
|
||||
constructor(storageService: AbstractStorageService) {
|
||||
this.support$ = of({
|
||||
type: "not-preferred",
|
||||
service: storageService,
|
||||
reason: "portable-desktop",
|
||||
} satisfies SupportStatus);
|
||||
}
|
||||
|
||||
support$: Observable<SupportStatus>;
|
||||
}
|
||||
@@ -120,6 +120,10 @@ import { ModalService } from "./modal.service";
|
||||
import { RouterService } from "./router.service";
|
||||
import { WebFileDownloadService } from "./web-file-download.service";
|
||||
import { WebPlatformUtilsService } from "./web-platform-utils.service";
|
||||
import {
|
||||
SecureStorageService,
|
||||
UnsupportedSecureStorageService,
|
||||
} from "@bitwarden/common/platform/storage/secure-storage.service";
|
||||
|
||||
/**
|
||||
* Provider definitions used in the ngModule.
|
||||
@@ -313,6 +317,11 @@ const safeProviders: SafeProvider[] = [
|
||||
useClass: WebLoginDecryptionOptionsService,
|
||||
deps: [MessagingService, RouterService, AcceptOrganizationInviteService],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: SecureStorageService,
|
||||
useFactory: () => new UnsupportedSecureStorageService("no-web-api"),
|
||||
deps: [],
|
||||
}),
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -335,6 +335,7 @@ import {
|
||||
ENV_ADDITIONAL_REGIONS,
|
||||
} from "./injection-tokens";
|
||||
import { ModalService } from "./modal.service";
|
||||
import { SecureStorageService } from "@bitwarden/common/platform/storage/secure-storage.service";
|
||||
|
||||
/**
|
||||
* Provider definitions used in the ngModule.
|
||||
@@ -593,8 +594,7 @@ const safeProviders: SafeProvider[] = [
|
||||
deps: [
|
||||
SingleUserStateProvider,
|
||||
GlobalStateProvider,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
SECURE_STORAGE,
|
||||
SecureStorageService,
|
||||
KeyGenerationServiceAbstraction,
|
||||
EncryptService,
|
||||
LogService,
|
||||
|
||||
@@ -43,6 +43,11 @@ export abstract class PlatformUtilsService {
|
||||
abstract isSelfHost(): boolean;
|
||||
abstract copyToClipboard(text: string, options?: ClipboardOptions): void | boolean;
|
||||
abstract readFromClipboard(): Promise<string>;
|
||||
|
||||
/**
|
||||
* @deprecated Use `SecureStorageService.support$` to check the current support status of SecureStorage
|
||||
* on the current client.
|
||||
*/
|
||||
abstract supportsSecureStorage(): boolean;
|
||||
abstract getAutofillKeyboardShortcut(): Promise<string>;
|
||||
}
|
||||
|
||||
64
libs/common/src/platform/storage/secure-storage.service.ts
Normal file
64
libs/common/src/platform/storage/secure-storage.service.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { Observable, of } from "rxjs";
|
||||
|
||||
import { AbstractStorageService } from "../abstractions/storage.service";
|
||||
|
||||
export type SupportStatus =
|
||||
| {
|
||||
type: "supported";
|
||||
service: AbstractStorageService;
|
||||
}
|
||||
| { type: "not-preferred"; service: AbstractStorageService; reason: string }
|
||||
| { type: "needs-configuration"; reason: string }
|
||||
| { type: "not-supported"; reason: string };
|
||||
|
||||
export abstract class SecureStorageService {
|
||||
/**
|
||||
* Returns an observable stream of a Rust-like enum showing if secure storage is
|
||||
* supported for a given platform.
|
||||
*
|
||||
* If the type is `supported` then the service property _should_ be used to store
|
||||
* security sensitive information, particularly keys and access tokens that need
|
||||
* to be available after a restart of the application.
|
||||
*
|
||||
* If the type is `not-preferred` then the service property should be used only for
|
||||
* retrieval of existing data and clearing of data. But should not be used for storage
|
||||
* of new data. This means that the a secure storage implementation exists but for some
|
||||
* reason the usage of it might degrade user experience. An example of this is our
|
||||
* desktop app operating in its portable mode. The system API's are available to make
|
||||
* secure storage work but it makes it impossible to login on one machine, then move
|
||||
* the application to another machine and unlock there.
|
||||
*
|
||||
* If the type is `needs-configuration` then secure storage cannot be used at all. For
|
||||
* purposes of using the API this type is the same as `not-supported` but you may utilize
|
||||
* this type to add a callout somewhere in the application so that the documentation can
|
||||
* be linked to. This documentation can then help instruct the user on what to do so that
|
||||
* they can start using secure storage.
|
||||
*
|
||||
* If the type is `not-supported` then secure storage is not available and cannot be used.
|
||||
* Depending on the feature, then you may need to not allow a feature to be used or you
|
||||
* will need to fallback to using insecure, disk based storage.
|
||||
*/
|
||||
support$: Observable<SupportStatus>;
|
||||
}
|
||||
|
||||
export class UnsupportedSecureStorageService implements SecureStorageService {
|
||||
constructor(reason: string) {
|
||||
this.support$ = of({
|
||||
type: "not-supported",
|
||||
reason: reason,
|
||||
} satisfies SupportStatus);
|
||||
}
|
||||
|
||||
support$: Observable<SupportStatus>;
|
||||
}
|
||||
|
||||
export class SupportedSecureStorageService implements SecureStorageService {
|
||||
constructor(storageService: AbstractStorageService) {
|
||||
this.support$ = of({
|
||||
type: "supported",
|
||||
service: storageService,
|
||||
} satisfies SupportStatus);
|
||||
}
|
||||
|
||||
support$: Observable<SupportStatus>;
|
||||
}
|
||||
Reference in New Issue
Block a user