diff --git a/package-lock.json b/package-lock.json index 0b9cb07f2..a49987c42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6350,8 +6350,8 @@ "express-interceptor": "1.2.0", "express-prom-bundle": "3.3.0", "express-request-id": "1.4.1", - "express-session": "1.17.0", - "grant-express": "4.1.2", + "express-session": "1.15.6", + "grant": "4.6.5", "helmet": "3.21.2", "isobject": "3.0.1", "jsonwebtoken": "8.5.1", @@ -6493,35 +6493,19 @@ "integrity": "sha512-6SK3MG/Bbhm8MsgyJAylg+ucIOU71/FzyFalcfu5nY19dH8y/z0tBJU0wrNBXD4B27EoQtqPF/9wqH0iYAd04g==" }, "express-session": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.0.tgz", - "integrity": "sha512-t4oX2z7uoSqATbMfsxWMbNjAL0T5zpvcJCk3Z9wnPPN7ibddhnmDZXHfEcoBMG2ojKXZoCyPMc5FbtK+G7SoDg==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.15.6.tgz", + "integrity": "sha512-r0nrHTCYtAMrFwZ0kBzZEXa1vtPVrw0dKvGSrKP4dahwBQ1BJpF2/y1Pp4sCD/0kvxV4zZeclyvfmw0B4RMJQA==", "requires": { - "cookie": "0.4.0", + "cookie": "0.3.1", "cookie-signature": "1.0.6", + "crc": "3.4.4", "debug": "2.6.9", - "depd": "~2.0.0", - "on-headers": "~1.0.2", - "parseurl": "~1.3.3", - "safe-buffer": "5.2.0", - "uid-safe": "~2.1.5" - }, - "dependencies": { - "cookie": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", - "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" - } + "depd": "~1.1.1", + "on-headers": "~1.0.1", + "parseurl": "~1.3.2", + "uid-safe": "~2.1.5", + "utils-merge": "1.0.1" } }, "frameguard": { @@ -12933,8 +12917,7 @@ "crc": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz", - "integrity": "sha1-naHpgOO9RPxck79as9ozeNheRms=", - "dev": true + "integrity": "sha1-naHpgOO9RPxck79as9ozeNheRms=" }, "crc32-stream": { "version": "3.0.1", diff --git a/packages/@uppy/companion/package.json b/packages/@uppy/companion/package.json index 4bc65ea53..8f9428f03 100644 --- a/packages/@uppy/companion/package.json +++ b/packages/@uppy/companion/package.json @@ -42,8 +42,8 @@ "express-interceptor": "1.2.0", "express-prom-bundle": "3.3.0", "express-request-id": "1.4.1", - "express-session": "1.17.0", - "grant-express": "4.1.2", + "express-session": "1.15.6", + "grant": "4.6.5", "helmet": "3.21.2", "isobject": "3.0.1", "jsonwebtoken": "8.5.1", diff --git a/packages/@uppy/companion/src/companion.js b/packages/@uppy/companion/src/companion.js index 4a8a476f9..3c1ca672f 100644 --- a/packages/@uppy/companion/src/companion.js +++ b/packages/@uppy/companion/src/companion.js @@ -1,6 +1,6 @@ const express = require('express') // @ts-ignore -const Grant = require('grant-express') +const Grant = require('grant').express() const grantConfig = require('./config/grant')() const providerManager = require('./server/provider') const controllers = require('./server/controllers') @@ -19,7 +19,6 @@ const { STORAGE_PREFIX } = require('./server/Uploader') const middlewares = require('./server/middlewares') const { shortenToken } = require('./server/Uploader') -const providers = providerManager.getDefaultProviders() const defaultOptions = { server: { protocol: 'http', @@ -44,6 +43,7 @@ const defaultOptions = { */ module.exports.app = (options = {}) => { options = merge({}, defaultOptions, options) + const providers = providerManager.getDefaultProviders(options) providerManager.addProviderOptions(options, grantConfig) const customProviders = options.customProviders @@ -61,7 +61,7 @@ module.exports.app = (options = {}) => { app.use(cookieParser()) // server tokens are added to cookies app.use(interceptGrantErrorResponse) - app.use(new Grant(grantConfig)) + app.use(Grant(grantConfig)) app.use((req, res, next) => { res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, DELETE') res.header( diff --git a/packages/@uppy/companion/src/server/controllers/send-token.js b/packages/@uppy/companion/src/server/controllers/send-token.js index 4f072a83b..d5dde169e 100644 --- a/packages/@uppy/companion/src/server/controllers/send-token.js +++ b/packages/@uppy/companion/src/server/controllers/send-token.js @@ -19,7 +19,8 @@ module.exports = function sendToken (req, res, next) { // add the token to cookies for thumbnail/image requests tokenService.addToCookies(res, uppyAuthToken, req.companion.options, req.companion.provider.authProvider) - const state = (req.session.grant || {}).state + const dynamic = (req.session.grant || {}).dynamic || {} + const state = dynamic.state if (state) { const origin = oAuthState.getFromState(state, 'origin', req.companion.options.secret) const clientVersion = oAuthState.getFromState( diff --git a/packages/@uppy/companion/src/server/provider/Provider.js b/packages/@uppy/companion/src/server/provider/Provider.js new file mode 100644 index 000000000..0a935e966 --- /dev/null +++ b/packages/@uppy/companion/src/server/provider/Provider.js @@ -0,0 +1,64 @@ +/** + * Provider interface defines the specifications of any provider implementation + */ +class Provider { + /** + * + * @param {object} options + */ + constructor (options) { + return this + } + + /** + * config to extend the grant config + */ + static getExtraConfig () { + return {} + } + + /** + * list the files and folders in the provider account + * @param {object} options + * @param {function} cb + */ + list (options, cb) { + throw new Error('method not implemented') + } + + /** + * download a certain file from the provider account + * @param {object} options + * @param {function} cb + */ + download (options, cb) { + throw new Error('method not implemented') + } + + /** + * return a thumbnail for a provider file + * @param {object} options + * @param {function} cb + */ + thumbnail (options, cb) { + throw new Error('method not implemented') + } + + /** + * get the size of a certain file in the provider account + * @param {object} options + * @param {function} cb + */ + size (options, cb) { + throw new Error('method not implemented') + } + + /** + * @returns {string} + */ + static get authProvider () { + return '' + } +} + +module.exports = Provider diff --git a/packages/@uppy/companion/src/server/provider/drive/index.js b/packages/@uppy/companion/src/server/provider/drive/index.js index c9421ec12..98e6d777d 100644 --- a/packages/@uppy/companion/src/server/provider/drive/index.js +++ b/packages/@uppy/companion/src/server/provider/drive/index.js @@ -1,3 +1,5 @@ +const Provider = require('../Provider') + const request = require('request') // @ts-ignore const purest = require('purest')({ request }) @@ -9,8 +11,9 @@ const DRIVE_FILES_FIELDS = `kind,nextPageToken,incompleteSearch,files(${DRIVE_FI // using wildcard to get all 'drive' fields because specifying fields seems no to work for the /drives endpoint const SHARED_DRIVE_FIELDS = '*' -class Drive { +class Drive extends Provider { constructor (options) { + super(options) this.authProvider = options.provider = Drive.authProvider options.alias = 'drive' options.version = 'v3' diff --git a/packages/@uppy/companion/src/server/provider/dropbox/index.js b/packages/@uppy/companion/src/server/provider/dropbox/index.js index 2193bb844..3fc117e77 100644 --- a/packages/@uppy/companion/src/server/provider/dropbox/index.js +++ b/packages/@uppy/companion/src/server/provider/dropbox/index.js @@ -1,3 +1,5 @@ +const Provider = require('../Provider') + const request = require('request') const purest = require('purest')({ request }) const logger = require('../../logger') @@ -17,8 +19,9 @@ function httpHeaderSafeJson (v) { ) } -class DropBox { +class DropBox extends Provider { constructor (options) { + super(options) this.authProvider = options.provider = DropBox.authProvider this.client = purest(options) } diff --git a/packages/@uppy/companion/src/server/provider/facebook/index.js b/packages/@uppy/companion/src/server/provider/facebook/index.js index d5226b25c..1c34fc445 100644 --- a/packages/@uppy/companion/src/server/provider/facebook/index.js +++ b/packages/@uppy/companion/src/server/provider/facebook/index.js @@ -1,3 +1,5 @@ +const Provider = require('../Provider') + const request = require('request') const purest = require('purest')({ request }) const utils = require('../../helpers/utils') @@ -5,8 +7,9 @@ const logger = require('../../logger') const adapter = require('./adapter') const AuthError = require('../error') -class Facebook { +class Facebook extends Provider { constructor (options) { + super(options) this.authProvider = options.provider = Facebook.authProvider this.client = purest(options) } diff --git a/packages/@uppy/companion/src/server/provider/index.js b/packages/@uppy/companion/src/server/provider/index.js index 895491341..8dc742c3c 100644 --- a/packages/@uppy/companion/src/server/provider/index.js +++ b/packages/@uppy/companion/src/server/provider/index.js @@ -6,62 +6,13 @@ const config = require('@purest/providers') const dropbox = require('./dropbox') const drive = require('./drive') const instagram = require('./instagram') +const instagramGraph = require('./instagram/graph') const facebook = require('./facebook') const onedrive = require('./onedrive') const { getURLBuilder } = require('../helpers/utils') const logger = require('../logger') - -/** - * Provider interface defines the specifications of any provider implementation - * - * @interface - */ -class Provider { - /** - * - * @param {object} options - */ - constructor (options) { - return this - } - - /** - * - * @param {object} options - * @param {function} cb - */ - list (options, cb) {} - - /** - * - * @param {object} options - * @param {function} cb - */ - download (options, cb) {} - - /** - * - * @param {object} options - * @param {function} cb - */ - thumbnail (options, cb) {} - - /** - * - * @param {object} options - * @param {function} cb - */ - size (options, cb) {} - - /** - * @returns {string} - */ - static get authProvider () { - return '' - } -} - -module.exports.ProviderInterface = Provider +// eslint-disable-next-line +const Provider = require('./Provider') /** * adds the desired provider module to the request object, @@ -90,10 +41,21 @@ module.exports.getProviderMiddleware = (providers) => { } /** + * @param {{server: object, providerOptions: object}} companionOptions * @return {Object.} */ -module.exports.getDefaultProviders = () => { - return { dropbox, drive, instagram, facebook, onedrive } +module.exports.getDefaultProviders = (companionOptions) => { + const { providerOptions } = companionOptions || { providerOptions: null } + const providers = { dropbox, drive, facebook, onedrive } + // Instagram's Graph API key is just numbers, while the old API key is hex + const usesGraphAPI = () => /^\d+$/.test(providerOptions.instagram.key) + if (providerOptions && providerOptions.instagram && usesGraphAPI()) { + providers.instagram = instagramGraph + } else { + providers.instagram = instagram + } + + return providers } /** @@ -113,17 +75,17 @@ module.exports.addCustomProviders = (customProviders, providers, grantConfig) => /** * - * @param {{server: object, providerOptions: object}} options + * @param {{server: object, providerOptions: object}} companionOptions * @param {object} grantConfig */ -module.exports.addProviderOptions = (options, grantConfig) => { - const { server, providerOptions } = options +module.exports.addProviderOptions = (companionOptions, grantConfig) => { + const { server, providerOptions } = companionOptions if (!validOptions({ server })) { logger.warn('invalid provider options detected. Providers will not be loaded', 'provider.options.invalid') return } - grantConfig.server = { + grantConfig.defaults = { host: server.host, protocol: server.protocol, path: server.path @@ -136,13 +98,15 @@ module.exports.addProviderOptions = (options, grantConfig) => { // explicitly add providerOptions so users don't override other providerOptions. grantConfig[authProvider].key = providerOptions[authProvider].key grantConfig[authProvider].secret = providerOptions[authProvider].secret + const { provider, name } = authNameToProvider(authProvider, companionOptions) + Object.assign(grantConfig[authProvider], provider.getExtraConfig()) // override grant.js redirect uri with companion's custom redirect url if (oauthDomain) { - const providerName = authToProviderName(authProvider) + const providerName = name const redirectPath = `/${providerName}/redirect` const isExternal = !!server.implicitPath - const fullRedirectPath = getURLBuilder(options)(redirectPath, isExternal, true) + const fullRedirectPath = getURLBuilder(companionOptions)(redirectPath, isExternal, true) grantConfig[authProvider].redirect_uri = `${server.protocol}://${oauthDomain}${fullRedirectPath}` } @@ -161,14 +125,16 @@ module.exports.addProviderOptions = (options, grantConfig) => { /** * * @param {string} authProvider + * @param {{server: object, providerOptions: object}} options + * @return {{name: string, provider: typeof Provider}} */ -const authToProviderName = (authProvider) => { - const providers = exports.getDefaultProviders() +const authNameToProvider = (authProvider, options) => { + const providers = exports.getDefaultProviders(options) const providerNames = Object.keys(providers) for (const name of providerNames) { const provider = providers[name] if (provider.authProvider === authProvider) { - return name + return { name, provider } } } } diff --git a/packages/@uppy/companion/src/server/provider/instagram/graph/adapter.js b/packages/@uppy/companion/src/server/provider/instagram/graph/adapter.js new file mode 100644 index 000000000..fe1f4ff3a --- /dev/null +++ b/packages/@uppy/companion/src/server/provider/instagram/graph/adapter.js @@ -0,0 +1,72 @@ +const querystring = require('querystring') + +const MEDIA_TYPES = Object.freeze({ + video: 'VIDEO', + carousel: 'CAROUSEL_ALBUM', + image: 'IMAGE' +}) + +const isVideo = (item) => item.media_type === MEDIA_TYPES.video + +exports.isFolder = (_) => { + return false +} + +exports.getItemIcon = (item) => { + return isVideo(item) ? item.thumbnail_url : item.media_url +} + +exports.getItemSubList = (item) => { + const newItems = [] + item.data.forEach((subItem) => { + // exclude videos because of bug https://developers.facebook.com/support/bugs/801145630390846/ + // @todo remove this clause when bug is fixed + if (isVideo(subItem)) { + return + } + + if (subItem.media_type === MEDIA_TYPES.carousel) { + subItem.children.data.forEach((i) => { + // exclude videos because of bug https://developers.facebook.com/support/bugs/801145630390846/ + // @todo remove this clause when bug is fixed + if (isVideo(i)) { + return + } + + newItems.push(i) + }) + } else { + newItems.push(subItem) + } + }) + return newItems +} + +exports.getItemName = (item, index) => { + const ext = isVideo(item) ? 'mp4' : 'jpeg' + // adding index, so the name is unique + return `Instagram ${item.timestamp}${index}.${ext}` +} + +exports.getMimeType = (item) => { + return isVideo(item) ? 'video/mp4' : 'image/jpeg' +} + +exports.getItemId = (item) => item.id + +exports.getItemRequestPath = (item) => item.id + +exports.getItemModifiedDate = (item) => item.timestamp + +exports.getItemThumbnailUrl = (item) => exports.getItemIcon(item) + +exports.getNextPagePath = (data, currentQuery, currentPath) => { + if (!data.paging || !data.paging.cursors) { + return null + } + + const query = Object.assign({}, currentQuery, { + cursor: data.paging.cursors.after + }) + return `${currentPath || ''}?${querystring.stringify(query)}` +} diff --git a/packages/@uppy/companion/src/server/provider/instagram/graph/index.js b/packages/@uppy/companion/src/server/provider/instagram/graph/index.js new file mode 100644 index 000000000..1bfe2734c --- /dev/null +++ b/packages/@uppy/companion/src/server/provider/instagram/graph/index.js @@ -0,0 +1,154 @@ +const Provider = require('../../Provider') + +const request = require('request') +const purest = require('purest')({ request }) +const utils = require('../../../helpers/utils') +const logger = require('../../../logger') +const adapter = require('./adapter') +const AuthError = require('../../error') + +class Instagram extends Provider { + constructor (options) { + super(options) + this.authProvider = options.provider = Instagram.authProvider + this.client = purest(options) + } + + static getExtraConfig () { + return { + protocol: 'https', + scope: ['user_profile', 'user_media'] + } + } + + static get authProvider () { + return 'instagram' + } + + list ({ directory, token, query = {} }, done) { + const qs = { + fields: 'id,media_type,thumbnail_url,media_url,timestamp,children{media_type,media_url,thumbnail_url,timestamp}' + } + + if (query.cursor) { + qs.after = query.cursor + } + + this.client + .get('https://graph.instagram.com/me/media') + .qs(qs) + .auth(token) + .request((err, resp, body) => { + if (err || resp.statusCode !== 200) { + err = this._error(err, resp) + logger.error(err, 'provider.instagram.list.error') + return done(err) + } else { + this._getUsername(token, (err, username) => { + err ? done(err) : done(null, this.adaptData(body, username, directory, query)) + }) + } + }) + } + + _getUsername (token, done) { + this.client + .get('https://graph.instagram.com/me') + .qs({ fields: 'username' }) + .auth(token) + .request((err, resp, body) => { + if (err || resp.statusCode !== 200) { + err = this._error(err, resp) + logger.error(err, 'provider.instagram.user.error') + return done(err) + } else { + done(null, body.username) + } + }) + } + + download ({ id, token }, onData) { + return this.client + .get(`https://graph.instagram.com/${id}`) + .qs({ fields: 'media_url' }) + .auth(token) + .request((err, resp, body) => { + if (err) return logger.error(err, 'provider.instagram.download.error') + request(body.media_url) + .on('data', onData) + .on('end', () => onData(null)) + .on('error', (err) => { + logger.error(err, 'provider.instagram.download.url.error') + }) + }) + } + + thumbnail (_, done) { + // not implementing this because a public thumbnail from instagram will be used instead + const err = new Error('call to thumbnail is not implemented') + logger.error(err, 'provider.instagram.thumbnail.error') + return done(err) + } + + size ({ id, token }, done) { + return this.client + .get(`https://graph.instagram.com/${id}`) + .qs({ fields: 'media_url' }) + .auth(token) + .request((err, resp, body) => { + if (err || resp.statusCode !== 200) { + err = this._error(err, resp) + logger.error(err, 'provider.instagram.size.error') + return done(err) + } + + utils.getURLMeta(body.media_url) + .then(({ size }) => done(null, size)) + .catch((err) => { + logger.error(err, 'provider.instagram.size.error') + done() + }) + }) + } + + logout (_, done) { + // access revoke is not supported by Instagram's API + done(null, { revoked: false, manual_revoke_url: 'https://www.instagram.com/accounts/manage_access/' }) + } + + adaptData (res, username, directory, currentQuery) { + const data = { username: username, items: [] } + const items = adapter.getItemSubList(res) + items.forEach((item, i) => { + data.items.push({ + isFolder: adapter.isFolder(item), + icon: adapter.getItemIcon(item), + name: adapter.getItemName(item, i), + mimeType: adapter.getMimeType(item), + id: adapter.getItemId(item), + thumbnail: adapter.getItemThumbnailUrl(item), + requestPath: adapter.getItemRequestPath(item), + modifiedDate: adapter.getItemModifiedDate(item) + }) + }) + + data.nextPagePath = adapter.getNextPagePath(res, currentQuery, directory) + return data + } + + _error (err, resp) { + if (resp) { + if (resp.body && resp.body.error.code === 190) { + // Invalid OAuth 2.0 Access Token + return new AuthError() + } + + const msg = resp.body && resp.body.error ? resp.body.error.message : '' + return new Error(`request to ${this.authProvider} returned status: ${resp.statusCode}, message: ${msg}`) + } + + return err + } +} + +module.exports = Instagram diff --git a/packages/@uppy/companion/src/server/provider/instagram/index.js b/packages/@uppy/companion/src/server/provider/instagram/index.js index a5865e344..f155fbd5a 100644 --- a/packages/@uppy/companion/src/server/provider/instagram/index.js +++ b/packages/@uppy/companion/src/server/provider/instagram/index.js @@ -1,3 +1,5 @@ +const Provider = require('../Provider') + const request = require('request') const purest = require('purest')({ request }) const utils = require('../../helpers/utils') @@ -5,8 +7,9 @@ const logger = require('../../logger') const adapter = require('./adapter') const AuthError = require('../error') -class Instagram { +class Instagram extends Provider { constructor (options) { + super(options) this.authProvider = options.provider = Instagram.authProvider this.client = purest(options) } diff --git a/packages/@uppy/companion/src/server/provider/onedrive/index.js b/packages/@uppy/companion/src/server/provider/onedrive/index.js index 05da6403b..216c11427 100644 --- a/packages/@uppy/companion/src/server/provider/onedrive/index.js +++ b/packages/@uppy/companion/src/server/provider/onedrive/index.js @@ -1,11 +1,14 @@ +const Provider = require('../Provider') + const request = require('request') const purest = require('purest')({ request }) const logger = require('../../logger') const adapter = require('./adapter') const AuthError = require('../error') -class OneDrive { +class OneDrive extends Provider { constructor (options) { + super(options) this.authProvider = options.provider = OneDrive.authProvider this.client = purest(options) } diff --git a/packages/@uppy/companion/test/__tests__/provider-manager.js b/packages/@uppy/companion/test/__tests__/provider-manager.js index 7a12e9a7c..3a0d1c7f3 100644 --- a/packages/@uppy/companion/test/__tests__/provider-manager.js +++ b/packages/@uppy/companion/test/__tests__/provider-manager.js @@ -1,13 +1,14 @@ /* global jest:false, test:false, expect:false, describe:false, beforeEach:false */ const providerManager = require('../../src/server/provider') +const { getCompanionOptions } = require('../../src/standalone/helper') let grantConfig let companionOptions describe('Test Provider options', () => { beforeEach(() => { grantConfig = require('../../src/config/grant')() - companionOptions = require('../../src/standalone/helper').getCompanionOptions() + companionOptions = getCompanionOptions() }) test('adds provider options', () => { @@ -22,12 +23,44 @@ describe('Test Provider options', () => { expect(grantConfig.instagram.secret).toBe('instagram_secret') }) + test('adds extra provider config', () => { + process.env.COMPANION_INSTAGRAM_KEY = '123456' + providerManager.addProviderOptions(getCompanionOptions(), grantConfig) + expect(grantConfig.instagram).toEqual({ + transport: 'session', + callback: '/instagram/callback', + key: '123456', + secret: 'instagram_secret', + protocol: 'https', + scope: ['user_profile', 'user_media'] + }) + + expect(grantConfig.dropbox).toEqual({ + key: 'dropbox_key', + secret: 'dropbox_secret', + transport: 'session', + authorize_url: 'https://www.dropbox.com/oauth2/authorize', + access_url: 'https://api.dropbox.com/oauth2/token', + callback: '/dropbox/callback' + }) + + expect(grantConfig.google).toEqual({ + key: 'google_key', + secret: 'google_secret', + transport: 'session', + scope: [ + 'https://www.googleapis.com/auth/drive.readonly' + ], + callback: '/drive/callback' + }) + }) + test('adds provider options for secret files', () => { process.env.COMPANION_DROPBOX_SECRET_FILE = process.env.PWD + '/test/resources/dropbox_secret_file' process.env.COMPANION_GOOGLE_SECRET_FILE = process.env.PWD + '/test/resources/google_secret_file' process.env.COMPANION_INSTAGRAM_SECRET_FILE = process.env.PWD + '/test/resources/instagram_secret_file' - companionOptions = require('../../src/standalone/helper').getCompanionOptions() + companionOptions = getCompanionOptions() providerManager.addProviderOptions(companionOptions, grantConfig) diff --git a/packages/@uppy/companion/test/mockserver.js b/packages/@uppy/companion/test/mockserver.js index 6bb746479..7e2460943 100644 --- a/packages/@uppy/companion/test/mockserver.js +++ b/packages/@uppy/companion/test/mockserver.js @@ -12,7 +12,7 @@ authServer.all('*/callback', (req, res, next) => { next() }) authServer.all('/drive/send-token', (req, res, next) => { - req.session.grant = { state: req.query.state || 'non-empty-value' } + req.session.grant = { dynamic: { state: req.query.state || 'non-empty-value' } } next() })