'use strict' const npa = require('npm-package-arg') // Find the longest registry key that is used for some kind of auth // in the options. const regKeyFromURI = (uri, opts) => { const parsed = new URL(uri) // try to find a config key indicating we have auth for this registry // can be one of :_authToken, :_auth, or :_password and :username // We walk up the "path" until we're left with just //[:], // stopping when we reach '//'. let regKey = `//${parsed.host}${parsed.pathname}` while (regKey.length > '//'.length) { // got some auth for this URI if (hasAuth(regKey, opts)) return regKey // can be either //host/some/path/:_auth or //host/some/path:_auth // walk up by removing EITHER what's after the slash OR the slash itself regKey = regKey.replace(/([^/]+|\/)$/, '') } } const hasAuth = (regKey, opts) => ( opts[`${regKey}:_authToken`] || opts[`${regKey}:_auth`] || opts[`${regKey}:username`] && opts[`${regKey}:_password`] ) const sameHost = (a, b) => { const parsedA = new URL(a) const parsedB = new URL(b) return parsedA.host === parsedB.host } const getRegistry = opts => { const { spec } = opts const { scope: specScope, subSpec } = spec ? npa(spec) : {} const subSpecScope = subSpec && subSpec.scope const scope = subSpec ? subSpecScope : specScope const scopeReg = scope && opts[`${scope}:registry`] return scopeReg || opts.registry } const getAuth = (uri, opts = {}) => { const { forceAuth } = opts if (!uri) throw new Error('URI is required') const regKey = regKeyFromURI(uri, forceAuth || opts) // we are only allowed to use what's in forceAuth if specified if (forceAuth && !regKey) { return new Auth({ scopeAuthKey: null, token: forceAuth._authToken || forceAuth.token, username: forceAuth.username, password: forceAuth._password || forceAuth.password, auth: forceAuth._auth || forceAuth.auth, }) } // no auth for this URI, but might have it for the registry if (!regKey) { const registry = getRegistry(opts) if (registry && uri !== registry && sameHost(uri, registry)) return getAuth(registry, opts) else if (registry !== opts.registry) { // If making a tarball request to a different base URI than the // registry where we logged in, but the same auth SHOULD be sent // to that artifact host, then we track where it was coming in from, // and warn the user if we get a 4xx error on it. const scopeAuthKey = regKeyFromURI(registry, opts) return new Auth({ scopeAuthKey }) } } const { [`${regKey}:_authToken`]: token, [`${regKey}:username`]: username, [`${regKey}:_password`]: password, [`${regKey}:_auth`]: auth, } = opts return new Auth({ scopeAuthKey: null, token, auth, username, password, }) } class Auth { constructor ({ token, auth, username, password, scopeAuthKey }) { this.scopeAuthKey = scopeAuthKey this.token = null this.auth = null this.isBasicAuth = false if (token) this.token = token else if (auth) this.auth = auth else if (username && password) { const p = Buffer.from(password, 'base64').toString('utf8') this.auth = Buffer.from(`${username}:${p}`, 'utf8').toString('base64') this.isBasicAuth = true } } } module.exports = getAuth