1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-11 14:04:03 +00:00

Add SecureStorageService

This commit is contained in:
Justin Baur
2025-01-22 15:20:12 -05:00
parent ba42d31b6f
commit e9464d09ff
9 changed files with 134 additions and 6 deletions

View File

@@ -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,

View File

@@ -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({

View File

@@ -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,

View File

@@ -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({

View File

@@ -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>;
}

View File

@@ -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({

View File

@@ -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,

View File

@@ -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>;
}

View 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>;
}