mirror of
https://github.com/bitwarden/browser
synced 2025-12-28 06:03:40 +00:00
PM-19095: Wire passkey autofill to UI (#13051)
* Passkey stuff Co-authored-by: Anders Åberg <github@andersaberg.com> * Ugly hacks * Work On Modal State Management * Applying modalStyles * modal * Improved hide/show * fixed promise * File name * fix prettier * Protecting against null API's and undefined data * Only show fake popup to devs * cleanup mock code * rename minmimal-app to modal-app * Added comment * Added comment * removed old comment * Avoided changing minimum size * Add small comment * Rename component * adress feedback * Fixed uppercase file * Fixed build * Added codeowners * added void * commentary * feat: reset setting on app start * Moved reset to be in main / process launch * Add comment to create window * Added a little bit of styling * Use Messaging service to loadUrl * Enable passkeysautofill * Add logging * halfbaked * Integration working * And now it works without extra delay * Clean up * add note about messaging * lb * removed console.logs * Cleanup and adress review feedback * This hides the swift UI * pick credential, draft * Remove logger * a whole lot of wiring * not working * Improved wiring * Cancel after 90s * Introduced observable * Launching bitwarden if its not running * Passing position from native to electron * Rename inModalMode to modalMode * remove tap * revert spaces * added back isDev * cleaned up a bit * Cleanup swift file * tweaked logging * clean up * Update apps/desktop/macos/autofill-extension/CredentialProviderViewController.swift Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com> * Update apps/desktop/src/platform/main/autofill/native-autofill.main.ts Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com> * Update apps/desktop/src/platform/services/desktop-settings.service.ts Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com> * adress position feedback * Update apps/desktop/macos/autofill-extension/CredentialProviderViewController.swift Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com> * Removed extra logging * Adjusted error logging * Use .error to log errors * remove dead code * Update desktop-autofill.service.ts * use parseCredentialId instead of guidToRawFormat * Update apps/desktop/src/autofill/services/desktop-autofill.service.ts Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com> * Change windowXy to a Record instead of [number,number] * Update apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com> * Remove unsued dep and comment * changed timeout to be spec recommended maxium, 10 minutes, for now. * Correctly assume UP * Removed extra cancelRequest in deinint * Add timeout and UV to confirmChoseCipher UV is performed by UI, not the service * Improved docs regarding undefined cipherId * cleanup: UP is no longer undefined * Run completeError if ipc messages conversion failed * don't throw, instead return undefined * Disabled passkey provider * Throw error if no activeUserId was found * removed comment * Fixed lint * removed unsued service * reset entitlement formatting * Update entitlements.mas.plist --------- Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> Co-authored-by: Colton Hurst <colton@coltonhurst.com> Co-authored-by: Andreas Coroiu <andreas.coroiu@gmail.com> Co-authored-by: Andreas Coroiu <acoroiu@bitwarden.com>
This commit is contained in:
@@ -40,6 +40,7 @@ export class NativeAutofillMain {
|
||||
(error, clientId, sequenceNumber, request) => {
|
||||
if (error) {
|
||||
this.logService.error("autofill.IpcServer.registration", error);
|
||||
this.ipcServer.completeError(clientId, sequenceNumber, String(error));
|
||||
return;
|
||||
}
|
||||
this.windowMain.win.webContents.send("autofill.passkeyRegistration", {
|
||||
@@ -52,6 +53,7 @@ export class NativeAutofillMain {
|
||||
(error, clientId, sequenceNumber, request) => {
|
||||
if (error) {
|
||||
this.logService.error("autofill.IpcServer.assertion", error);
|
||||
this.ipcServer.completeError(clientId, sequenceNumber, String(error));
|
||||
return;
|
||||
}
|
||||
this.windowMain.win.webContents.send("autofill.passkeyAssertion", {
|
||||
@@ -60,6 +62,19 @@ export class NativeAutofillMain {
|
||||
request,
|
||||
});
|
||||
},
|
||||
// AssertionWithoutUserInterfaceCallback
|
||||
(error, clientId, sequenceNumber, request) => {
|
||||
if (error) {
|
||||
this.logService.error("autofill.IpcServer.assertion", error);
|
||||
this.ipcServer.completeError(clientId, sequenceNumber, String(error));
|
||||
return;
|
||||
}
|
||||
this.windowMain.win.webContents.send("autofill.passkeyAssertionWithoutUserInterface", {
|
||||
clientId,
|
||||
sequenceNumber,
|
||||
request,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
ipcMain.on("autofill.completePasskeyRegistration", (event, data) => {
|
||||
@@ -77,7 +92,7 @@ export class NativeAutofillMain {
|
||||
ipcMain.on("autofill.completeError", (event, data) => {
|
||||
this.logService.warning("autofill.completeError", data);
|
||||
const { clientId, sequenceNumber, error } = data;
|
||||
this.ipcServer.completeAssertion(clientId, sequenceNumber, error);
|
||||
this.ipcServer.completeError(clientId, sequenceNumber, String(error));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -11,3 +11,8 @@ export class WindowState {
|
||||
y?: number;
|
||||
zoomFactor?: number;
|
||||
}
|
||||
|
||||
export class ModalModeState {
|
||||
isModalModeActive: boolean;
|
||||
modalPosition?: { x: number; y: number }; // Modal position is often passed from the native UI
|
||||
}
|
||||
|
||||
@@ -6,10 +6,11 @@ import { WindowState } from "./models/domain/window-state";
|
||||
const popupWidth = 680;
|
||||
const popupHeight = 500;
|
||||
|
||||
export function applyPopupModalStyles(window: BrowserWindow) {
|
||||
type Position = { x: number; y: number };
|
||||
|
||||
export function applyPopupModalStyles(window: BrowserWindow, position?: Position) {
|
||||
window.unmaximize();
|
||||
window.setSize(popupWidth, popupHeight);
|
||||
window.center();
|
||||
window.setWindowButtonVisibility?.(false);
|
||||
window.setMenuBarVisibility?.(false);
|
||||
window.setResizable(false);
|
||||
@@ -20,8 +21,21 @@ export function applyPopupModalStyles(window: BrowserWindow) {
|
||||
window.setFullScreen(false);
|
||||
window.once("leave-full-screen", () => {
|
||||
window.setSize(popupWidth, popupHeight);
|
||||
window.center();
|
||||
positionWindow(window, position);
|
||||
});
|
||||
} else {
|
||||
// If not in full screen
|
||||
positionWindow(window, position);
|
||||
}
|
||||
}
|
||||
|
||||
function positionWindow(window: BrowserWindow, position?: Position) {
|
||||
if (position) {
|
||||
const centeredX = position.x - popupWidth / 2;
|
||||
const centeredY = position.y - popupHeight / 2;
|
||||
window.setPosition(centeredX, centeredY);
|
||||
} else {
|
||||
window.center();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from "@bitwarden/common/platform/state";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { WindowState } from "../models/domain/window-state";
|
||||
import { ModalModeState, WindowState } from "../models/domain/window-state";
|
||||
|
||||
export const HARDWARE_ACCELERATION = new KeyDefinition<boolean>(
|
||||
DESKTOP_SETTINGS_DISK,
|
||||
@@ -75,7 +75,7 @@ const MINIMIZE_ON_COPY = new UserKeyDefinition<boolean>(DESKTOP_SETTINGS_DISK, "
|
||||
clearOn: [], // User setting, no need to clear
|
||||
});
|
||||
|
||||
const IN_MODAL_MODE = new KeyDefinition<boolean>(DESKTOP_SETTINGS_DISK, "inModalMode", {
|
||||
const MODAL_MODE = new KeyDefinition<ModalModeState>(DESKTOP_SETTINGS_DISK, "modalMode", {
|
||||
deserializer: (b) => b,
|
||||
});
|
||||
|
||||
@@ -174,9 +174,9 @@ export class DesktopSettingsService {
|
||||
*/
|
||||
minimizeOnCopy$ = this.minimizeOnCopyState.state$.pipe(map(Boolean));
|
||||
|
||||
private readonly inModalModeState = this.stateProvider.getGlobal(IN_MODAL_MODE);
|
||||
private readonly modalModeState = this.stateProvider.getGlobal(MODAL_MODE);
|
||||
|
||||
inModalMode$ = this.inModalModeState.state$.pipe(map(Boolean));
|
||||
modalMode$ = this.modalModeState.state$;
|
||||
|
||||
constructor(private stateProvider: StateProvider) {
|
||||
this.window$ = this.windowState.state$.pipe(
|
||||
@@ -190,8 +190,8 @@ export class DesktopSettingsService {
|
||||
* This is used to clear the setting on application start to make sure we don't end up
|
||||
* stuck in modal mode if the application is force-closed in modal mode.
|
||||
*/
|
||||
async resetInModalMode() {
|
||||
await this.inModalModeState.update(() => false);
|
||||
async resetModalMode() {
|
||||
await this.modalModeState.update(() => ({ isModalModeActive: false }));
|
||||
}
|
||||
|
||||
async setHardwareAcceleration(enabled: boolean) {
|
||||
@@ -306,8 +306,11 @@ export class DesktopSettingsService {
|
||||
* Sets the modal mode of the application. Setting this changes the windows-size and other properties.
|
||||
* @param value `true` if the application is in modal mode, `false` if it is not.
|
||||
*/
|
||||
async setInModalMode(value: boolean) {
|
||||
await this.inModalModeState.update(() => value);
|
||||
async setModalMode(value: boolean, modalPosition?: { x: number; y: number }) {
|
||||
await this.modalModeState.update(() => ({
|
||||
isModalModeActive: value,
|
||||
modalPosition,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user