[PM-4401] fix: wait for focus before triggering fallback (#6670)

* [PM-4401] fix: wait for focus before triggering fallback

* [PM-4401] feat: add timeout
This commit is contained in:
Andreas Coroiu 2023-10-24 19:33:52 +02:00 committed by GitHub
parent c798c92c84
commit c3e56152cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 39 additions and 0 deletions

View File

@ -94,6 +94,7 @@ navigator.credentials.create = async (
return WebauthnUtils.mapCredentialRegistrationResult(response.result);
} catch (error) {
if (error && error.fallbackRequested && fallbackSupported) {
await waitForFocus();
return await browserCredentials.create(options);
}
@ -132,9 +133,47 @@ navigator.credentials.get = async (
return WebauthnUtils.mapCredentialAssertResult(response.result);
} catch (error) {
if (error && error.fallbackRequested && fallbackSupported) {
await waitForFocus();
return await browserCredentials.get(options);
}
throw error;
}
};
/**
* Wait for window to be focused.
* Safari doesn't allow scripts to trigger webauthn when window is not focused.
*
* @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 (document.hasFocus()) {
return;
}
let focusListener;
const focusPromise = new Promise<void>((resolve) => {
focusListener = () => resolve();
window.addEventListener("focus", focusListener, { once: true });
});
let timeoutId;
const timeoutPromise = new Promise<void>((_, reject) => {
timeoutId = window.setTimeout(
() =>
reject(
new DOMException("The operation either timed out or was not allowed.", "AbortError")
),
timeout
);
});
try {
await Promise.race([focusPromise, timeoutPromise]);
} finally {
window.removeEventListener("focus", focusListener);
window.clearTimeout(timeoutId);
}
}