diff --git a/.env.example b/.env.example index bac7262a7c..ffb5c438d7 100644 --- a/.env.example +++ b/.env.example @@ -54,6 +54,12 @@ COMPANION_UNSPLASH_SECRET=*** COMPANION_ONEDRIVE_KEY=*** COMPANION_ONEDRIVE_SECRET=**** +# To test dynamic Oauth against local companion (which is pointless but allows us to test it without Transloadit's servers), enable these: +#COMPANION_GOOGLE_KEYS_ENDPOINT=http://localhost:3020/drive/test-dynamic-oauth-credentials?secret=development +#COMPANION_TEST_DYNAMIC_OAUTH_CREDENTIALS=true +#COMPANION_TEST_DYNAMIC_OAUTH_CREDENTIALS_SECRET=development + + # Development environment # ======================= @@ -73,6 +79,9 @@ VITE_COMPANION_ALLOWED_HOSTS="/\.transloadit\.com\$/" VITE_TUS_ENDPOINT=https://tusd.tusdemo.net/files/ VITE_XHR_ENDPOINT=https://xhr-server.herokuapp.com/upload +# If you want to test dynamic Oauth +# VITE_COMPANION_GOOGLE_DRIVE_KEYS_PARAMS_CREDENTIALS_NAME=companion-google-drive + VITE_TRANSLOADIT_KEY=*** VITE_TRANSLOADIT_TEMPLATE=*** VITE_TRANSLOADIT_SERVICE_URL=https://api2.transloadit.com diff --git a/packages/@uppy/companion/src/companion.js b/packages/@uppy/companion/src/companion.js index a3d6235de9..c3deec6f92 100644 --- a/packages/@uppy/companion/src/companion.js +++ b/packages/@uppy/companion/src/companion.js @@ -136,6 +136,27 @@ module.exports.app = (optionsArg = {}) => { app.get('/:providerName/thumbnail/:id', middlewares.hasSessionAndProvider, middlewares.hasOAuthProvider, middlewares.cookieAuthToken, middlewares.verifyToken, controllers.thumbnail) + // Used for testing dynamic credentials only, normally this would run on a separate server. + if (options.testDynamicOauthCredentials) { + app.post('/:providerName/test-dynamic-oauth-credentials', (req, res) => { + if (req.query.secret !== options.testDynamicOauthCredentialsSecret) throw new Error('Invalid secret') + logger.info('Returning dynamic OAuth2 credentials') + const { providerName } = req.params + // for simplicity, we just return the normal credentials for the provider, but in a real-world scenario, + // we would query based on parameters + const { key, secret } = options.providerOptions[providerName] + res.send({ + credentials: { + key, + secret, + redirect_uri: providerManager.getGrantConfigForProvider({ + providerName, companionOptions: options, grantConfig, + })?.redirect_uri, + }, + }) + }) + } + app.param('providerName', providerManager.getProviderMiddleware(providers, grantConfig)) if (app.get('env') !== 'test') { diff --git a/packages/@uppy/companion/src/server/provider/index.js b/packages/@uppy/companion/src/server/provider/index.js index 02e2470eba..78ddee1ea2 100644 --- a/packages/@uppy/companion/src/server/provider/index.js +++ b/packages/@uppy/companion/src/server/provider/index.js @@ -36,6 +36,15 @@ const providerNameToAuthName = (name, options) => { // eslint-disable-line no-un return (providers[name] || {}).authProvider } +function getGrantConfigForProvider ({ providerName, companionOptions, grantConfig }) { + const authProvider = providerNameToAuthName(providerName, companionOptions) + + if (!isOAuthProvider(authProvider)) return undefined + return grantConfig[authProvider] +} + +module.exports.getGrantConfigForProvider = getGrantConfigForProvider + /** * adds the desired provider module to the request object, * based on the providerName parameter specified diff --git a/packages/@uppy/companion/src/standalone/helper.js b/packages/@uppy/companion/src/standalone/helper.js index 33129fcf4f..1a77119e16 100644 --- a/packages/@uppy/companion/src/standalone/helper.js +++ b/packages/@uppy/companion/src/standalone/helper.js @@ -176,6 +176,8 @@ const getConfigFromEnv = () => { metrics: process.env.COMPANION_HIDE_METRICS !== 'true', loggerProcessName: process.env.COMPANION_LOGGER_PROCESS_NAME, corsOrigins: getCorsOrigins(), + testDynamicOauthCredentials: process.env.COMPANION_TEST_DYNAMIC_OAUTH_CREDENTIALS === 'true', + testDynamicOauthCredentialsSecret: process.env.COMPANION_TEST_DYNAMIC_OAUTH_CREDENTIALS_SECRET, } } diff --git a/private/dev/Dashboard.js b/private/dev/Dashboard.js index 69f8ef83a6..d8f37ee8ff 100644 --- a/private/dev/Dashboard.js +++ b/private/dev/Dashboard.js @@ -16,6 +16,7 @@ import ImageEditor from '@uppy/image-editor' import DropTarget from '@uppy/drop-target' import Audio from '@uppy/audio' import Compressor from '@uppy/compressor' +import GoogleDrive from '@uppy/google-drive' /* eslint-enable import/no-extraneous-dependencies */ import generateSignatureIfSecret from './generateSignatureIfSecret.js' @@ -53,6 +54,25 @@ async function assemblyOptions () { }) } +function getCompanionKeysParams (name) { + const { + [`VITE_COMPANION_${name}_KEYS_PARAMS_CREDENTIALS_NAME`]: credentialsName, + [`VITE_COMPANION_${name}_KEYS_PARAMS_KEY`]: key, + } = import.meta.env + + if (credentialsName && key) { + // https://github.com/transloadit/uppy/pull/2802#issuecomment-1023093616 + return { + companionKeysParams: { + key, + credentialsName, + }, + } + } + + return {} +} + // Rest is implementation! Obviously edit as necessary... export default () => { @@ -80,7 +100,7 @@ export default () => { proudlyDisplayPoweredByUppy: true, note: `${JSON.stringify(restrictions)}`, }) - // .use(GoogleDrive, { target: Dashboard, companionUrl: COMPANION_URL, companionAllowedHosts }) + .use(GoogleDrive, { target: Dashboard, companionUrl: COMPANION_URL, companionAllowedHosts, ...getCompanionKeysParams('GOOGLE_DRIVE') }) // .use(Instagram, { target: Dashboard, companionUrl: COMPANION_URL, companionAllowedHosts }) // .use(Dropbox, { target: Dashboard, companionUrl: COMPANION_URL, companionAllowedHosts }) // .use(Box, { target: Dashboard, companionUrl: COMPANION_URL, companionAllowedHosts }) @@ -91,7 +111,7 @@ export default () => { // .use(Unsplash, { target: Dashboard, companionUrl: COMPANION_URL, companionAllowedHosts }) .use(RemoteSources, { companionUrl: COMPANION_URL, - sources: ['Box', 'Dropbox', 'Facebook', 'GoogleDrive', 'Instagram', 'OneDrive', 'Unsplash', 'Zoom', 'Url'], + sources: ['Box', 'Dropbox', 'Facebook', 'Instagram', 'OneDrive', 'Unsplash', 'Zoom', 'Url'], companionAllowedHosts, }) .use(Webcam, {