diff --git a/lib/utils/otplease.js b/lib/utils/otplease.js index 790f74bd9704f..62a779afc7f1d 100644 --- a/lib/utils/otplease.js +++ b/lib/utils/otplease.js @@ -8,6 +8,23 @@ async function otplease (npm, opts, fn) { const isBackwardsCompatibleOTP = err.code === 'E401' && /one-time pass/.test(err.body) const isClassicOTP = err.code === 'EOTP' + const isWebOTP = err.code === 'EOTP' && err.body.authUrl && err.body.doneUrl + + if (isWebOTP) { + const webAuth = require('./web-auth') + const openUrlPrompt = require('./open-url-prompt') + + const openerPromise = (url, emitter) => + openUrlPrompt( + npm, + url, + 'Authenticate your account at', + 'Press ENTER to open in the browser...', + emitter + ) + const otp = await webAuth(openerPromise, err.body.authUrl, err.body.doneUrl, opts) + return await fn({ ...opts, otp }) + } if (isClassicOTP || isBackwardsCompatibleOTP) { const readUserInfo = require('./read-user-info.js') diff --git a/lib/utils/web-auth.js b/lib/utils/web-auth.js new file mode 100644 index 0000000000000..1a7e227de2191 --- /dev/null +++ b/lib/utils/web-auth.js @@ -0,0 +1,22 @@ +const EventEmitter = require('events') +const { webAuthCheckLogin } = require('npm-profile') + +function webAuth (opener, initialUrl, doneUrl, opts) { + const doneEmitter = new EventEmitter() + + const openPromise = opener(initialUrl, doneEmitter) + const webAuthCheckPromise = webAuthCheckLogin(doneUrl, { ...opts, cache: false }) + .then(authResult => { + // cancel open prompt if it's present + doneEmitter.emit('abort') + + return authResult + }) + + return Promise.all([openPromise, webAuthCheckPromise]).then( + // pick the auth result and pass it along + ([, authResult]) => authResult + ) +} + +module.exports = webAuth