diff --git a/apps/browser/src/autofill/content/autofill.js b/apps/browser/src/autofill/content/autofill.js
index 2f3857d3fa8..ef0fb73408b 100644
--- a/apps/browser/src/autofill/content/autofill.js
+++ b/apps/browser/src/autofill/content/autofill.js
@@ -993,11 +993,6 @@
function fillTheElement(el, op) {
var shouldCheck;
if (el && null !== op && void 0 !== op && !(el.disabled || el.a || el.readOnly)) {
- const tabURLChanged = !fillScript.savedUrls?.some(url => url.startsWith(window.location.origin))
- // Check to make sure the page location didn't change
- if (tabURLChanged) {
- return;
- }
switch (markTheFilling && el.form && !el.form.opfilled && (el.form.opfilled = true),
el.type ? el.type.toLowerCase() : null) {
case 'checkbox':
diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts
index 0ab74875fbf..828d768ca25 100644
--- a/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts
+++ b/apps/browser/src/autofill/services/insert-autofill-content.service.spec.ts
@@ -108,7 +108,6 @@ describe("InsertAutofillContentService", () => {
jest.spyOn(insertAutofillContentService as any, "fillingWithinSandboxedIframe");
jest.spyOn(insertAutofillContentService as any, "userCancelledInsecureUrlAutofill");
jest.spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill");
- jest.spyOn(insertAutofillContentService as any, "tabURLChanged");
jest.spyOn(insertAutofillContentService as any, "runFillScriptAction");
insertAutofillContentService.fillForm(fillScript);
@@ -120,7 +119,6 @@ describe("InsertAutofillContentService", () => {
expect(
insertAutofillContentService["userCancelledUntrustedIframeAutofill"]
).not.toHaveBeenCalled();
- expect(insertAutofillContentService["tabURLChanged"]).not.toHaveBeenCalled();
expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled();
});
@@ -130,7 +128,6 @@ describe("InsertAutofillContentService", () => {
.mockReturnValue(true);
jest.spyOn(insertAutofillContentService as any, "userCancelledInsecureUrlAutofill");
jest.spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill");
- jest.spyOn(insertAutofillContentService as any, "tabURLChanged");
jest.spyOn(insertAutofillContentService as any, "runFillScriptAction");
insertAutofillContentService.fillForm(fillScript);
@@ -142,7 +139,6 @@ describe("InsertAutofillContentService", () => {
expect(
insertAutofillContentService["userCancelledUntrustedIframeAutofill"]
).not.toHaveBeenCalled();
- expect(insertAutofillContentService["tabURLChanged"]).not.toHaveBeenCalled();
expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled();
});
@@ -154,7 +150,6 @@ describe("InsertAutofillContentService", () => {
.spyOn(insertAutofillContentService as any, "userCancelledInsecureUrlAutofill")
.mockReturnValue(true);
jest.spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill");
- jest.spyOn(insertAutofillContentService as any, "tabURLChanged");
jest.spyOn(insertAutofillContentService as any, "runFillScriptAction");
insertAutofillContentService.fillForm(fillScript);
@@ -164,7 +159,6 @@ describe("InsertAutofillContentService", () => {
expect(
insertAutofillContentService["userCancelledUntrustedIframeAutofill"]
).not.toHaveBeenCalled();
- expect(insertAutofillContentService["tabURLChanged"]).not.toHaveBeenCalled();
expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled();
});
@@ -178,7 +172,6 @@ describe("InsertAutofillContentService", () => {
jest
.spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill")
.mockReturnValue(true);
- jest.spyOn(insertAutofillContentService as any, "tabURLChanged").mockReturnValue(false);
jest.spyOn(insertAutofillContentService as any, "runFillScriptAction");
insertAutofillContentService.fillForm(fillScript);
@@ -188,31 +181,6 @@ describe("InsertAutofillContentService", () => {
expect(
insertAutofillContentService["userCancelledUntrustedIframeAutofill"]
).toHaveBeenCalled();
- expect(insertAutofillContentService["tabURLChanged"]).not.toHaveBeenCalled();
- expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled();
- });
-
- it("returns early if the page location origin does not match against any of the cipher saved URLs", () => {
- jest
- .spyOn(insertAutofillContentService as any, "fillingWithinSandboxedIframe")
- .mockReturnValue(false);
- jest
- .spyOn(insertAutofillContentService as any, "userCancelledInsecureUrlAutofill")
- .mockReturnValue(false);
- jest
- .spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill")
- .mockReturnValue(false);
- jest.spyOn(insertAutofillContentService as any, "tabURLChanged").mockReturnValue(true);
- jest.spyOn(insertAutofillContentService as any, "runFillScriptAction");
-
- insertAutofillContentService.fillForm(fillScript);
-
- expect(insertAutofillContentService["fillingWithinSandboxedIframe"]).toHaveBeenCalled();
- expect(insertAutofillContentService["userCancelledInsecureUrlAutofill"]).toHaveBeenCalled();
- expect(
- insertAutofillContentService["userCancelledUntrustedIframeAutofill"]
- ).toHaveBeenCalled();
- expect(insertAutofillContentService["tabURLChanged"]).toHaveBeenCalled();
expect(insertAutofillContentService["runFillScriptAction"]).not.toHaveBeenCalled();
});
@@ -226,7 +194,6 @@ describe("InsertAutofillContentService", () => {
jest
.spyOn(insertAutofillContentService as any, "userCancelledUntrustedIframeAutofill")
.mockReturnValue(false);
- jest.spyOn(insertAutofillContentService as any, "tabURLChanged").mockReturnValue(false);
jest.spyOn(insertAutofillContentService as any, "runFillScriptAction");
insertAutofillContentService.fillForm(fillScript);
@@ -236,7 +203,6 @@ describe("InsertAutofillContentService", () => {
expect(
insertAutofillContentService["userCancelledUntrustedIframeAutofill"]
).toHaveBeenCalled();
- expect(insertAutofillContentService["tabURLChanged"]).toHaveBeenCalled();
expect(insertAutofillContentService["runFillScriptAction"]).toHaveBeenCalledTimes(3);
expect(insertAutofillContentService["runFillScriptAction"]).toHaveBeenNthCalledWith(
1,
diff --git a/apps/browser/src/autofill/services/insert-autofill-content.service.ts b/apps/browser/src/autofill/services/insert-autofill-content.service.ts
index ad40b76fbcd..46cb53d4f59 100644
--- a/apps/browser/src/autofill/services/insert-autofill-content.service.ts
+++ b/apps/browser/src/autofill/services/insert-autofill-content.service.ts
@@ -38,8 +38,7 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf
!fillScript.script?.length ||
this.fillingWithinSandboxedIframe() ||
this.userCancelledInsecureUrlAutofill(fillScript.savedUrls) ||
- this.userCancelledUntrustedIframeAutofill(fillScript) ||
- this.tabURLChanged(fillScript.savedUrls)
+ this.userCancelledUntrustedIframeAutofill(fillScript)
) {
return;
}
@@ -47,16 +46,6 @@ class InsertAutofillContentService implements InsertAutofillContentServiceInterf
fillScript.script.forEach(this.runFillScriptAction);
}
- /**
- * Determines if the page URL no longer matches one of the cipher's savedURL domains
- * @param {string[] | null} savedUrls
- * @returns {boolean}
- * @private
- */
- private tabURLChanged(savedUrls?: AutofillScript["savedUrls"]): boolean {
- return savedUrls && !savedUrls.some((url) => url.startsWith(window.location.origin));
- }
-
/**
* Identifies if the execution of this script is happening
* within a sandboxed iframe.
diff --git a/apps/browser/src/vault/popup/components/vault/view.component.ts b/apps/browser/src/vault/popup/components/vault/view.component.ts
index 6c9f3967d56..29027b33505 100644
--- a/apps/browser/src/vault/popup/components/vault/view.component.ts
+++ b/apps/browser/src/vault/popup/components/vault/view.component.ts
@@ -331,11 +331,21 @@ export class ViewComponent extends BaseViewComponent {
}
private async doAutofill() {
+ const originalTabURL = this.tab.url?.length && new URL(this.tab.url);
+
if (!(await this.promptPassword())) {
return false;
}
- if (this.pageDetails == null || this.pageDetails.length === 0) {
+ const currentTabURL = this.tab.url?.length && new URL(this.tab.url);
+
+ const originalTabHostPath =
+ originalTabURL && `${originalTabURL.origin}${originalTabURL.pathname}`;
+ const currentTabHostPath = currentTabURL && `${currentTabURL.origin}${currentTabURL.pathname}`;
+
+ const tabUrlChanged = originalTabHostPath !== currentTabHostPath;
+
+ if (this.pageDetails == null || this.pageDetails.length === 0 || tabUrlChanged) {
this.platformUtilsService.showToast("error", null, this.i18nService.t("autofillError"));
return false;
}
diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json
index 0830fabf13d..69d1c0074fa 100644
--- a/apps/desktop/electron-builder.json
+++ b/apps/desktop/electron-builder.json
@@ -19,7 +19,7 @@
"**/node_modules/@bitwarden/desktop-native/index.js",
"**/node_modules/@bitwarden/desktop-native/desktop_native.${platform}-${arch}*.node"
],
- "electronVersion": "26.3.0",
+ "electronVersion": "25.9.1",
"generateUpdatesFilesForAllChannels": true,
"publish": {
"provider": "generic",
diff --git a/apps/desktop/package.json b/apps/desktop/package.json
index 444753e9206..68d2cc0f87f 100644
--- a/apps/desktop/package.json
+++ b/apps/desktop/package.json
@@ -1,7 +1,7 @@
{
"name": "@bitwarden/desktop",
"description": "A secure and free password manager for all of your devices.",
- "version": "2023.9.2",
+ "version": "2023.9.3",
"keywords": [
"bitwarden",
"password",
@@ -19,8 +19,10 @@
"postinstall": "electron-rebuild",
"start": "cross-env ELECTRON_IS_DEV=0 ELECTRON_NO_UPDATER=1 electron ./build",
"build-native": "cd desktop_native && npm run build",
- "build": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\"",
+ "build": "concurrently -n Main,Rend,Prel -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\" \"npm run build:preload\"",
"build:dev": "concurrently -n Main,Rend -c yellow,cyan \"npm run build:main:dev\" \"npm run build:renderer:dev\"",
+ "build:preload": "cross-env NODE_ENV=production webpack --config webpack.preload.js",
+ "build:preload:watch": "cross-env NODE_ENV=production webpack --config webpack.preload.js --watch",
"build:main": "cross-env NODE_ENV=production webpack --config webpack.main.js",
"build:main:dev": "npm run build-native && cross-env NODE_ENV=development webpack --config webpack.main.js",
"build:main:watch": "npm run build-native && cross-env NODE_ENV=development webpack --config webpack.main.js --watch",
diff --git a/apps/desktop/scripts/start.js b/apps/desktop/scripts/start.js
index 19b11125061..388bf09405c 100644
--- a/apps/desktop/scripts/start.js
+++ b/apps/desktop/scripts/start.js
@@ -13,6 +13,11 @@ concurrently(
command: "npm run build:main:watch",
prefixColor: "yellow",
},
+ {
+ name: "Prel",
+ command: "npm run build:preload:watch",
+ prefixColor: "magenta",
+ },
{
name: "Rend",
command: "npm run build:renderer:watch",
diff --git a/apps/desktop/src/app/accounts/settings.component.ts b/apps/desktop/src/app/accounts/settings.component.ts
index 7ccf4aca770..a2a9e71a32b 100644
--- a/apps/desktop/src/app/accounts/settings.component.ts
+++ b/apps/desktop/src/app/accounts/settings.component.ts
@@ -21,7 +21,6 @@ import { DialogService } from "@bitwarden/components";
import { flagEnabled } from "../../platform/flags";
import { ElectronStateService } from "../../platform/services/electron-state.service.abstraction";
-import { isWindowsStore } from "../../utils";
import { SetPinComponent } from "../components/set-pin.component";
@Component({
selector: "app-settings",
@@ -589,7 +588,7 @@ export class SettingsComponent implements OnInit {
this.form.controls.enableBrowserIntegration.setValue(false);
return;
- } else if (isWindowsStore()) {
+ } else if (ipc.platform.isWindowsStore) {
await this.dialogService.openSimpleDialog({
title: { key: "browserIntegrationUnsupportedTitle" },
content: { key: "browserIntegrationWindowsStoreDesc" },
diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts
index 34262c3a309..65ac83b59fd 100644
--- a/apps/desktop/src/app/app.component.ts
+++ b/apps/desktop/src/app/app.component.ts
@@ -10,7 +10,6 @@ import {
} from "@angular/core";
import { DomSanitizer } from "@angular/platform-browser";
import { Router } from "@angular/router";
-import { ipcRenderer } from "electron";
import { IndividualConfig, ToastrService } from "ngx-toastr";
import { firstValueFrom, Subject, takeUntil } from "rxjs";
@@ -227,7 +226,7 @@ export class AppComponent implements OnInit, OnDestroy {
this.systemService.cancelProcessReload();
break;
case "reloadProcess":
- ipcRenderer.send("reload-process");
+ ipc.platform.reloadProcess();
break;
case "syncStarted":
break;
diff --git a/apps/desktop/src/app/main.ts b/apps/desktop/src/app/main.ts
index 7d99e48ea22..c22d4eb9e10 100644
--- a/apps/desktop/src/app/main.ts
+++ b/apps/desktop/src/app/main.ts
@@ -1,8 +1,12 @@
import { enableProdMode } from "@angular/core";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
+import { ipc } from "../preload";
import { isDev } from "../utils";
+// Temporary polyfill for preload script
+(window as any).ipc = ipc;
+
require("../scss/styles.scss");
require("../scss/tailwind.css");
diff --git a/apps/desktop/src/app/services/desktop-theming.service.ts b/apps/desktop/src/app/services/desktop-theming.service.ts
index 21277dfd736..3157ad9f661 100644
--- a/apps/desktop/src/app/services/desktop-theming.service.ts
+++ b/apps/desktop/src/app/services/desktop-theming.service.ts
@@ -1,5 +1,4 @@
import { Injectable } from "@angular/core";
-import { ipcRenderer } from "electron";
import { ThemingService } from "@bitwarden/angular/services/theming/theming.service";
import { ThemeType } from "@bitwarden/common/enums";
@@ -7,12 +6,10 @@ import { ThemeType } from "@bitwarden/common/enums";
@Injectable()
export class DesktopThemingService extends ThemingService {
protected async getSystemTheme(): Promise
{
- return await ipcRenderer.invoke("systemTheme");
+ return await ipc.platform.getSystemTheme();
}
protected monitorSystemThemeChanges(): void {
- ipcRenderer.on("systemThemeUpdated", (_event, theme: ThemeType) =>
- this.updateSystemTheme(theme)
- );
+ ipc.platform.onSystemThemeUpdated((theme: ThemeType) => this.updateSystemTheme(theme));
}
}
diff --git a/apps/desktop/src/auth/two-factor.component.html b/apps/desktop/src/auth/two-factor.component.html
index cd21f91f59e..2b9a1722ee0 100644
--- a/apps/desktop/src/auth/two-factor.component.html
+++ b/apps/desktop/src/auth/two-factor.component.html
@@ -83,7 +83,10 @@ {{ title }}
"
>
-
+
diff --git a/apps/desktop/src/global.d.ts b/apps/desktop/src/global.d.ts
index 1b85bb1b6b1..4d103b2cdef 100644
--- a/apps/desktop/src/global.d.ts
+++ b/apps/desktop/src/global.d.ts
@@ -1 +1,2 @@
declare module "forcefocus";
+declare const ipc: typeof import("./preload").ipc;
diff --git a/apps/desktop/src/main/window.main.ts b/apps/desktop/src/main/window.main.ts
index 4ea28c74c5f..e326b9e9e1d 100644
--- a/apps/desktop/src/main/window.main.ts
+++ b/apps/desktop/src/main/window.main.ts
@@ -143,6 +143,7 @@ export class WindowMain {
backgroundColor: await this.getBackgroundColor(),
alwaysOnTop: this.enableAlwaysOnTop,
webPreferences: {
+ // preload: path.join(__dirname, "preload.js"),
spellcheck: false,
nodeIntegration: true,
backgroundThrottling: false,
diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json
index 6112806d32a..bf2744cd873 100644
--- a/apps/desktop/src/package-lock.json
+++ b/apps/desktop/src/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@bitwarden/desktop",
- "version": "2023.9.2",
+ "version": "2023.9.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@bitwarden/desktop",
- "version": "2023.9.2",
+ "version": "2023.9.3",
"license": "GPL-3.0",
"dependencies": {
"@bitwarden/desktop-native": "file:../desktop_native"
diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json
index 3f4163c160a..9421f7a5277 100644
--- a/apps/desktop/src/package.json
+++ b/apps/desktop/src/package.json
@@ -2,7 +2,7 @@
"name": "@bitwarden/desktop",
"productName": "Bitwarden",
"description": "A secure and free password manager for all of your devices.",
- "version": "2023.9.2",
+ "version": "2023.9.3",
"author": "Bitwarden Inc.
(https://bitwarden.com)",
"homepage": "https://bitwarden.com",
"license": "GPL-3.0",
diff --git a/apps/desktop/src/platform/preload.ts b/apps/desktop/src/platform/preload.ts
new file mode 100644
index 00000000000..1ea4f3b91b4
--- /dev/null
+++ b/apps/desktop/src/platform/preload.ts
@@ -0,0 +1,31 @@
+import { ipcRenderer } from "electron";
+
+import { DeviceType, ThemeType } from "@bitwarden/common/enums";
+
+import { isDev, isWindowsStore } from "../utils";
+
+export default {
+ versions: {
+ app: (): Promise => ipcRenderer.invoke("appVersion"),
+ },
+ deviceType: deviceType(),
+ isDev: isDev(),
+ isWindowsStore: isWindowsStore(),
+ reloadProcess: () => ipcRenderer.send("reload-process"),
+
+ getSystemTheme: (): Promise => ipcRenderer.invoke("systemTheme"),
+ onSystemThemeUpdated: (callback: (theme: ThemeType) => void) => {
+ ipcRenderer.on("systemThemeUpdated", (_event, theme: ThemeType) => callback(theme));
+ },
+};
+
+function deviceType(): DeviceType {
+ switch (process.platform) {
+ case "win32":
+ return DeviceType.WindowsDesktop;
+ case "darwin":
+ return DeviceType.MacOsDesktop;
+ default:
+ return DeviceType.LinuxDesktop;
+ }
+}
diff --git a/apps/desktop/src/platform/services/electron-platform-utils.service.ts b/apps/desktop/src/platform/services/electron-platform-utils.service.ts
index dbc35de9311..6c99507b12b 100644
--- a/apps/desktop/src/platform/services/electron-platform-utils.service.ts
+++ b/apps/desktop/src/platform/services/electron-platform-utils.service.ts
@@ -9,31 +9,14 @@ import {
} from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { BiometricMessage, BiometricStorageAction } from "../../types/biometric-message";
-import { isDev, isMacAppStore } from "../../utils";
+import { isMacAppStore } from "../../utils";
import { ClipboardWriteMessage } from "../types/clipboard";
export class ElectronPlatformUtilsService implements PlatformUtilsService {
- private deviceCache: DeviceType = null;
-
constructor(protected i18nService: I18nService, private messagingService: MessagingService) {}
getDevice(): DeviceType {
- if (!this.deviceCache) {
- switch (process.platform) {
- case "win32":
- this.deviceCache = DeviceType.WindowsDesktop;
- break;
- case "darwin":
- this.deviceCache = DeviceType.MacOsDesktop;
- break;
- case "linux":
- default:
- this.deviceCache = DeviceType.LinuxDesktop;
- break;
- }
- }
-
- return this.deviceCache;
+ return ipc.platform.deviceType;
}
getDeviceString(): string {
@@ -82,7 +65,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
}
getApplicationVersion(): Promise {
- return ipcRenderer.invoke("appVersion");
+ return ipc.platform.versions.app();
}
async getApplicationVersionNumber(): Promise {
@@ -92,7 +75,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
// Temporarily restricted to only Windows until https://github.com/electron/electron/pull/28349
// has been merged and an updated electron build is available.
supportsWebAuthn(win: Window): boolean {
- return process.platform === "win32";
+ return this.getDevice() === DeviceType.WindowsDesktop;
}
supportsDuo(): boolean {
@@ -114,7 +97,7 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
}
isDev(): boolean {
- return isDev();
+ return ipc.platform.isDev;
}
isSelfHost(): boolean {
diff --git a/apps/desktop/src/preload.ts b/apps/desktop/src/preload.ts
new file mode 100644
index 00000000000..a6ddfdefca5
--- /dev/null
+++ b/apps/desktop/src/preload.ts
@@ -0,0 +1,19 @@
+// import { contextBridge } from "electron";
+import platform from "./platform/preload";
+
+/**
+ * Bitwarden Preload script.
+ *
+ * This file contains the "glue" between the main process and the renderer process. Please ensure
+ * that you have read through the following articles before modifying any preload script.
+ *
+ * https://www.electronjs.org/docs/latest/tutorial/tutorial-preload
+ * https://www.electronjs.org/docs/latest/api/context-bridge
+ */
+
+// Each team owns a subspace of the `ipc` global variable in the renderer.
+export const ipc = {
+ platform,
+};
+
+// contextBridge.exposeInMainWorld("ipc", ipc);
diff --git a/apps/desktop/src/scss/list.scss b/apps/desktop/src/scss/list.scss
index ec56eaa6c88..39e520f7d89 100644
--- a/apps/desktop/src/scss/list.scss
+++ b/apps/desktop/src/scss/list.scss
@@ -120,8 +120,12 @@
.item-content {
display: block;
+ overflow-x: hidden;
.item-title {
display: block;
+ overflow-x: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
.title-badges {
@include themify($themes) {
color: themed("mutedColor");
diff --git a/apps/desktop/webpack.main.js b/apps/desktop/webpack.main.js
index efd1de20d13..59e043fa12e 100644
--- a/apps/desktop/webpack.main.js
+++ b/apps/desktop/webpack.main.js
@@ -67,7 +67,6 @@ const main = {
],
},
plugins: [
- new CleanWebpackPlugin(),
new CopyWebpackPlugin({
patterns: [
"./src/package.json",
diff --git a/apps/desktop/webpack.preload.js b/apps/desktop/webpack.preload.js
new file mode 100644
index 00000000000..721d0567ca4
--- /dev/null
+++ b/apps/desktop/webpack.preload.js
@@ -0,0 +1,63 @@
+const path = require("path");
+const { merge } = require("webpack-merge");
+const CopyWebpackPlugin = require("copy-webpack-plugin");
+const { CleanWebpackPlugin } = require("clean-webpack-plugin");
+const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin");
+const configurator = require("./config/config");
+const { EnvironmentPlugin } = require("webpack");
+
+const NODE_ENV = process.env.NODE_ENV == null ? "development" : process.env.NODE_ENV;
+
+console.log("Preload process config");
+const envConfig = configurator.load(NODE_ENV);
+configurator.log(envConfig);
+
+const common = {
+ module: {
+ rules: [
+ {
+ test: /\.tsx?$/,
+ use: "ts-loader",
+ exclude: /node_modules\/(?!(@bitwarden)\/).*/,
+ },
+ ],
+ },
+ plugins: [],
+ resolve: {
+ extensions: [".tsx", ".ts", ".js"],
+ plugins: [new TsconfigPathsPlugin({ configFile: "./tsconfig.json" })],
+ },
+};
+
+const prod = {
+ output: {
+ filename: "[name].js",
+ path: path.resolve(__dirname, "build"),
+ },
+};
+
+const dev = {
+ output: {
+ filename: "[name].js",
+ path: path.resolve(__dirname, "build"),
+ devtoolModuleFilenameTemplate: "[absolute-resource-path]",
+ },
+ devtool: "cheap-source-map",
+};
+
+const main = {
+ mode: NODE_ENV,
+ target: "electron-preload",
+ node: {
+ __dirname: false,
+ __filename: false,
+ },
+ entry: {
+ preload: "./src/preload.ts",
+ },
+ optimization: {
+ minimize: false,
+ },
+};
+
+module.exports = merge(common, NODE_ENV === "development" ? dev : prod, main);
diff --git a/apps/desktop/webpack.renderer.js b/apps/desktop/webpack.renderer.js
index 64eef5729f6..ea1c350c389 100644
--- a/apps/desktop/webpack.renderer.js
+++ b/apps/desktop/webpack.renderer.js
@@ -62,6 +62,8 @@ const common = {
const renderer = {
mode: NODE_ENV,
devtool: "source-map",
+ // TODO: Replace this with web.
+ // target: "web",
target: "electron-renderer",
node: {
__dirname: false,
diff --git a/apps/web/src/app/auth/auth.module.ts b/apps/web/src/app/auth/auth.module.ts
index 49be17aa264..056b9f161f9 100644
--- a/apps/web/src/app/auth/auth.module.ts
+++ b/apps/web/src/app/auth/auth.module.ts
@@ -1,12 +1,11 @@
import { NgModule } from "@angular/core";
-import { CoreAuthModule } from "./core";
-import { SettingsModule } from "./settings/settings.module";
+import { AuthSettingsModule } from "./settings/settings.module";
@NgModule({
- imports: [CoreAuthModule, SettingsModule],
+ imports: [AuthSettingsModule],
declarations: [],
providers: [],
- exports: [SettingsModule],
+ exports: [AuthSettingsModule],
})
export class AuthModule {}
diff --git a/apps/web/src/app/auth/core/core.module.ts b/apps/web/src/app/auth/core/core.module.ts
deleted file mode 100644
index e196b1c3d76..00000000000
--- a/apps/web/src/app/auth/core/core.module.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { NgModule, Optional, SkipSelf } from "@angular/core";
-
-import { WebauthnLoginApiService } from "./services/webauthn-login/webauthn-login-api.service";
-import { WebauthnLoginService } from "./services/webauthn-login/webauthn-login.service";
-
-@NgModule({
- providers: [WebauthnLoginService, WebauthnLoginApiService],
-})
-export class CoreAuthModule {
- constructor(@Optional() @SkipSelf() parentModule?: CoreAuthModule) {
- if (parentModule) {
- throw new Error("CoreAuthModule is already loaded. Import it in AuthModule only");
- }
- }
-}
diff --git a/apps/web/src/app/auth/core/index.ts b/apps/web/src/app/auth/core/index.ts
index 3d2d739adf9..b2221a94a89 100644
--- a/apps/web/src/app/auth/core/index.ts
+++ b/apps/web/src/app/auth/core/index.ts
@@ -1,2 +1 @@
export * from "./services";
-export * from "./core.module";
diff --git a/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-api.service.ts b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-api.service.ts
index 33e1aea369b..6dc61563491 100644
--- a/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-api.service.ts
+++ b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-api.service.ts
@@ -1,25 +1,20 @@
import { Injectable } from "@angular/core";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
-import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
+import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
-import { Verification } from "@bitwarden/common/types/verification";
import { SaveCredentialRequest } from "./request/save-credential.request";
import { WebauthnLoginCredentialCreateOptionsResponse } from "./response/webauthn-login-credential-create-options.response";
import { WebauthnLoginCredentialResponse } from "./response/webauthn-login-credential.response";
-@Injectable()
+@Injectable({ providedIn: "root" })
export class WebauthnLoginApiService {
- constructor(
- private apiService: ApiService,
- private userVerificationService: UserVerificationService
- ) {}
+ constructor(private apiService: ApiService) {}
async getCredentialCreateOptions(
- verification: Verification
+ request: SecretVerificationRequest
): Promise {
- const request = await this.userVerificationService.buildRequest(verification);
const response = await this.apiService.send("POST", "/webauthn/options", request, true, true);
return new WebauthnLoginCredentialCreateOptionsResponse(response);
}
@@ -33,8 +28,7 @@ export class WebauthnLoginApiService {
return this.apiService.send("GET", "/webauthn", null, true, true);
}
- async deleteCredential(credentialId: string, verification: Verification): Promise {
- const request = await this.userVerificationService.buildRequest(verification);
+ async deleteCredential(credentialId: string, request: SecretVerificationRequest): Promise {
await this.apiService.send("POST", `/webauthn/${credentialId}/delete`, request, true, true);
}
}
diff --git a/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login.service.spec.ts b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login.service.spec.ts
index 070513f19e8..1e4f1fa7717 100644
--- a/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login.service.spec.ts
+++ b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login.service.spec.ts
@@ -1,5 +1,7 @@
import { mock, MockProxy } from "jest-mock-extended";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
+
import { CredentialCreateOptionsView } from "../../views/credential-create-options.view";
import { WebauthnLoginApiService } from "./webauthn-login-api.service";
@@ -7,6 +9,7 @@ import { WebauthnLoginService } from "./webauthn-login.service";
describe("WebauthnService", () => {
let apiService!: MockProxy;
+ let userVerificationService!: MockProxy;
let credentials: MockProxy;
let webauthnService!: WebauthnLoginService;
@@ -15,8 +18,9 @@ describe("WebauthnService", () => {
window.PublicKeyCredential = class {} as any;
window.AuthenticatorAttestationResponse = class {} as any;
apiService = mock();
+ userVerificationService = mock();
credentials = mock();
- webauthnService = new WebauthnLoginService(apiService, credentials);
+ webauthnService = new WebauthnLoginService(apiService, userVerificationService, credentials);
});
describe("createCredential", () => {
diff --git a/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login.service.ts b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login.service.ts
index 760214961a7..c5979f08c61 100644
--- a/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login.service.ts
+++ b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login.service.ts
@@ -1,6 +1,7 @@
import { Injectable, Optional } from "@angular/core";
import { BehaviorSubject, filter, from, map, Observable, shareReplay, switchMap, tap } from "rxjs";
+import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { Verification } from "@bitwarden/common/types/verification";
@@ -11,8 +12,10 @@ import { SaveCredentialRequest } from "./request/save-credential.request";
import { WebauthnLoginAttestationResponseRequest } from "./request/webauthn-login-attestation-response.request";
import { WebauthnLoginApiService } from "./webauthn-login-api.service";
-@Injectable()
+@Injectable({ providedIn: "root" })
export class WebauthnLoginService {
+ static readonly MaxCredentialCount = 5;
+
private navigatorCredentials: CredentialsContainer;
private _refresh$ = new BehaviorSubject(undefined);
private _loading$ = new BehaviorSubject(true);
@@ -27,6 +30,7 @@ export class WebauthnLoginService {
constructor(
private apiService: WebauthnLoginApiService,
+ private userVerificationService: UserVerificationService,
@Optional() navigatorCredentials?: CredentialsContainer,
@Optional() private logService?: LogService
) {
@@ -37,7 +41,8 @@ export class WebauthnLoginService {
async getCredentialCreateOptions(
verification: Verification
): Promise {
- const response = await this.apiService.getCredentialCreateOptions(verification);
+ const request = await this.userVerificationService.buildRequest(verification);
+ const response = await this.apiService.getCredentialCreateOptions(request);
return new CredentialCreateOptionsView(response.options, response.token);
}
@@ -95,7 +100,8 @@ export class WebauthnLoginService {
}
async deleteCredential(credentialId: string, verification: Verification): Promise {
- await this.apiService.deleteCredential(credentialId, verification);
+ const request = await this.userVerificationService.buildRequest(verification);
+ await this.apiService.deleteCredential(credentialId, request);
this.refresh();
}
diff --git a/apps/web/src/app/auth/settings/settings.module.ts b/apps/web/src/app/auth/settings/settings.module.ts
index 282524d07e4..12ae6bcbf5e 100644
--- a/apps/web/src/app/auth/settings/settings.module.ts
+++ b/apps/web/src/app/auth/settings/settings.module.ts
@@ -11,6 +11,6 @@ import { WebauthnLoginSettingsModule } from "./webauthn-login-settings";
imports: [SharedModule, WebauthnLoginSettingsModule, PasswordCalloutComponent],
declarations: [ChangePasswordComponent],
providers: [],
- exports: [WebauthnLoginSettingsModule, ChangePasswordComponent],
+ exports: [ChangePasswordComponent],
})
-export class SettingsModule {}
+export class AuthSettingsModule {}
diff --git a/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.html b/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.html
index 57a2c545ca1..aadcf5e5960 100644
--- a/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.html
+++ b/apps/web/src/app/auth/settings/webauthn-login-settings/create-credential-dialog/create-credential-dialog.component.html
@@ -1,5 +1,5 @@