auth.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. 'use strict'
  2. const npa = require('npm-package-arg')
  3. // Find the longest registry key that is used for some kind of auth
  4. // in the options.
  5. const regKeyFromURI = (uri, opts) => {
  6. const parsed = new URL(uri)
  7. // try to find a config key indicating we have auth for this registry
  8. // can be one of :_authToken, :_auth, or :_password and :username
  9. // We walk up the "path" until we're left with just //<host>[:<port>],
  10. // stopping when we reach '//'.
  11. let regKey = `//${parsed.host}${parsed.pathname}`
  12. while (regKey.length > '//'.length) {
  13. // got some auth for this URI
  14. if (hasAuth(regKey, opts))
  15. return regKey
  16. // can be either //host/some/path/:_auth or //host/some/path:_auth
  17. // walk up by removing EITHER what's after the slash OR the slash itself
  18. regKey = regKey.replace(/([^/]+|\/)$/, '')
  19. }
  20. }
  21. const hasAuth = (regKey, opts) => (
  22. opts[`${regKey}:_authToken`] ||
  23. opts[`${regKey}:_auth`] ||
  24. opts[`${regKey}:username`] && opts[`${regKey}:_password`]
  25. )
  26. const sameHost = (a, b) => {
  27. const parsedA = new URL(a)
  28. const parsedB = new URL(b)
  29. return parsedA.host === parsedB.host
  30. }
  31. const getRegistry = opts => {
  32. const { spec } = opts
  33. const { scope: specScope, subSpec } = spec ? npa(spec) : {}
  34. const subSpecScope = subSpec && subSpec.scope
  35. const scope = subSpec ? subSpecScope : specScope
  36. const scopeReg = scope && opts[`${scope}:registry`]
  37. return scopeReg || opts.registry
  38. }
  39. const getAuth = (uri, opts = {}) => {
  40. const { forceAuth } = opts
  41. if (!uri)
  42. throw new Error('URI is required')
  43. const regKey = regKeyFromURI(uri, forceAuth || opts)
  44. // we are only allowed to use what's in forceAuth if specified
  45. if (forceAuth && !regKey) {
  46. return new Auth({
  47. scopeAuthKey: null,
  48. token: forceAuth._authToken || forceAuth.token,
  49. username: forceAuth.username,
  50. password: forceAuth._password || forceAuth.password,
  51. auth: forceAuth._auth || forceAuth.auth,
  52. })
  53. }
  54. // no auth for this URI, but might have it for the registry
  55. if (!regKey) {
  56. const registry = getRegistry(opts)
  57. if (registry && uri !== registry && sameHost(uri, registry))
  58. return getAuth(registry, opts)
  59. else if (registry !== opts.registry) {
  60. // If making a tarball request to a different base URI than the
  61. // registry where we logged in, but the same auth SHOULD be sent
  62. // to that artifact host, then we track where it was coming in from,
  63. // and warn the user if we get a 4xx error on it.
  64. const scopeAuthKey = regKeyFromURI(registry, opts)
  65. return new Auth({ scopeAuthKey })
  66. }
  67. }
  68. const {
  69. [`${regKey}:_authToken`]: token,
  70. [`${regKey}:username`]: username,
  71. [`${regKey}:_password`]: password,
  72. [`${regKey}:_auth`]: auth,
  73. } = opts
  74. return new Auth({
  75. scopeAuthKey: null,
  76. token,
  77. auth,
  78. username,
  79. password,
  80. })
  81. }
  82. class Auth {
  83. constructor ({ token, auth, username, password, scopeAuthKey }) {
  84. this.scopeAuthKey = scopeAuthKey
  85. this.token = null
  86. this.auth = null
  87. this.isBasicAuth = false
  88. if (token)
  89. this.token = token
  90. else if (auth)
  91. this.auth = auth
  92. else if (username && password) {
  93. const p = Buffer.from(password, 'base64').toString('utf8')
  94. this.auth = Buffer.from(`${username}:${p}`, 'utf8').toString('base64')
  95. this.isBasicAuth = true
  96. }
  97. }
  98. }
  99. module.exports = getAuth