From 8e10f5cb03be680fc02b90aa052a3a4ffc39eb18 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Thu, 5 Feb 2026 19:14:56 +0000 Subject: [PATCH] fix: Add abort mechanism for conditional UI to prevent pending request errors - Add global AbortController for conditional UI requests - Call abortConditionalUI() at start of registerPasskey and authenticatePasskey - Export abortConditionalUI from index for manual use if needed Co-Authored-By: Claude Opus 4.5 --- src/encryptid/index.ts | 1 + src/encryptid/webauthn.ts | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/encryptid/index.ts b/src/encryptid/index.ts index efa0e21..7886fc5 100644 --- a/src/encryptid/index.ts +++ b/src/encryptid/index.ts @@ -16,6 +16,7 @@ export { registerPasskey, authenticatePasskey, startConditionalUI, + abortConditionalUI, isConditionalMediationAvailable, detectCapabilities, bufferToBase64url, diff --git a/src/encryptid/webauthn.ts b/src/encryptid/webauthn.ts index d930a44..6b8d0bf 100644 --- a/src/encryptid/webauthn.ts +++ b/src/encryptid/webauthn.ts @@ -44,6 +44,21 @@ const DEFAULT_CONFIG: EncryptIDConfig = { timeout: 60000, }; +// Global abort controller for conditional UI +let conditionalUIAbortController: AbortController | null = null; + +/** + * Abort any pending conditional UI request + * Call this before starting registration or authentication + */ +export function abortConditionalUI(): void { + if (conditionalUIAbortController) { + conditionalUIAbortController.abort(); + conditionalUIAbortController = null; + console.log('EncryptID: Conditional UI aborted'); + } +} + // ============================================================================ // UTILITY FUNCTIONS // ============================================================================ @@ -108,6 +123,9 @@ export async function registerPasskey( displayName: string, config: Partial = {} ): Promise { + // Abort any pending conditional UI to prevent "request already pending" error + abortConditionalUI(); + const cfg = { ...DEFAULT_CONFIG, ...config }; // Check WebAuthn support @@ -237,6 +255,9 @@ export async function authenticatePasskey( credentialId?: string, // Optional: specify credential, or let user choose config: Partial = {} ): Promise { + // Abort any pending conditional UI to prevent "request already pending" error + abortConditionalUI(); + const cfg = { ...DEFAULT_CONFIG, ...config }; // Check WebAuthn support @@ -358,6 +379,12 @@ export async function startConditionalUI( return null; } + // Abort any existing conditional UI request + abortConditionalUI(); + + // Create new abort controller for this request + conditionalUIAbortController = new AbortController(); + const cfg = { ...DEFAULT_CONFIG, ...config }; const challenge = generateChallenge(); const prfSalt = await generatePRFSalt('master-key'); @@ -380,8 +407,12 @@ export async function startConditionalUI( }, // @ts-ignore - conditional mediation mediation: 'conditional', + signal: conditionalUIAbortController.signal, }) as PublicKeyCredential; + // Clear abort controller on success + conditionalUIAbortController = null; + if (!credential) { return null; }