diff --git a/.changeset/gold-rice-jog.md b/.changeset/gold-rice-jog.md
new file mode 100644
index 000000000..71b7a8fc0
--- /dev/null
+++ b/.changeset/gold-rice-jog.md
@@ -0,0 +1,9 @@
+---
+"@uppy/components": major
+"@uppy/companion": major
+"uppy": major
+"@uppy/transloadit": minor
+"@uppy/locales": minor
+---
+
+Remove @uppy/instagram references from all the packages
diff --git a/.changeset/legal-drinks-marry.md b/.changeset/legal-drinks-marry.md
new file mode 100644
index 000000000..e859660be
--- /dev/null
+++ b/.changeset/legal-drinks-marry.md
@@ -0,0 +1,5 @@
+---
+"@uppy/transloadit": minor
+---
+
+Add assemblyStatus and lastAssemblyStatus to transloadit's plugin state
diff --git a/.changeset/mighty-deers-sin.md b/.changeset/mighty-deers-sin.md
new file mode 100644
index 000000000..71159166a
--- /dev/null
+++ b/.changeset/mighty-deers-sin.md
@@ -0,0 +1,8 @@
+---
+"@uppy/companion-client": major
+"@uppy/companion": major
+---
+
+Send token using websocket instead of window.opener.
+Breaking in `@uppy/companion-client` because it needs newest version of Companion in order to work.
+Breaking in `@uppy/companion` because `companion.socket()` now requires `companionOptions` to be passed as the second argument.
diff --git a/.env.example b/.env.example
index a3bbe74b7..f8ccdd5f4 100644
--- a/.env.example
+++ b/.env.example
@@ -44,9 +44,6 @@ COMPANION_DROPBOX_SECRET=***
COMPANION_GOOGLE_KEY=***
COMPANION_GOOGLE_SECRET=***
-COMPANION_INSTAGRAM_KEY=***
-COMPANION_INSTAGRAM_SECRET=***
-
COMPANION_FACEBOOK_KEY=***
COMPANION_FACEBOOK_SECRET=***
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 22ea1f915..b154e0281 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -57,7 +57,7 @@ This is not needed for changing existing components.
## Companion
If you’d like to work on features that the basic development version of Uppy
-doesn’t support, such as Uppy integrations with Instagram/Google Drive/Facebook
+doesn’t support, such as Uppy integrations with Google Drive/Facebook
etc., you need to set up your `.env` file (copy the contents of `.env.example`
and adjust them based on what you need to work on), and run:
@@ -78,7 +78,7 @@ when files are changed.
### How the Authentication and Token mechanism works
This section describes how Authentication works between Companion and Providers.
-While this behaviour is the same for all Providers (Dropbox, Instagram, Google
+While this behaviour is the same for all Providers (Dropbox, Google
Drive, etc.), we are going to be referring to Dropbox in place of any Provider
throughout this section.
diff --git a/.github/workflows/bundlers.yml b/.github/workflows/bundlers.yml
index 0f2035da8..a0dcf03ee 100644
--- a/.github/workflows/bundlers.yml
+++ b/.github/workflows/bundlers.yml
@@ -42,7 +42,7 @@ jobs:
node-version: lts/*
- name: Install dependencies
run:
- corepack yarn install
+ corepack yarn install --immutable
env:
# https://docs.cypress.io/guides/references/advanced-installation#Skipping-installation
CYPRESS_INSTALL_BINARY: 0
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2d657c7ef..8846111ce 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -47,7 +47,7 @@ jobs:
node-version: ${{matrix.node-version}}
- name: Install dependencies
run:
- corepack yarn install
+ corepack yarn install --immutable
- name: Install Playwright Browsers
run: corepack yarn workspace @uppy/dashboard playwright install --with-deps
- name: Build
@@ -84,7 +84,7 @@ jobs:
node-version: lts/*
- name: Install dependencies
run:
- corepack yarn install
+ corepack yarn install --immutable
- run: corepack yarn run typecheck
lint_js:
diff --git a/.github/workflows/companion-deploy.yml b/.github/workflows/companion-deploy.yml
index b7696d1b1..53e87b9f0 100644
--- a/.github/workflows/companion-deploy.yml
+++ b/.github/workflows/companion-deploy.yml
@@ -38,35 +38,49 @@ jobs:
path: /tmp/companion-${{ github.sha }}.tar.gz
docker:
- name: DockerHub
- runs-on: ubuntu-latest
- env:
- DOCKER_BUILDKIT: 0
- COMPOSE_DOCKER_CLI_BUILD: 0
+ name: DockerHub (${{ matrix.platform }})
+ strategy:
+ matrix:
+ include:
+ - runner: ubuntu-24.04
+ platform: linux/amd64
+ platform_tag: amd64
+ - runner: ubuntu-24.04-arm
+ platform: linux/arm64
+ platform_tag: arm64
+ runs-on: ${{ matrix.runner }}
steps:
- name: Checkout sources
uses: actions/checkout@v6
- - name: Docker meta
- id: docker_meta
- uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
- with:
- images: transloadit/companion
- tags: |
- type=edge
- type=raw,value=latest,enable=false
- - uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
- uses: docker/setup-buildx-action@v3
- name: Log in to DockerHub
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
- username: ${{secrets.DOCKER_USERNAME}}
- password: ${{secrets.DOCKER_PASSWORD}}
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
with:
push: true
context: .
- platforms: linux/amd64,linux/arm64
+ platforms: ${{ matrix.platform }}
file: Dockerfile
- tags: ${{ steps.docker_meta.outputs.tags }}
- labels: ${{ steps.docker_meta.outputs.labels }}
\ No newline at end of file
+ tags: transloadit/companion:edge-${{ matrix.platform_tag }}
+
+ docker-manifest:
+ name: DockerHub manifest
+ needs: docker
+ runs-on: ubuntu-latest
+ steps:
+ - uses: docker/setup-buildx-action@v3
+ - name: Log in to DockerHub
+ uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
+ with:
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_PASSWORD }}
+ - name: Publish edge manifest
+ run: |
+ docker buildx imagetools create \
+ --tag transloadit/companion:edge \
+ transloadit/companion:edge-amd64 \
+ transloadit/companion:edge-arm64
diff --git a/.github/workflows/manual-cdn.yml b/.github/workflows/manual-cdn.yml
index 2d511a965..0efbe6fa7 100644
--- a/.github/workflows/manual-cdn.yml
+++ b/.github/workflows/manual-cdn.yml
@@ -30,18 +30,6 @@ jobs:
steps:
- name: Checkout sources
uses: actions/checkout@v6
- - name: Get yarn cache directory path
- id: yarn-cache-dir-path
- run:
- echo "dir=$(corepack yarn config get cacheFolder)" >> $GITHUB_OUTPUT
-
- - uses: actions/cache@v5
- id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
- with:
- path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
- key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
- restore-keys: |
- ${{ runner.os }}-yarn-
- name: Install Node.js
uses: actions/setup-node@v6
with:
diff --git a/.yarnrc.yml b/.yarnrc.yml
index d2890fb76..2057decd7 100644
--- a/.yarnrc.yml
+++ b/.yarnrc.yml
@@ -9,5 +9,6 @@ initScope: uppy
nodeLinker: node-modules
-npmPublishAccess: public
+npmMinimalAgeGate: 2880
+npmPublishAccess: public
diff --git a/BUNDLE-README.md b/BUNDLE-README.md
index ffc6fc6c0..5e6396bd5 100644
--- a/BUNDLE-README.md
+++ b/BUNDLE-README.md
@@ -83,7 +83,7 @@ inline into the page. This, and many more configuration options can be found
here: .
Uppy has many more Plugins besides Xhr and the Dashboard. For example, you can
-enable Webcam, Instagram, or video encoding support. Note that for some Plugins,
+enable Webcam or video encoding support. Note that for some Plugins,
you will need to run a server side component called: Companion. Those plugins
are marked with a (c) symbol. Alternatively, you can sign up for a free
Transloadit account. Transloadit runs Companion for you, tusd servers to handle
diff --git a/CLAUDE.md b/CLAUDE.md
index eb73dda14..4091038aa 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -46,7 +46,7 @@ yarn workspace @uppy/[package-name] build
- **Uppy Core** (`@uppy/core`) - Main class that manages plugins, state, and events
- **Plugins** - Modular components for different functionalities:
- **UI Plugins**: Dashboard, Drag & Drop, File Input, Webcam, etc.
- - **Provider Plugins**: Google Drive, Dropbox, Instagram, etc. (require Companion)
+ - **Provider Plugins**: Google Drive, Dropbox, etc. (require Companion)
- **Uploader Plugins**: Tus (resumable), XHR Upload, AWS S3, etc.
- **Utility Plugins**: Golden Retriever (recovery), Thumbnail Generator, etc.
diff --git a/README.md b/README.md
index 261e5ab0b..2b63aff7f 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ with any application. It’s fast, has a comprehensible API and lets you worry
about more important problems than building a file uploader.
- **Fetch** files from local disk, remote URLs, Google Drive, Dropbox, Box,
- Instagram or snap and record selfies with a camera
+ or snap and record selfies with a camera
- **Preview** and edit metadata with a nice interface
- **Upload** to the final destination, optionally process/encode
@@ -82,7 +82,7 @@ For the supported frameworks (except Angular) Uppy offers three ways to build us
- Lightweight, modular plugin-based architecture, light on dependencies :zap:
- Resumable file uploads via the open [tus](https://tus.io/) standard, so large
uploads survive network hiccups
-- Supports picking files from: Webcam, Dropbox, Box, Google Drive, Instagram,
+- Supports picking files from: Webcam, Dropbox, Box, Google Drive,
bypassing the user’s device where possible, syncing between servers directly
via [@uppy/companion](https://uppy.io/docs/companion)
- Works great with file encoding and processing backends, such as
@@ -140,7 +140,7 @@ CDN. In that case `Uppy` will attach itself to the global `window.Uppy` object.
- [Uppy](https://uppy.io/docs/uppy/) — full list of options, methods and events
- [Companion](https://uppy.io/docs/companion/) — setting up and running a
- Companion instance, which adds support for Instagram, Dropbox, Box, Google
+ Companion instance, which adds support for Dropbox, Box, Google
Drive and remote URLs
- [React](https://uppy.io/docs/react/) — components to integrate Uppy UI plugins
with React apps
@@ -153,7 +153,7 @@ CDN. In that case `Uppy` will attach itself to the global `window.Uppy` object.
- [`Dashboard`](https://uppy.io/docs/dashboard/) — universal UI with previews,
progress bars, metadata editor and all the cool stuff. Required for most UI
- plugins like Webcam and Instagram
+ plugins like Webcam
- Headless components ([react](https://uppy.io/docs/react/), [svelte](https://uppy.io/docs/svelte/), [vue](https://uppy.io/docs/vue/))
### Sources
@@ -166,8 +166,6 @@ CDN. In that case `Uppy` will attach itself to the global `window.Uppy` object.
Google Drive
- ⓒ [`Dropbox`](https://uppy.io/docs/dropbox/) — import files from Dropbox
- ⓒ [`Box`](https://uppy.io/docs/box/) — import files from Box
-- ⓒ [`Instagram`](https://uppy.io/docs/instagram/) — import images and videos
- from Instagram
- ⓒ [`Facebook`](https://uppy.io/docs/facebook/) — import images and videos from
Facebook
- ⓒ [`OneDrive`](https://uppy.io/docs/onedrive/) — import files from Microsoft
@@ -222,7 +220,7 @@ cases, the file input leaves some to be desired:
- Uppy supports editing meta information before uploading.
- Uppy allows cropping images before uploading.
- There’s the situation where people are using their mobile devices and want to
- upload on the go, but they have their picture on Instagram, files in Dropbox
+ upload on the go, but they have files in Dropbox
or a plain file URL from anywhere on the open web. Uppy allows to pick files
from those and push it to the destination without downloading it to your
mobile device first.
@@ -272,7 +270,7 @@ If you want resumability with the Tus plugin, use
[one of the tus server implementations](https://tus.io/implementations.html) 👌🏼
And you’ll need [`@uppy/companion`](https://uppy.io/docs/companion) if you’d
-like your users to be able to pick files from Instagram, Google Drive, Dropbox
+like your users to be able to pick files from Google Drive, Dropbox
or via direct URLs (with more services coming).
## Contributions are welcome
diff --git a/examples/companion/client/index.html b/examples/companion/client/index.html
index 26489beb8..64d93e91c 100644
--- a/examples/companion/client/index.html
+++ b/examples/companion/client/index.html
@@ -16,17 +16,12 @@
import {
Uppy,
Dashboard,
- Instagram,
GoogleDrive,
Tus,
} from 'https://releases.transloadit.com/uppy/v4.18.0/uppy.min.mjs'
const uppy = new Uppy({ debug: true, autoProceed: false })
.use(Dashboard, { trigger: '#uppyModalOpener' })
- .use(Instagram, {
- target: Dashboard,
- companionUrl: 'http://localhost:3020',
- })
.use(GoogleDrive, {
target: Dashboard,
companionUrl: 'http://localhost:3020',
diff --git a/examples/companion/server/index.js b/examples/companion/server/index.js
index 076e187cd..ab7c5f6ee 100644
--- a/examples/companion/server/index.js
+++ b/examples/companion/server/index.js
@@ -32,10 +32,6 @@ const companionOptions = {
key: 'your google key',
secret: 'your google secret',
},
- instagram: {
- key: 'your instagram key',
- secret: 'your instagram secret',
- },
dropbox: {
key: 'your dropbox key',
secret: 'your dropbox secret',
diff --git a/examples/react/test/index.test.tsx b/examples/react/test/index.test.tsx
index 1c69dc57e..886023456 100644
--- a/examples/react/test/index.test.tsx
+++ b/examples/react/test/index.test.tsx
@@ -129,6 +129,5 @@ describe('RemoteSource Component', () => {
await expect.element(loginButton).toBeInTheDocument()
await loginButton.click()
- await expect.element(loginButton).toBeInTheDocument()
})
})
diff --git a/examples/sveltekit/test/index.test.ts b/examples/sveltekit/test/index.test.ts
index 2c11f4fb7..a14fb3537 100644
--- a/examples/sveltekit/test/index.test.ts
+++ b/examples/sveltekit/test/index.test.ts
@@ -132,7 +132,6 @@ describe('RemoteSource Component', () => {
await expect.element(loginButton).toBeInTheDocument()
await loginButton.click()
- await expect.element(loginButton).toBeInTheDocument()
})
})
diff --git a/examples/vue/test/index.test.ts b/examples/vue/test/index.test.ts
index 8fd0be53b..1aa54275d 100644
--- a/examples/vue/test/index.test.ts
+++ b/examples/vue/test/index.test.ts
@@ -131,6 +131,5 @@ describe('RemoteSource Component', () => {
await expect.element(loginButton).toBeInTheDocument()
await loginButton.click()
- await expect.element(loginButton).toBeInTheDocument()
})
})
diff --git a/package.json b/package.json
index fbc1d08dc..19499e50c 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "@uppy-dev/build",
"version": "0.0.0",
"private": true,
- "description": "Extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:",
+ "description": "Extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Dropbox, Google Drive, S3 and more :dog:",
"license": "MIT",
"workspaces": [
"examples/*",
diff --git a/packages/@uppy/companion-client/src/Provider.ts b/packages/@uppy/companion-client/src/Provider.ts
index 4e0ef1a3b..7a50897e4 100644
--- a/packages/@uppy/companion-client/src/Provider.ts
+++ b/packages/@uppy/companion-client/src/Provider.ts
@@ -5,8 +5,11 @@ import type {
UnknownProviderPlugin,
Uppy,
} from '@uppy/core'
-import type { CompanionClientProvider, RequestOptions } from '@uppy/utils'
-import { isOriginAllowed } from './getAllowedHosts.js'
+import {
+ type CompanionClientProvider,
+ getSocketHost,
+ type RequestOptions,
+} from '@uppy/utils'
import type { CompanionPluginOptions } from './index.js'
import RequestClient, { authErrorStatusCode } from './RequestClient.js'
@@ -135,22 +138,27 @@ export default class Provider
authUrl({
authFormData,
query,
+ authCallbackToken,
}: {
authFormData: unknown
query: Record
+ authCallbackToken?: string | undefined
}): string {
- const params = new URLSearchParams({
+ const searchParams = new URLSearchParams({
...query,
- // This is only used for Companion instances configured to accept multiple origins.
+ // `origin` is only used for Companion instances configured to accept multiple origins.
state: btoa(JSON.stringify({ origin: getOrigin() })),
...this.authQuery({ authFormData }),
})
if (this.preAuthToken) {
- params.set('uppyPreAuthToken', this.preAuthToken)
+ searchParams.set('uppyPreAuthToken', this.preAuthToken)
+ }
+ if (authCallbackToken) {
+ searchParams.set('authCallbackToken', authCallbackToken)
}
- return `${this.hostname}/${this.id}/connect?${params}`
+ return `${this.hostname}/${this.id}/connect?${searchParams}`
}
protected async loginSimpleAuth({
@@ -181,67 +189,62 @@ export default class Provider
signal: AbortSignal
}): Promise {
await this.ensurePreAuth()
-
signal.throwIfAborted()
- const link = this.authUrl({ query: { uppyVersions }, authFormData })
+ // Important: We need to do this synchronously, or else browsers might block the popup.
+ // (We cannot wait until the websocket connection is established).
+ // This opens up a race condtition if the websocket is not connected before the user
+ // completes(or cancels) authentication, but that’s a small compromose we gotta make.
+ const authCallbackToken = crypto.randomUUID()
+
+ const link = this.authUrl({
+ query: { uppyVersions },
+ authFormData,
+ authCallbackToken,
+ })
const authWindow = window.open(link, '_blank')
let interval: number | undefined
- let handleMessage: ((e: MessageEvent) => void) | undefined
+ let webSocket: WebSocket | undefined
try {
- return await new Promise((resolve, reject) => {
- handleMessage = (e: MessageEvent) => {
- if (e.source !== authWindow) {
- let jsonData = ''
- try {
- // TODO improve our uppy logger so that it can take an arbitrary number of arguments,
- // each either objects, errors or strings,
- // then we don’t have to manually do these things like json stringify when logging.
- // the logger should never throw an error.
- jsonData = JSON.stringify(e.data)
- } catch (_err) {
- // in case JSON.stringify fails (ignored)
+ const host = getSocketHost(this.opts.companionUrl)
+
+ // Note that this promise is not guaranteed to settle in all cases
+ const token = await new Promise((resolve, reject) => {
+ webSocket = new WebSocket(
+ `${host}/api2/auth-callback/token/${authCallbackToken}`,
+ )
+
+ webSocket.addEventListener('close', () => {
+ reject(new Error('Socket closed'))
+ })
+
+ webSocket.addEventListener('error', (error) => {
+ this.uppy.log(
+ `Companion socket error ${JSON.stringify(error)}, closing socket`,
+ 'warning',
+ )
+ webSocket?.close() // 'close' event will be emitted
+ })
+
+ webSocket.addEventListener('message', (e) => {
+ try {
+ const { token, error } = JSON.parse(e.data)
+ if (error) {
+ reject(new Error('Authentication reported error'))
+ } else if (!token) {
+ reject(new Error('Authentication did not return a token'))
+ } else {
+ resolve(token)
}
- this.uppy.log(
- `ignoring event from unknown source ${jsonData}`,
- 'warning',
- )
- return
+ } catch (err) {
+ reject(err)
}
+ })
- const { companionAllowedHosts } = this.#getPlugin().opts
- if (!isOriginAllowed(e.origin, companionAllowedHosts)) {
- this.uppy.log(
- `ignoring event from ${e.origin} vs allowed pattern ${companionAllowedHosts}`,
- 'warning',
- )
- // We cannot reject here because the page might send events from other origins
- // before sending the "real" auth completed event.
- // for example Box has a "Pendo" tool that sends events to the opener
- // https://github.com/transloadit/uppy/pull/5719
- return
- }
-
- // Check if it's a string before doing the JSON.parse to maintain support
- // for older Companion versions that used object references
- const data = typeof e.data === 'string' ? JSON.parse(e.data) : e.data
-
- if (data.error) {
- const { uppy } = this
- const message = uppy.i18n('authAborted')
- uppy.info({ message }, 'warning', 5000)
- reject(new Error('auth aborted'))
- return
- }
-
- if (!data.token) {
- reject(new Error('did not receive token from auth window'))
- return
- }
-
- resolve(this.setAuthToken(data.token))
- }
+ signal.addEventListener('abort', () =>
+ reject(new Error('Authentication was aborted')),
+ )
// poll for user closure of the window, so we can reject when it happens
if (authWindow) {
@@ -251,15 +254,21 @@ export default class Provider
}
}, 500)
}
-
- signal.addEventListener('abort', () => reject(new Error('Aborted')))
- window.addEventListener('message', handleMessage)
})
+
+ this.setAuthToken(token)
+ } catch (err) {
+ const message = this.uppy.i18n('authAborted')
+ this.uppy.info({ message }, 'warning', 5000)
+ this.uppy.log(`Authentication failed: ${err.message}`, 'warning')
+ throw err
} finally {
// cleanup:
- authWindow?.close()
- window.clearInterval(interval)
- if (handleMessage) window.removeEventListener('message', handleMessage)
+ // 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}`)
+ webSocket?.close()
+ clearInterval(interval)
}
}
diff --git a/packages/@uppy/companion-client/src/getAllowedHosts.test.ts b/packages/@uppy/companion-client/src/getAllowedHosts.test.ts
index d82c31583..45eef785e 100644
--- a/packages/@uppy/companion-client/src/getAllowedHosts.test.ts
+++ b/packages/@uppy/companion-client/src/getAllowedHosts.test.ts
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest'
-import getAllowedHosts, { isOriginAllowed } from './getAllowedHosts.js'
+import getAllowedHosts from './getAllowedHosts.js'
describe('getAllowedHosts', () => {
it('can convert companionAllowedHosts', () => {
@@ -30,20 +30,3 @@ describe('getAllowedHosts', () => {
)
})
})
-
-describe('isOriginAllowed', () => {
- it('should check origin', () => {
- expect(isOriginAllowed('a', [/^.+$/])).toBeTruthy()
- expect(isOriginAllowed('a', ['^.+$'])).toBeTruthy()
- expect(
- isOriginAllowed('www.transloadit.com', ['^www\\.transloadit\\.com$']),
- ).toBeTruthy()
- expect(
- isOriginAllowed('www.transloadit.com', ['^transloadit\\.com$']),
- ).toBeFalsy()
- expect(isOriginAllowed('match', ['fail', 'match'])).toBeTruthy()
- expect(
- isOriginAllowed('www.transloadit.com', ['\\.transloadit\\.com$']),
- ).toBeTruthy()
- })
-})
diff --git a/packages/@uppy/companion-client/src/getAllowedHosts.ts b/packages/@uppy/companion-client/src/getAllowedHosts.ts
index 5487dca03..ff58300d8 100644
--- a/packages/@uppy/companion-client/src/getAllowedHosts.ts
+++ b/packages/@uppy/companion-client/src/getAllowedHosts.ts
@@ -47,15 +47,3 @@ export default function getAllowedHosts(
ret = escapeRegex(ret)
return ret
}
-
-export function isOriginAllowed(
- origin: string,
- allowedOrigin: string | RegExp | Array | undefined,
-): boolean {
- const patterns = Array.isArray(allowedOrigin)
- ? allowedOrigin.map(wrapInRegex)
- : [wrapInRegex(allowedOrigin)]
- return patterns.some(
- (pattern) => pattern?.test(origin) || pattern?.test(`${origin}/`),
- ) // allowing for trailing '/'
-}
diff --git a/packages/@uppy/companion/KUBERNETES.md b/packages/@uppy/companion/KUBERNETES.md
index 5b148a478..e72b2c131 100644
--- a/packages/@uppy/companion/KUBERNETES.md
+++ b/packages/@uppy/companion/KUBERNETES.md
@@ -38,8 +38,6 @@ data:
COMPANION_BOX_SECRET: 'YOUR BOX SECRET'
COMPANION_GOOGLE_KEY: 'YOUR GOOGLE KEY'
COMPANION_GOOGLE_SECRET: 'YOUR GOOGLE SECRET'
- COMPANION_INSTAGRAM_KEY: 'YOUR INSTAGRAM KEY'
- COMPANION_INSTAGRAM_SECRET: 'YOUR INSTAGRAM SECRET'
COMPANION_AWS_KEY: 'YOUR AWS KEY'
COMPANION_AWS_SECRET: 'YOUR AWS SECRET'
COMPANION_AWS_BUCKET: 'YOUR AWS S3 BUCKET'
diff --git a/packages/@uppy/companion/README.md b/packages/@uppy/companion/README.md
index bed8ac782..30e37e963 100644
--- a/packages/@uppy/companion/README.md
+++ b/packages/@uppy/companion/README.md
@@ -8,7 +8,7 @@ Companion is a server integration for
[Uppy](https://github.com/transloadit/uppy) file uploader.
It handles the server-to-server communication between your server and file
-storage providers such as Google Drive, Dropbox, Instagram, etc. **Companion is
+storage providers such as Google Drive, Dropbox, etc. **Companion is
not a target to upload files to**. For this, use a server (if
you want resumable) or your existing Apache/Nginx server (if you don’t).
[See here for full documentation](https://uppy.io/docs/companion/)
diff --git a/packages/@uppy/companion/env_example b/packages/@uppy/companion/env_example
index cd5726e69..162232a21 100644
--- a/packages/@uppy/companion/env_example
+++ b/packages/@uppy/companion/env_example
@@ -26,10 +26,6 @@ COMPANION_GOOGLE_KEY=
COMPANION_GOOGLE_SECRET=
COMPANION_GOOGLE_SECRET_FILE=
-COMPANION_INSTAGRAM_KEY=
-COMPANION_INSTAGRAM_SECRET=
-COMPANION_INSTAGRAM_SECRET_FILE=
-
COMPANION_FACEBOOK_KEY=
COMPANION_FACEBOOK_SECRET=
COMPANION_FACEBOOK_SECRET_FILE=
diff --git a/packages/@uppy/companion/src/config/grant.ts b/packages/@uppy/companion/src/config/grant.ts
index c923b02c7..c8ad6af17 100644
--- a/packages/@uppy/companion/src/config/grant.ts
+++ b/packages/@uppy/companion/src/config/grant.ts
@@ -56,10 +56,6 @@ export default function grantConfig(): GrantStaticConfig {
access_url: 'https://api.box.com/oauth2/token',
callback: '/box/callback',
},
- instagram: {
- ...defaults,
- callback: '/instagram/callback',
- },
facebook: {
...defaults,
scope: ['email', 'user_photos'],
diff --git a/packages/@uppy/companion/src/server/Uploader.ts b/packages/@uppy/companion/src/server/Uploader.ts
index 2352e34d5..b564cdafa 100644
--- a/packages/@uppy/companion/src/server/Uploader.ts
+++ b/packages/@uppy/companion/src/server/Uploader.ts
@@ -222,7 +222,7 @@ export default class Uploader {
/**
* Uploads file to destination based on the supplied protocol (tus, s3-multipart, multipart)
* For tus uploads, the deferredLength option is enabled, because file size value can be unreliable
- * for some providers (Instagram particularly)
+ * for some providers.
*
* @param optionsIn
*/
diff --git a/packages/@uppy/companion/src/server/controllers/callback.ts b/packages/@uppy/companion/src/server/controllers/callback.ts
index 93f864d8f..7e1ab3a69 100644
--- a/packages/@uppy/companion/src/server/controllers/callback.ts
+++ b/packages/@uppy/companion/src/server/controllers/callback.ts
@@ -3,32 +3,23 @@
*/
import type { NextFunction, Request, Response } from 'express'
-import serialize from 'serialize-javascript'
+import emitter from '../emitter/index.js'
+import {
+ authCallbackErrorHtml,
+ legacyAuthCallbackHtml,
+} from '../helpers/html.js'
import * as tokenService from '../helpers/jwt.js'
import * as oAuthState from '../helpers/oauth-state.js'
import logger from '../logger.js'
-const closePageHtml = (origin: string | undefined) => `
-
-
-
-
-
-
- Authentication failed.
- `
-
export default function callback(
req: Request,
res: Response,
next: NextFunction,
): void {
const providerName = req.params['providerName']
+ const { companion } = req
+
if (providerName == null || providerName.length === 0) {
res.sendStatus(400)
return
@@ -51,7 +42,25 @@ export default function callback(
req.id,
)
logger.debug(req.session?.grant?.response, 'callback.oauth.resp', req.id)
- res.status(400).send(closePageHtml(originString))
+ const authCallbackToken =
+ grantDynamic.state &&
+ oAuthState.getFromState(
+ grantDynamic.state,
+ 'authCallbackToken',
+ companion.options.secret,
+ )
+ // only new Uppy clients will set an authCallbackToken in the state
+ // in that case, we send the token through the emitter.
+ if (authCallbackToken) {
+ emitter().emit(authCallbackToken, { error: true })
+ res.status(400).send(authCallbackErrorHtml())
+ } else {
+ // This is backwards compatible with old Uppy clients:
+ res
+ .status(400)
+ .send(legacyAuthCallbackHtml({ error: true }, originString))
+ }
+
return
}
diff --git a/packages/@uppy/companion/src/server/controllers/connect.ts b/packages/@uppy/companion/src/server/controllers/connect.ts
index 2d62c4d95..a30e607da 100644
--- a/packages/@uppy/companion/src/server/controllers/connect.ts
+++ b/packages/@uppy/companion/src/server/controllers/connect.ts
@@ -123,6 +123,11 @@ export default function connect(
stateObj.preAuthToken = preAuthTokenValue
}
+ const authCallbackToken = req.query['authCallbackToken']
+ if (typeof authCallbackToken === 'string') {
+ stateObj.authCallbackToken = authCallbackToken
+ }
+
// Get the computed header generated by `cors` in a previous middleware.
stateObj.origin = res.getHeader('Access-Control-Allow-Origin')
let clientOrigin: string | undefined
diff --git a/packages/@uppy/companion/src/server/controllers/send-token.ts b/packages/@uppy/companion/src/server/controllers/send-token.ts
index b0396fa28..44efb0679 100644
--- a/packages/@uppy/companion/src/server/controllers/send-token.ts
+++ b/packages/@uppy/companion/src/server/controllers/send-token.ts
@@ -1,45 +1,12 @@
import type { NextFunction, Request, Response } from 'express'
-import serialize from 'serialize-javascript'
+import emitter from '../emitter/index.js'
+import {
+ authCallbackSuccessHtml,
+ legacyAuthCallbackHtml,
+} from '../helpers/html.js'
import * as oAuthState from '../helpers/oauth-state.js'
import { isOriginAllowed } from './connect.js'
-const htmlContent = (token: string, origin: string): string => {
- return `
-
-
-
-
-
-
-
-
-
- `
-}
-
export default function sendToken(
req: Request,
res: Response,
@@ -74,5 +41,18 @@ export default function sendToken(
return
}
- res.send(htmlContent(`${uppyAuthToken}`, clientOrigin))
+ const authCallbackToken = oAuthState.getFromState(
+ state,
+ 'authCallbackToken',
+ companion.options.secret,
+ )
+ // only new Uppy clients will set an authCallbackToken in the state
+ // in that case, we send the token through the emitter.
+ if (authCallbackToken) {
+ emitter().emit(authCallbackToken, { token: uppyAuthToken })
+ res.send(authCallbackSuccessHtml())
+ } else {
+ // This is backwards compatible with old Uppy clients:
+ res.send(legacyAuthCallbackHtml({ token: uppyAuthToken }, clientOrigin))
+ }
}
diff --git a/packages/@uppy/companion/src/server/helpers/html.ts b/packages/@uppy/companion/src/server/helpers/html.ts
new file mode 100644
index 000000000..a7e723db5
--- /dev/null
+++ b/packages/@uppy/companion/src/server/helpers/html.ts
@@ -0,0 +1,92 @@
+import serialize from 'serialize-javascript'
+
+export const authCallbackSuccessHtml = () => {
+ return `
+
+
+
+
+
+
+
+
Authentication successful. You may now close this page.