UNPKG

@uppy/companion

Version:

OAuth helper and remote fetcher for Uppy's (https://uppy.io) extensible file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Dropbox and Google Drive, S3 and more :dog:

165 lines (164 loc) 6.52 kB
/** * @module provider */ import { getRedirectPath, getURLBuilder } from '../helpers/utils.js'; import * as logger from '../logger.js'; import box from './box/index.js'; import { getCredentialsResolver } from './credentials.js'; import dropbox from './dropbox/index.js'; import facebook from './facebook/index.js'; import { Drive } from './google/drive/index.js'; import instagram from './instagram/graph/index.js'; import onedrive from './onedrive/index.js'; // biome-ignore lint/correctness/noUnusedImports: It's used as a type import Provider, { isOAuthProvider } from './Provider.js'; import unsplash from './unsplash/index.js'; import webdav from './webdav/index.js'; import zoom from './zoom/index.js'; /** * * @param {{server: object}} options */ const validOptions = (options) => { return options.server.host && options.server.protocol; }; /** * adds the desired provider module to the request object, * based on the providerName parameter specified * * @param {Record<string, typeof Provider>} providers */ export function getProviderMiddleware(providers, grantConfig) { /** * * @param {object} req * @param {object} res * @param {Function} next * @param {string} providerName */ const middleware = (req, res, next, providerName) => { const ProviderClass = providers[providerName]; if (ProviderClass && validOptions(req.companion.options)) { const { allowLocalUrls, providerOptions } = req.companion.options; const { oauthProvider } = ProviderClass; let providerGrantConfig; if (isOAuthProvider(oauthProvider)) { req.companion.getProviderCredentials = getCredentialsResolver(providerName, req.companion.options, req); providerGrantConfig = grantConfig[oauthProvider]; req.companion.providerGrantConfig = providerGrantConfig; } const secret = providerOptions[providerName]?.secret; req.companion.provider = new ProviderClass({ secret, providerName, providerGrantConfig, allowLocalUrls, }); req.companion.providerName = providerName; req.companion.providerClass = ProviderClass; } else { logger.warn('invalid provider options detected. Provider will not be loaded', 'provider.middleware.invalid', req.id); } next(); }; return middleware; } /** * @returns {Record<string, typeof Provider>} */ export function getDefaultProviders() { const providers = { dropbox, box, drive: Drive, facebook, onedrive, zoom, instagram, unsplash, webdav, }; return providers; } /** * * @typedef {{'module': typeof Provider, config: Record<string,unknown>}} CustomProvider * * @param {Record<string, CustomProvider>} customProviders * @param {Record<string, typeof Provider>} providers * @param {object} grantConfig */ export function addCustomProviders(customProviders, providers, grantConfig) { Object.keys(customProviders).forEach((providerName) => { const customProvider = customProviders[providerName]; providers[providerName] = customProvider.module; const { oauthProvider } = customProvider.module; if (isOAuthProvider(oauthProvider)) { grantConfig[oauthProvider] = { ...customProvider.config, // todo: consider setting these options from a universal point also used // by official providers. It'll prevent these from getting left out if the // requirement changes. callback: `/${providerName}/callback`, transport: 'session', }; } }); } /** * * @param {{server: object, providerOptions: object}} companionOptions * @param {object} grantConfig * @param {(a: string) => string} getOauthProvider */ export function addProviderOptions(companionOptions, grantConfig, getOauthProvider) { const { server, providerOptions } = companionOptions; if (!validOptions({ server })) { logger.warn('invalid provider options detected. Providers will not be loaded', 'provider.options.invalid'); return; } grantConfig.defaults = { host: server.host, protocol: server.protocol, path: server.path, }; const { oauthDomain } = server; const keys = Object.keys(providerOptions).filter((key) => key !== 'server'); keys.forEach((providerName) => { const oauthProvider = getOauthProvider?.(providerName); if (isOAuthProvider(oauthProvider) && grantConfig[oauthProvider]) { // explicitly add providerOptions so users don't override other providerOptions. grantConfig[oauthProvider].key = providerOptions[providerName].key; grantConfig[oauthProvider].secret = providerOptions[providerName].secret; if (providerOptions[providerName].credentialsURL) { grantConfig[oauthProvider].dynamic = [ 'key', 'secret', 'redirect_uri', 'origins', ]; } const provider = getDefaultProviders()[providerName]; Object.assign(grantConfig[oauthProvider], provider.getExtraGrantConfig()); // override grant.js redirect uri with companion's custom redirect url const isExternal = !!server.implicitPath; const redirectPath = getRedirectPath(providerName); grantConfig[oauthProvider].redirect_uri = getURLBuilder(companionOptions)(redirectPath, isExternal); if (oauthDomain) { const fullRedirectPath = getURLBuilder(companionOptions)(redirectPath, isExternal, true); grantConfig[oauthProvider].redirect_uri = `${server.protocol}://${oauthDomain}${fullRedirectPath}`; } if (server.implicitPath) { // no url builder is used for this because grant internally adds the path grantConfig[oauthProvider].callback = `${server.implicitPath}${grantConfig[oauthProvider].callback}`; } else if (server.path) { grantConfig[oauthProvider].callback = `${server.path}${grantConfig[oauthProvider].callback}`; } } }); }