sso.js 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. // XXX: To date, npm Enterprise Legacy is the only system that ever
  2. // implemented support for this type of login. A better way to do
  3. // SSO is to use the WebLogin type of login supported by the npm-login
  4. // module. This more forward-looking login style is, ironically,
  5. // supported by the '--auth-type=legacy' type of login.
  6. // When and if npm Enterprise Legacy is no longer supported by the npm
  7. // CLI, we can remove this, and fold the lib/auth/legacy.js back into
  8. // lib/adduser.js
  9. const log = require('npmlog')
  10. const profile = require('npm-profile')
  11. const npmFetch = require('npm-registry-fetch')
  12. const openUrl = require('../utils/open-url.js')
  13. const otplease = require('../utils/otplease.js')
  14. const pollForSession = ({ registry, token, opts }) => {
  15. log.info('adduser', 'Polling for validated SSO session')
  16. return npmFetch.json(
  17. '/-/whoami', { ...opts, registry, forceAuth: { token } }
  18. ).then(
  19. ({ username }) => username,
  20. err => {
  21. if (err.code === 'E401') {
  22. return sleep(opts.ssoPollFrequency).then(() => {
  23. return pollForSession({ registry, token, opts })
  24. })
  25. } else
  26. throw err
  27. }
  28. )
  29. }
  30. function sleep (time) {
  31. return new Promise((resolve) => setTimeout(resolve, time))
  32. }
  33. const login = async (npm, { creds, registry, scope }) => {
  34. log.warn('deprecated', 'SSO --auth-type is deprecated')
  35. const opts = { ...npm.flatOptions, creds, registry, scope }
  36. const { ssoType } = opts
  37. if (!ssoType)
  38. throw new Error('Missing option: sso-type')
  39. // We're reusing the legacy login endpoint, so we need some dummy
  40. // stuff here to pass validation. They're never used.
  41. const auth = {
  42. username: 'npm_' + ssoType + '_auth_dummy_user',
  43. password: 'placeholder',
  44. email: 'support@npmjs.com',
  45. authType: ssoType,
  46. }
  47. const { token, sso } = await otplease(opts,
  48. opts => profile.loginCouch(auth.username, auth.password, opts)
  49. )
  50. if (!token)
  51. throw new Error('no SSO token returned')
  52. if (!sso)
  53. throw new Error('no SSO URL returned by services')
  54. await openUrl(npm, sso, 'to complete your login please visit')
  55. const username = await pollForSession({ registry, token, opts })
  56. log.info('adduser', `Authorized user ${username}`)
  57. const scopeMessage = scope ? ' to scope ' + scope : ''
  58. const message = `Logged in as ${username}${scopeMessage} on ${registry}.`
  59. return {
  60. message,
  61. newCreds: { token },
  62. }
  63. }
  64. module.exports = login