Skip to content

Send token using websocket#6248

Open
mifi wants to merge 6 commits intomainfrom
send-token-websocket
Open

Send token using websocket#6248
mifi wants to merge 6 commits intomainfrom
send-token-websocket

Conversation

@mifi
Copy link
Copy Markdown
Contributor

@mifi mifi commented Mar 28, 2026

instead of window.opener - it is more robust

inspired by #4110
closes #6246
closes #4107

Biggest caveat: In cases where window.opener is null (Dropbox), the auth window will not be auto closed (I don't know how to fix that). It will show the user a message "Authentication successful. You may now close this page." https://github.com/transloadit/uppy/pull/6248/changes#diff-ef5b69c4ab5b83168eaba6f57047bb07c53e3a426f375b42fcb32d79c746872cR22

instead of window.opener
it is more robust
note that in cases where window.opener is null, the auth window will not be auto closed (I don't know how to fix that)
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 28, 2026

⚠️ No Changeset found

Latest commit: 15196b8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@mifi mifi marked this pull request as ready for review March 28, 2026 08:52
@mifi mifi requested a review from qxprakash March 28, 2026 08:52
@mifi mifi requested a review from Copilot March 28, 2026 08:56
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Uppy’s OAuth token handoff to use a WebSocket-based callback channel (with legacy HTML postMessage fallback) to avoid failures when window.opener is unavailable (eg due to COOP / app-to-browser redirects), particularly impacting Dropbox/Instagram flows.

Changes:

  • Add an authCallbackToken param to the OAuth connect flow and deliver the final auth result via Companion’s WebSocket server + emitter.
  • Preserve backward compatibility by keeping the legacy HTML postMessage mechanism when no callback token is present.
  • Adjust Companion socket routing/state storage and update tests/examples accordingly.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
packages/@uppy/provider-views/src/ProviderView/AuthView.tsx Disables auth submit buttons while starting auth.
packages/@uppy/companion/test/mockserver.js Exposes Companion emitter from the mock server for WebSocket/auth callback testing.
packages/@uppy/companion/test/mockoauthstate.js Updates oauth-state mock signature/behavior for new state key usage.
packages/@uppy/companion/test/callback.test.js Adds tests for WebSocket-based token delivery and retains legacy HTML fallback test.
packages/@uppy/companion/src/standalone/index.js Returns emitter from standalone server bootstrap.
packages/@uppy/companion/src/server/socket.js Adds a WebSocket “router” to handle both upload sockets and auth-callback sockets.
packages/@uppy/companion/src/server/helpers/html.js Introduces shared HTML templates for success/error pages and legacy auth callback HTML.
packages/@uppy/companion/src/server/controllers/send-token.js Sends token via emitter (WebSocket path) when callback token is present; otherwise uses legacy HTML.
packages/@uppy/companion/src/server/controllers/connect.js Persists authCallbackToken into encrypted OAuth state.
packages/@uppy/companion/src/server/controllers/callback.js Emits auth failure via emitter (WebSocket path) when callback token is present; otherwise uses legacy HTML.
packages/@uppy/companion/src/server/Uploader.js Adjusts redis storage prefix constant for upload token state.
packages/@uppy/companion-client/src/getAllowedHosts.ts Removes isOriginAllowed helper from companion-client.
packages/@uppy/companion-client/src/getAllowedHosts.test.ts Removes tests for the removed isOriginAllowed helper.
packages/@uppy/companion-client/src/Provider.ts Implements OAuth login via WebSocket auth callback token instead of postMessage.
examples/vue/test/index.test.ts Updates remote source auth test assertions (post-click expectation removed).
examples/sveltekit/test/index.test.ts Updates remote source auth test assertions (post-click expectation removed).
examples/react/test/index.test.tsx Updates remote source auth test assertions (post-click expectation removed).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +253 to +256
authWindow?.addEventListener('close', () => {
reject(new Error('Authentication window was closed by user'))
closeSocket()
})
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

authWindow?.addEventListener('close', ...) won’t fire: Window objects don’t emit a close event. This means you no longer detect the user closing the auth popup (the old polling via authWindow.closed did). Reintroduce a reliable close detection mechanism (eg polling) and ensure it’s cleaned up in finally.

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +113
const path = req.url

// authentication callback token?
const authCallbackTokenMatch = path.match(
/^\/api2\/auth-callback\/token\/([^/]+)/,
)
const authCallbackToken = authCallbackTokenMatch?.[1]

emitter().emit(`connection:${token}`)
emitter().on(token, send)
// or token that identifies which ongoing upload's progress, the socket connection wishes to listen to.
const uploadTokenMatch = path.match(/^\/api\/([^/]+)/)
const uploadToken = uploadTokenMatch?.[1]
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new path matching is anchored to the start of req.url (^/api...). This breaks setups where Companion is mounted under a path prefix (eg /companion/api/<token>), which previously worked because the token was extracted with replace(/^.*\/api\//, ''). Please update the routing/token extraction to handle optional prefixes for both upload sockets and the new auth-callback sockets.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed this would break routing when companion is mounted under a path prefix

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@jcampbell05
Copy link
Copy Markdown

jcampbell05 commented Mar 28, 2026

Biggest caveat: In cases where window.opener is null (Dropbox), the auth window will not be auto closed

I think only way would to be change the redirect flow so it doesn't need different endpoints / domains / use of companion endpoints (perhaps opton for us to handle the Oauth and pass it to companion client side)

But a much deeper change.

Copy link
Copy Markdown
Collaborator

@qxprakash qxprakash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overall looks good, minor comments and I tested few bugs which were reported by copilot

Copy link
Copy Markdown
Contributor Author

@mifi mifi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i've now resolved the feedback i believe

@mifi mifi requested a review from qxprakash April 7, 2026 19:56
@mifi
Copy link
Copy Markdown
Contributor Author

mifi commented Apr 8, 2026

once @qxprakash has reviewed this we should merge and make a release asap, so people can start to test

Copy link
Copy Markdown
Collaborator

@qxprakash qxprakash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good, I've tested the bugs they're all fixed. I'm fine with doing a major release with the breaking change if we don't have any other alternatives.

const { app } = standalone()
const { app, companionOptions } = standalone()

companion.socket(app.listen(port))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a breaking API change , can we think of any alternatives to support path prefixes without a breaking change or this is the only way ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it a breaking change though? start-server is just a script to run companion

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's breaking for users who integrate companion into their express app by calling the public API directly ,

it broke both our examples -

companion.socket(app.listen(3020))

companion.socket(server)

as it now requires an extra argument companionOptions

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mifi what do you think of this ?

Comment on lines +267 to +269
// if we don't setTimeout, the window doesn't really close (I don't know why).
setTimeout(() => authWindow?.close(), 1)
this.uppy.log(`Closing auth callback socket ${authCallbackToken}`)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will check this if I get time or maybe we can debug this later after we've merged this PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dropbox authentication broken Auth redirect to blank page & Cannot read properties of null (reading 'postMessage')

4 participants