index.js 5.8 KB

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