diff --git a/apps/browser/src/background/runtime.background.ts b/apps/browser/src/background/runtime.background.ts index 159efc7de7..83764da49d 100644 --- a/apps/browser/src/background/runtime.background.ts +++ b/apps/browser/src/background/runtime.background.ts @@ -312,12 +312,36 @@ export default class RuntimeBackground { case "checkFido2FeatureEnabled": return await this.main.fido2ClientService.isFido2FeatureEnabled(); case "fido2RegisterCredentialRequest": - return await this.abortManager.runWithAbortController(msg.requestId, (abortController) => - this.main.fido2ClientService.createCredential(msg.data, sender.tab, abortController) + return await this.abortManager.runWithAbortController( + msg.requestId, + async (abortController) => { + try { + return await this.main.fido2ClientService.createCredential( + msg.data, + sender.tab, + abortController + ); + } finally { + await BrowserApi.focusTab(sender.tab.id); + await BrowserApi.focusWindow(sender.tab.windowId); + } + } ); case "fido2GetCredentialRequest": - return await this.abortManager.runWithAbortController(msg.requestId, (abortController) => - this.main.fido2ClientService.assertCredential(msg.data, sender.tab, abortController) + return await this.abortManager.runWithAbortController( + msg.requestId, + async (abortController) => { + try { + return await this.main.fido2ClientService.assertCredential( + msg.data, + sender.tab, + abortController + ); + } finally { + await BrowserApi.focusTab(sender.tab.id); + await BrowserApi.focusWindow(sender.tab.windowId); + } + } ); } } diff --git a/apps/browser/src/vault/fido2/content/page-script.ts b/apps/browser/src/vault/fido2/content/page-script.ts index e072ea20ef..e64fb92617 100644 --- a/apps/browser/src/vault/fido2/content/page-script.ts +++ b/apps/browser/src/vault/fido2/content/page-script.ts @@ -157,18 +157,24 @@ function isWebauthnCall(options?: CredentialCreationOptions | CredentialRequestO * Wait for window to be focused. * Safari doesn't allow scripts to trigger webauthn when window is not focused. * + * @param fallbackWait How long to wait when the script is not able to add event listeners to `window.top`. Defaults to 500ms. * @param timeout Maximum time to wait for focus in milliseconds. Defaults to 5 minutes. * @returns Promise that resolves when window is focused, or rejects if timeout is reached. */ -async function waitForFocus(timeout: number = 5 * 60 * 1000) { - if (window.top.document.hasFocus()) { - return; +async function waitForFocus(fallbackWait = 500, timeout = 5 * 60 * 1000) { + try { + if (window.top.document.hasFocus()) { + return; + } + } catch { + // Cannot access window.top due to cross-origin frame, fallback to waiting + return await new Promise((resolve) => window.setTimeout(resolve, fallbackWait)); } let focusListener; const focusPromise = new Promise((resolve) => { focusListener = () => resolve(); - window.top.addEventListener("focus", focusListener, { once: true }); + window.top.addEventListener("focus", focusListener); }); let timeoutId;