auth.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. const { webAuthOpener, adduserWeb, loginWeb, loginCouch, adduserCouch } = require('npm-profile')
  2. const { log } = require('proc-log')
  3. const { createOpener } = require('../utils/open-url.js')
  4. const read = require('../utils/read-user-info.js')
  5. const otplease = async (npm, opts, fn) => {
  6. try {
  7. return await fn(opts)
  8. } catch (err) {
  9. if (!process.stdin.isTTY || !process.stdout.isTTY) {
  10. throw err
  11. }
  12. // web otp
  13. if (err.code === 'EOTP' && err.body?.authUrl && err.body?.doneUrl) {
  14. const { token: otp } = await webAuthOpener(
  15. createOpener(npm, 'Authenticate your account at'),
  16. err.body.authUrl,
  17. err.body.doneUrl,
  18. opts
  19. )
  20. return await fn({ ...opts, otp })
  21. }
  22. // classic otp
  23. if (err.code === 'EOTP' || (err.code === 'E401' && /one-time pass/.test(err.body))) {
  24. const otp = await read.otp('This operation requires a one-time password.\nEnter OTP:')
  25. return await fn({ ...opts, otp })
  26. }
  27. throw err
  28. }
  29. }
  30. const adduser = async (npm, { creds, ...opts }) => {
  31. const authType = npm.config.get('auth-type')
  32. let res
  33. if (authType === 'web') {
  34. try {
  35. res = await adduserWeb(createOpener(npm, 'Create your account at'), opts)
  36. } catch (err) {
  37. if (err.code === 'ENYI') {
  38. log.verbose('web add user not supported, trying couch')
  39. } else {
  40. throw err
  41. }
  42. }
  43. }
  44. // auth type !== web or ENYI error w/ web adduser
  45. if (!res) {
  46. const username = await read.username('Username:', creds.username)
  47. const password = await read.password('Password:', creds.password)
  48. const email = await read.email('Email (this will be public):', creds.email)
  49. // npm registry quirk:
  50. // 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.
  51. res = await otplease(npm, opts, (reqOpts) => adduserCouch(username, email, password, reqOpts))
  52. }
  53. // We don't know the username if it was a web login, all we can reliably log is scope and registry
  54. const message = `Logged in${opts.scope ? ` to scope ${opts.scope}` : ''} on ${opts.registry}.`
  55. log.info('adduser', message)
  56. return {
  57. message,
  58. newCreds: { token: res.token },
  59. }
  60. }
  61. const login = async (npm, { creds, ...opts }) => {
  62. const authType = npm.config.get('auth-type')
  63. let res
  64. if (authType === 'web') {
  65. try {
  66. res = await loginWeb(createOpener(npm, 'Login at'), opts)
  67. } catch (err) {
  68. if (err.code === 'ENYI') {
  69. log.verbose('web login not supported, trying couch')
  70. } else {
  71. throw err
  72. }
  73. }
  74. }
  75. // auth type !== web or ENYI error w/ web login
  76. if (!res) {
  77. const username = await read.username('Username:', creds.username)
  78. const password = await read.password('Password:', creds.password)
  79. res = await otplease(npm, opts, (reqOpts) => loginCouch(username, password, reqOpts))
  80. }
  81. // We don't know the username if it was a web login, all we can reliably log is scope and registry
  82. const message = `Logged in${opts.scope ? ` to scope ${opts.scope}` : ''} on ${opts.registry}.`
  83. log.info('login', message)
  84. return {
  85. message,
  86. newCreds: { token: res.token },
  87. }
  88. }
  89. module.exports = {
  90. adduser,
  91. login,
  92. otplease,
  93. }