codekingpro/portable-devtools
115k
1const { webAuthOpener, adduserWeb, loginWeb, loginCouch, adduserCouch } = require('npm-profile')
2const { log } = require('proc-log')
3const { createOpener } = require('../utils/open-url.js')
4const read = require('../utils/read-user-info.js')
5
6const 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 { token: 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}
34
35const adduser = async (npm, { creds, ...opts }) => {
36 const authType = npm.config.get('auth-type')
37 let res
38 if (authType === 'web') {
39 try {
40 res = await adduserWeb(createOpener(npm, 'Create your account at'), opts)
41 } catch (err) {
42 if (err.code === 'ENYI') {
43 log.verbose('web add user not supported, trying couch')
44 } else {
45 throw err
46 }
47 }
48 }
49
50 // auth type !== web or ENYI error w/ web adduser
51 if (!res) {
52 const username = await read.username('Username:', creds.username)
53 const password = await read.password('Password:', creds.password)
54 const email = await read.email('Email (this will be public):', creds.email)
55 // npm registry quirk:
56 // If you "add" an existing user with their current password, it's effectively a login, and if that account has otp you'll be prompted for it.
57 res = await otplease(npm, opts, (reqOpts) => adduserCouch(username, email, password, reqOpts))
58 }
59
60 // We don't know the username if it was a web login, all we can reliably log is scope and registry
61 const message = `Logged in${opts.scope ? ` to scope ${opts.scope}` : ''} on ${opts.registry}.`
62
63 log.info('adduser', message)
64
65 return {
66 message,
67 newCreds: { token: res.token },
68 }
69}
70
71const login = async (npm, { creds, ...opts }) => {
72 const authType = npm.config.get('auth-type')
73 let res
74 if (authType === 'web') {
75 try {
76 res = await loginWeb(createOpener(npm, 'Login at'), opts)
77 } catch (err) {
78 if (err.code === 'ENYI') {
79 log.verbose('web login not supported, trying couch')
80 } else {
81 throw err
82 }
83 }
84 }
85
86 // auth type !== web or ENYI error w/ web login
87 if (!res) {
88 const username = await read.username('Username:', creds.username)
89 const password = await read.password('Password:', creds.password)
90 res = await otplease(npm, opts, (reqOpts) => loginCouch(username, password, reqOpts))
91 }
92
93 // We don't know the username if it was a web login, all we can reliably log is scope and registry
94 const message = `Logged in${opts.scope ? ` to scope ${opts.scope}` : ''} on ${opts.registry}.`
95
96 log.info('login', message)
97
98 return {
99 message,
100 newCreds: { token: res.token },
101 }
102}
103
104module.exports = {
105 adduser,
106 login,
107 otplease,
108}
109 