Skip to content

Commit 261ea19

Browse files
committed
fix: run input.start around help and openining urls
This also refactors auth and prompt related files to use promises and new npm-profile behavior
1 parent 4cbc2d4 commit 261ea19

31 files changed

+459
-571
lines changed

lib/commands/access.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const npa = require('npm-package-arg')
33
const { output } = require('proc-log')
44
const pkgJson = require('@npmcli/package-json')
55
const localeCompare = require('@isaacs/string-locale-compare')('en')
6-
const otplease = require('../utils/otplease.js')
6+
const { otplease } = require('../utils/auth.js')
77
const getIdentity = require('../utils/get-identity.js')
88
const BaseCommand = require('../base-cmd.js')
99

lib/commands/deprecate.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const fetch = require('npm-registry-fetch')
2-
const otplease = require('../utils/otplease.js')
2+
const { otplease } = require('../utils/auth.js')
33
const npa = require('npm-package-arg')
44
const semver = require('semver')
55
const getIdentity = require('../utils/get-identity.js')

lib/commands/dist-tag.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const npa = require('npm-package-arg')
22
const regFetch = require('npm-registry-fetch')
33
const semver = require('semver')
44
const { log, output } = require('proc-log')
5-
const otplease = require('../utils/otplease.js')
5+
const { otplease } = require('../utils/auth.js')
66
const pkgJson = require('@npmcli/package-json')
77
const BaseCommand = require('../base-cmd.js')
88

lib/commands/fund.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ const { output } = require('proc-log')
55
const npa = require('npm-package-arg')
66
const { depth } = require('treeverse')
77
const { readTree: getFundingInfo, normalizeFunding, isValidFunding } = require('libnpmfund')
8-
9-
const openUrl = require('../utils/open-url.js')
8+
const { openUrl } = require('../utils/open-url.js')
109
const ArboristWorkspaceCmd = require('../arborist-cmd.js')
1110

1211
const getPrintableName = ({ name, version }) => {

lib/commands/help.js

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
const spawn = require('@npmcli/promise-spawn')
22
const path = require('path')
3-
const openUrl = require('../utils/open-url.js')
3+
const { openUrl } = require('../utils/open-url.js')
44
const { glob } = require('glob')
5-
const { output } = require('proc-log')
5+
const { output, input } = require('proc-log')
66
const localeCompare = require('@isaacs/string-locale-compare')('en')
77
const { deref } = require('../utils/cmd-list.js')
88
const BaseCommand = require('../base-cmd.js')
@@ -95,13 +95,15 @@ class Help extends BaseCommand {
9595
args = ['emacsclient', ['-e', `(woman-find-file '${man}')`]]
9696
}
9797

98-
return spawn(...args, { stdio: 'inherit' }).catch(err => {
98+
try {
99+
await input.start(() => spawn(...args, { stdio: 'inherit' }))
100+
} catch (err) {
99101
if (err.code) {
100102
throw new Error(`help process exited with code: ${err.code}`)
101103
} else {
102104
throw err
103105
}
104-
})
106+
}
105107
}
106108

107109
// Returns the path to the html version of the man page

lib/commands/hook.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const hookApi = require('libnpmhook')
2-
const otplease = require('../utils/otplease.js')
2+
const { otplease } = require('../utils/auth.js')
33
const relativeDate = require('tiny-relative-date')
44
const { output } = require('proc-log')
55
const BaseCommand = require('../base-cmd.js')

lib/commands/org.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const liborg = require('libnpmorg')
2-
const otplease = require('../utils/otplease.js')
2+
const { otplease } = require('../utils/auth.js')
33
const BaseCommand = require('../base-cmd.js')
44
const { output } = require('proc-log')
55

lib/commands/owner.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const npa = require('npm-package-arg')
22
const npmFetch = require('npm-registry-fetch')
33
const pacote = require('pacote')
44
const { log, output } = require('proc-log')
5-
const otplease = require('../utils/otplease.js')
5+
const { otplease } = require('../utils/auth.js')
66
const pkgJson = require('@npmcli/package-json')
77
const BaseCommand = require('../base-cmd.js')
88
const { redact } = require('@npmcli/redact')

lib/commands/profile.js

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
const { inspect } = require('util')
22
const { URL } = require('url')
33
const { log, output } = require('proc-log')
4-
const npmProfile = require('npm-profile')
4+
const { get, set, createToken } = require('npm-profile')
55
const qrcodeTerminal = require('qrcode-terminal')
6-
const otplease = require('../utils/otplease.js')
6+
const { otplease } = require('../utils/auth.js')
77
const readUserInfo = require('../utils/read-user-info.js')
88
const BaseCommand = require('../base-cmd.js')
99

@@ -101,7 +101,7 @@ class Profile extends BaseCommand {
101101

102102
async get (args) {
103103
const tfa = 'two-factor auth'
104-
const info = await npmProfile.get({ ...this.npm.flatOptions })
104+
const info = await get({ ...this.npm.flatOptions })
105105

106106
if (!info.cidr_whitelist) {
107107
delete info.cidr_whitelist
@@ -199,7 +199,7 @@ class Profile extends BaseCommand {
199199
}
200200

201201
// FIXME: Work around to not clear everything other than what we're setting
202-
const user = await npmProfile.get(conf)
202+
const user = await get(conf)
203203
const newUser = {}
204204

205205
for (const key of writableProfileKeys) {
@@ -208,7 +208,7 @@ class Profile extends BaseCommand {
208208

209209
newUser[prop] = value
210210

211-
const result = await otplease(this.npm, conf, c => npmProfile.set(newUser, c))
211+
const result = await otplease(this.npm, conf, c => set(newUser, c))
212212

213213
if (this.npm.config.get('json')) {
214214
output.standard(JSON.stringify({ [prop]: result[prop] }, null, 2))
@@ -273,7 +273,7 @@ class Profile extends BaseCommand {
273273

274274
if (auth.basic) {
275275
log.info('profile', 'Updating authentication to bearer token')
276-
const result = await npmProfile.createToken(
276+
const result = await createToken(
277277
auth.basic.password, false, [], { ...this.npm.flatOptions }
278278
)
279279

@@ -297,12 +297,12 @@ class Profile extends BaseCommand {
297297
info.tfa.password = password
298298

299299
log.info('profile', 'Determine if tfa is pending')
300-
const userInfo = await npmProfile.get({ ...this.npm.flatOptions })
300+
const userInfo = await get({ ...this.npm.flatOptions })
301301

302302
const conf = { ...this.npm.flatOptions }
303303
if (userInfo && userInfo.tfa && userInfo.tfa.pending) {
304304
log.info('profile', 'Resetting two-factor authentication')
305-
await npmProfile.set({ tfa: { password, mode: 'disable' } }, conf)
305+
await set({ tfa: { password, mode: 'disable' } }, conf)
306306
} else if (userInfo && userInfo.tfa) {
307307
if (!conf.otp) {
308308
conf.otp = await readUserInfo.otp(
@@ -312,7 +312,7 @@ class Profile extends BaseCommand {
312312
}
313313

314314
log.info('profile', 'Setting two-factor authentication to ' + mode)
315-
const challenge = await npmProfile.set(info, conf)
315+
const challenge = await set(info, conf)
316316

317317
if (challenge.tfa === null) {
318318
output.standard('Two factor authentication mode changed to: ' + mode)
@@ -341,7 +341,7 @@ class Profile extends BaseCommand {
341341

342342
log.info('profile', 'Finalizing two-factor authentication')
343343

344-
const result = await npmProfile.set({ tfa: [interactiveOTP] }, conf)
344+
const result = await set({ tfa: [interactiveOTP] }, conf)
345345

346346
output.standard(
347347
'2FA successfully enabled. Below are your recovery codes, ' +
@@ -359,7 +359,7 @@ class Profile extends BaseCommand {
359359

360360
async disable2fa () {
361361
const conf = { ...this.npm.flatOptions }
362-
const info = await npmProfile.get(conf)
362+
const info = await get(conf)
363363

364364
if (!info.tfa || info.tfa.pending) {
365365
output.standard('Two factor authentication not enabled.')
@@ -375,7 +375,7 @@ class Profile extends BaseCommand {
375375

376376
log.info('profile', 'disabling tfa')
377377

378-
await npmProfile.set({ tfa: { password: password, mode: 'disable' } }, conf)
378+
await set({ tfa: { password: password, mode: 'disable' } }, conf)
379379

380380
if (this.npm.config.get('json')) {
381381
output.standard(JSON.stringify({ tfa: false }, null, 2))

lib/commands/publish.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const pacote = require('pacote')
77
const npa = require('npm-package-arg')
88
const npmFetch = require('npm-registry-fetch')
99
const { redactLog: replaceInfo } = require('@npmcli/redact')
10-
const otplease = require('../utils/otplease.js')
10+
const { otplease } = require('../utils/auth.js')
1111
const { getContents, logTar } = require('../utils/tar.js')
1212
// for historical reasons, publishConfig in package.json can contain ANY config
1313
// keys that npm supports in .npmrc files and elsewhere. We *may* want to

lib/commands/team.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const columns = require('cli-columns')
22
const libteam = require('libnpmteam')
33
const { output } = require('proc-log')
4-
const otplease = require('../utils/otplease.js')
4+
const { otplease } = require('../utils/auth.js')
55

66
const BaseCommand = require('../base-cmd.js')
77
class Team extends BaseCommand {

lib/commands/token.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const { log, output } = require('proc-log')
2-
const profile = require('npm-profile')
3-
const otplease = require('../utils/otplease.js')
2+
const { listTokens, createToken, removeToken } = require('npm-profile')
3+
const { otplease } = require('../utils/auth.js')
44
const readUserInfo = require('../utils/read-user-info.js')
55
const BaseCommand = require('../base-cmd.js')
66

@@ -48,7 +48,7 @@ class Token extends BaseCommand {
4848
const json = this.npm.config.get('json')
4949
const parseable = this.npm.config.get('parseable')
5050
log.info('token', 'getting list')
51-
const tokens = await profile.listTokens(this.npm.flatOptions)
51+
const tokens = await listTokens(this.npm.flatOptions)
5252
if (json) {
5353
output.standard(JSON.stringify(tokens, null, 2))
5454
return
@@ -92,7 +92,7 @@ class Token extends BaseCommand {
9292
const toRemove = []
9393
const opts = { ...this.npm.flatOptions }
9494
log.info('token', `removing ${toRemove.length} tokens`)
95-
const tokens = await profile.listTokens(opts)
95+
const tokens = await listTokens(opts)
9696
args.forEach(id => {
9797
const matches = tokens.filter(token => token.key.indexOf(id) === 0)
9898
if (matches.length === 1) {
@@ -113,7 +113,7 @@ class Token extends BaseCommand {
113113
})
114114
await Promise.all(
115115
toRemove.map(key => {
116-
return otplease(this.npm, opts, c => profile.removeToken(key, c))
116+
return otplease(this.npm, opts, c => removeToken(key, c))
117117
})
118118
)
119119
if (json) {
@@ -137,7 +137,7 @@ class Token extends BaseCommand {
137137
const result = await otplease(
138138
this.npm,
139139
{ ...this.npm.flatOptions },
140-
c => profile.createToken(password, readonly, validCIDR, c)
140+
c => createToken(password, readonly, validCIDR, c)
141141
)
142142
delete result.key
143143
delete result.updated

lib/commands/unpublish.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const { output, log } = require('proc-log')
66
const pkgJson = require('@npmcli/package-json')
77
const { flatten } = require('@npmcli/config/lib/definitions')
88
const getIdentity = require('../utils/get-identity.js')
9-
const otplease = require('../utils/otplease.js')
9+
const { otplease } = require('../utils/auth.js')
1010
const BaseCommand = require('../base-cmd.js')
1111

1212
const LAST_REMAINING_VERSION_ERROR = 'Refusing to delete the last version of the package. ' +

lib/package-url-cmd.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const pacote = require('pacote')
2-
const openUrl = require('./utils/open-url.js')
2+
const { openUrl } = require('./utils/open-url.js')
33
const { log } = require('proc-log')
44
const BaseCommand = require('./base-cmd.js')
55

lib/utils/auth.js

+36-27
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,43 @@
1-
const profile = require('npm-profile')
1+
const { webAuthOpener, adduserWeb, loginWeb, loginCouch, adduserCouch } = require('npm-profile')
22
const { log } = require('proc-log')
3-
const openUrlPrompt = require('../utils/open-url-prompt.js')
3+
const { createOpener } = require('../utils/open-url.js')
44
const read = require('../utils/read-user-info.js')
5-
const otplease = require('../utils/otplease.js')
5+
6+
const otplease = async (npm, opts, fn) => {
7+
try {
8+
return await fn(opts)
9+
} catch (err) {
10+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
11+
throw err
12+
}
13+
14+
// web otp
15+
if (err.code === 'EOTP' && err.body?.authUrl && err.body?.doneUrl) {
16+
const otp = await webAuthOpener(
17+
createOpener(npm, 'Authenticate your account at'),
18+
err.body.authUrl,
19+
err.body.doneUrl,
20+
opts
21+
)
22+
return await fn({ ...opts, otp })
23+
}
24+
25+
// classic otp
26+
if (err.code === 'EOTP' || (err.code === 'E401' && /one-time pass/.test(err.body))) {
27+
const otp = await read.otp('This operation requires a one-time password.\nEnter OTP:')
28+
return await fn({ ...opts, otp })
29+
}
30+
31+
throw err
32+
}
33+
}
634

735
const adduser = async (npm, { creds, ...opts }) => {
836
const authType = npm.config.get('auth-type')
937
let res
1038
if (authType === 'web') {
1139
try {
12-
res = await profile.adduserWeb((url, emitter) => {
13-
openUrlPrompt(
14-
npm,
15-
url,
16-
'Create your account at',
17-
'Press ENTER to open in the browser...',
18-
emitter
19-
)
20-
}, opts)
40+
res = await adduserWeb(createOpener(npm, 'Create your account at'), opts)
2141
} catch (err) {
2242
if (err.code === 'ENYI') {
2343
log.verbose('web add user not supported, trying couch')
@@ -35,9 +55,7 @@ const adduser = async (npm, { creds, ...opts }) => {
3555
// npm registry quirk: If you "add" an existing user with their current
3656
// password, it's effectively a login, and if that account has otp you'll
3757
// be prompted for it.
38-
res = await otplease(npm, opts, (reqOpts) =>
39-
profile.adduserCouch(username, email, password, reqOpts)
40-
)
58+
res = await otplease(npm, opts, (reqOpts) => adduserCouch(username, email, password, reqOpts))
4159
}
4260

4361
// We don't know the username if it was a web login, all we can reliably log is scope and registry
@@ -56,15 +74,7 @@ const login = async (npm, { creds, ...opts }) => {
5674
let res
5775
if (authType === 'web') {
5876
try {
59-
res = await profile.loginWeb((url, emitter) => {
60-
openUrlPrompt(
61-
npm,
62-
url,
63-
'Login at',
64-
'Press ENTER to open in the browser...',
65-
emitter
66-
)
67-
}, opts)
77+
res = await loginWeb(createOpener(npm, 'Login at'), opts)
6878
} catch (err) {
6979
if (err.code === 'ENYI') {
7080
log.verbose('web login not supported, trying couch')
@@ -78,9 +88,7 @@ const login = async (npm, { creds, ...opts }) => {
7888
if (!res) {
7989
const username = await read.username('Username:', creds.username)
8090
const password = await read.password('Password:', creds.password)
81-
res = await otplease(npm, opts, (reqOpts) =>
82-
profile.loginCouch(username, password, reqOpts)
83-
)
91+
res = await otplease(npm, opts, (reqOpts) => loginCouch(username, password, reqOpts))
8492
}
8593

8694
// We don't know the username if it was a web login, all we can reliably log is scope and registry
@@ -97,4 +105,5 @@ const login = async (npm, { creds, ...opts }) => {
97105
module.exports = {
98106
adduser,
99107
login,
108+
otplease,
100109
}

0 commit comments

Comments
 (0)