@@ -429,6 +429,8 @@ final class ClerkNativeBridge {
429429 private var lastObservedClientState : ClientStateSnapshot ?
430430 private var configurationDepth = 0
431431 private var jsOriginatedClientSyncDepth = 0
432+ private var pendingURL : URL ?
433+ private var shouldFlushPendingURL = false
432434
433435 private init ( ) { }
434436
@@ -459,6 +461,13 @@ final class ClerkNativeBridge {
459461 defer {
460462 lastObservedClientState = Self . clerkConfigured ? Self . clientStateSnapshot ( ) : nil
461463 configurationDepth = max ( 0 , configurationDepth - 1 )
464+
465+ // Overlapping calls can finish out of order, so replay once the last one settles and any
466+ // of them succeeded. A batch where every call threw keeps the URL for the next attempt.
467+ if configurationDepth == 0 , shouldFlushPendingURL {
468+ shouldFlushPendingURL = false
469+ flushPendingURL ( )
470+ }
462471 }
463472
464473 loadThemes ( )
@@ -472,6 +481,7 @@ final class ClerkNativeBridge {
472481 let shouldWaitForClient = try await Self . syncTokenState ( bearerToken: bearerToken)
473482 await Self . waitForLoadedClientIfNeeded ( shouldWaitForClient)
474483 Self . postConfiguredNotification ( )
484+ shouldFlushPendingURL = true
475485 return
476486 }
477487
@@ -486,6 +496,7 @@ final class ClerkNativeBridge {
486496 _ = try await Clerk . shared. refreshClient ( )
487497 await Self . waitForLoadedClient ( )
488498 }
499+ shouldFlushPendingURL = true
489500 return
490501 }
491502
@@ -497,6 +508,32 @@ final class ClerkNativeBridge {
497508 let shouldWaitForClient = try await Self . syncTokenState ( bearerToken: bearerToken)
498509 await Self . waitForLoadedClientIfNeeded ( shouldWaitForClient)
499510 Self . postConfiguredNotification ( )
511+ shouldFlushPendingURL = true
512+ }
513+
514+ @MainActor
515+ private func flushPendingURL( ) {
516+ guard let url = pendingURL else { return }
517+ pendingURL = nil
518+ handle ( url: url)
519+ }
520+
521+ /// `AuthView` only reaches `Clerk.handle(_:)` from `.onOpenURL`, which never fires for a UIKit-hosted controller.
522+ @MainActor
523+ func handle( url: URL ) {
524+ // A cold launch delivers the callback before, or partway through, JS calling `configure`.
525+ guard Self . clerkConfigured, configurationDepth == 0 else {
526+ pendingURL = url
527+ return
528+ }
529+
530+ Task { @MainActor in
531+ do {
532+ try await Clerk . shared. handle ( url)
533+ } catch {
534+ NSLog ( " [Clerk] Failed to handle callback URL: \( error. localizedDescription) " )
535+ }
536+ }
500537 }
501538
502539 @MainActor
0 commit comments