index.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /**
  2. * @module provider
  3. */
  4. const dropbox = require('./dropbox')
  5. const box = require('./box')
  6. const drive = require('./google/drive')
  7. const googlephotos = require('./google/googlephotos')
  8. const instagram = require('./instagram/graph')
  9. const facebook = require('./facebook')
  10. const onedrive = require('./onedrive')
  11. const unsplash = require('./unsplash')
  12. const zoom = require('./zoom')
  13. const { getURLBuilder } = require('../helpers/utils')
  14. const logger = require('../logger')
  15. const { getCredentialsResolver } = require('./credentials')
  16. const Provider = require('./Provider')
  17. const { isOAuthProvider } = Provider
  18. /**
  19. *
  20. * @param {{server: object}} options
  21. */
  22. const validOptions = (options) => {
  23. return options.server.host && options.server.protocol
  24. }
  25. /**
  26. * adds the desired provider module to the request object,
  27. * based on the providerName parameter specified
  28. *
  29. * @param {Record<string, typeof Provider>} providers
  30. */
  31. module.exports.getProviderMiddleware = (providers, grantConfig) => {
  32. /**
  33. *
  34. * @param {object} req
  35. * @param {object} res
  36. * @param {Function} next
  37. * @param {string} providerName
  38. */
  39. const middleware = (req, res, next, providerName) => {
  40. const ProviderClass = providers[providerName]
  41. if (ProviderClass && validOptions(req.companion.options)) {
  42. const { allowLocalUrls } = req.companion.options
  43. const { oauthProvider } = ProviderClass
  44. let providerGrantConfig
  45. if (isOAuthProvider(oauthProvider)) {
  46. req.companion.getProviderCredentials = getCredentialsResolver(providerName, req.companion.options, req)
  47. providerGrantConfig = grantConfig[oauthProvider]
  48. req.companion.providerGrantConfig = providerGrantConfig
  49. }
  50. req.companion.provider = new ProviderClass({ providerName, providerGrantConfig, allowLocalUrls })
  51. req.companion.providerClass = ProviderClass
  52. } else {
  53. logger.warn('invalid provider options detected. Provider will not be loaded', 'provider.middleware.invalid', req.id)
  54. }
  55. next()
  56. }
  57. return middleware
  58. }
  59. /**
  60. * @returns {Record<string, typeof Provider>}
  61. */
  62. module.exports.getDefaultProviders = () => {
  63. const providers = { dropbox, box, drive, googlephotos, facebook, onedrive, zoom, instagram, unsplash }
  64. return providers
  65. }
  66. /**
  67. *
  68. * @typedef {{'module': typeof Provider, config: Record<string,unknown>}} CustomProvider
  69. *
  70. * @param {Record<string, CustomProvider>} customProviders
  71. * @param {Record<string, typeof Provider>} providers
  72. * @param {object} grantConfig
  73. */
  74. module.exports.addCustomProviders = (customProviders, providers, grantConfig) => {
  75. Object.keys(customProviders).forEach((providerName) => {
  76. const customProvider = customProviders[providerName]
  77. // eslint-disable-next-line no-param-reassign
  78. providers[providerName] = customProvider.module
  79. const { oauthProvider } = customProvider.module
  80. if (isOAuthProvider(oauthProvider)) {
  81. // eslint-disable-next-line no-param-reassign
  82. grantConfig[oauthProvider] = {
  83. ...customProvider.config,
  84. // todo: consider setting these options from a universal point also used
  85. // by official providers. It'll prevent these from getting left out if the
  86. // requirement changes.
  87. callback: `/${providerName}/callback`,
  88. transport: 'session',
  89. }
  90. }
  91. })
  92. }
  93. /**
  94. *
  95. * @param {{server: object, providerOptions: object}} companionOptions
  96. * @param {object} grantConfig
  97. * @param {(a: string) => string} getOauthProvider
  98. */
  99. module.exports.addProviderOptions = (companionOptions, grantConfig, getOauthProvider) => {
  100. const { server, providerOptions } = companionOptions
  101. if (!validOptions({ server })) {
  102. logger.warn('invalid provider options detected. Providers will not be loaded', 'provider.options.invalid')
  103. return
  104. }
  105. // eslint-disable-next-line no-param-reassign
  106. grantConfig.defaults = {
  107. host: server.host,
  108. protocol: server.protocol,
  109. path: server.path,
  110. }
  111. const { oauthDomain } = server
  112. const keys = Object.keys(providerOptions).filter((key) => key !== 'server')
  113. keys.forEach((providerName) => {
  114. const oauthProvider = getOauthProvider?.(providerName)
  115. if (isOAuthProvider(oauthProvider) && grantConfig[oauthProvider]) {
  116. // explicitly add providerOptions so users don't override other providerOptions.
  117. // eslint-disable-next-line no-param-reassign
  118. grantConfig[oauthProvider].key = providerOptions[providerName].key
  119. // eslint-disable-next-line no-param-reassign
  120. grantConfig[oauthProvider].secret = providerOptions[providerName].secret
  121. if (providerOptions[providerName].credentialsURL) {
  122. // eslint-disable-next-line no-param-reassign
  123. grantConfig[oauthProvider].dynamic = ['key', 'secret', 'redirect_uri']
  124. }
  125. const provider = exports.getDefaultProviders()[providerName]
  126. Object.assign(grantConfig[oauthProvider], provider.getExtraGrantConfig())
  127. // override grant.js redirect uri with companion's custom redirect url
  128. const isExternal = !!server.implicitPath
  129. const redirectPath = `/${providerName}/redirect`
  130. // eslint-disable-next-line no-param-reassign
  131. grantConfig[oauthProvider].redirect_uri = getURLBuilder(companionOptions)(redirectPath, isExternal)
  132. if (oauthDomain) {
  133. const fullRedirectPath = getURLBuilder(companionOptions)(redirectPath, isExternal, true)
  134. // eslint-disable-next-line no-param-reassign
  135. grantConfig[oauthProvider].redirect_uri = `${server.protocol}://${oauthDomain}${fullRedirectPath}`
  136. }
  137. if (server.implicitPath) {
  138. // no url builder is used for this because grant internally adds the path
  139. // eslint-disable-next-line no-param-reassign
  140. grantConfig[oauthProvider].callback = `${server.implicitPath}${grantConfig[oauthProvider].callback}`
  141. } else if (server.path) {
  142. // eslint-disable-next-line no-param-reassign
  143. grantConfig[oauthProvider].callback = `${server.path}${grantConfig[oauthProvider].callback}`
  144. }
  145. }
  146. })
  147. }