Skip to main content
If the OAuth window never appears after Connect, the browser likely blocked window.open. Browsers only allow that call while the user’s click is still considered active. Anything that delays the popup until after that gesture expires can make the browser treat it as unsolicited. When this happens, you can expect an OAuthBlockedError in the onError callback of paragon.connect. Then look for work that runs between the click and window.open, so installFlow.start (or connect / installIntegration) still happens in the click handler.

Symptoms

  • Clicking Connect does nothing, or a spinner never ends.
  • Chrome or Safari shows a blocked-popup indicator.
  • Connect SDK onError receives:

Cause

The OAuth prompt uses a popup. Chrome and Safari allow window.open when it runs in the same turn as a user gesture. They block it when the gesture has expired. The delay can come from several places. One of them is calling paragon.authenticate inside the click handler. Others include lazy-loading the SDK on click, awaiting a JWT, analytics, or a slow network after the click. A handler that stacks several of those steps looks like this:
That sequence is: click, then load the SDK, then authenticate, then open OAuth. The last step is no longer attached to the click. Any one of those awaits is enough to lose the gesture.

Fix

1. Confirm it is a popup block

In a Headless Connect Portal, inspect error.name in onError. If it is OAuthBlockedError, this article applies. If you are using the hosted Connect Portal, check the browser’s popup indicator on your app origin.

2. Keep the Connect click free of awaits

Start the install in the click handler with no async work in front of it. If you currently authenticate on click, move paragon.authenticate to page load or app start instead. Displaying the Connect Portal already recommends that for token freshness. It also happens to keep authenticate off the click path.
Apply the same rule to other delays: load @useparagon/connect before the click, and do not await analytics or a JWT fetch before start / connect / installIntegration. Keep onNext handlers that collect pre-OAuth inputs synchronous with the next user click. Calling setPreOptions from that second click is a new gesture, so the OAuth popup that follows it is still allowed.

3. Surface the error instead of spinning

OAuthBlockedError is thrown so you can replace an infinite spinner with a retry message.