1'use strict' 2 3const BB = require('bluebird') 4 5const figgyPudding = require('figgy-pudding') 6const log = require('npmlog') 7const npmConfig = require('../config/figgy-config.js') 8const npmFetch = require('npm-registry-fetch') 9const output = require('../utils/output.js') 10const openUrl = BB.promisify(require('../utils/open-url.js')) 11const otplease = require('../utils/otplease.js') 12const profile = require('libnpm/profile') 13 14const SsoOpts = figgyPudding({ 15 ssoType: 'sso-type', 16 'sso-type': {}, 17 ssoPollFrequency: 'sso-poll-frequency', 18 'sso-poll-frequency': {} 19}) 20 21module.exports.login = function login (creds, registry, scope, cb) { 22 const opts = SsoOpts(npmConfig()).concat({creds, registry, scope}) 23 const ssoType = opts.ssoType 24 if (!ssoType) { return cb(new Error('Missing option: sso-type')) } 25 26 // We're reusing the legacy login endpoint, so we need some dummy 27 // stuff here to pass validation. They're never used. 28 const auth = { 29 username: 'npm_' + ssoType + '_auth_dummy_user', 30 password: 'placeholder', 31 email: 'support@npmjs.com', 32 authType: ssoType 33 } 34 35 otplease(opts, 36 opts => profile.loginCouch(auth.username, auth.password, opts) 37 ).then(({token, sso}) => { 38 if (!token) { throw new Error('no SSO token returned') } 39 if (!sso) { throw new Error('no SSO URL returned by services') } 40 return openUrl(sso, 'to complete your login please visit').then(() => { 41 return pollForSession(registry, token, opts) 42 }).then(username => { 43 log.info('adduser', 'Authorized user %s', username) 44 var scopeMessage = scope ? ' to scope ' + scope : '' 45 output('Logged in as %s%s on %s.', username, scopeMessage, registry) 46 return {token} 47 }) 48 }).nodeify(cb) 49} 50 51function pollForSession (registry, token, opts) { 52 log.info('adduser', 'Polling for validated SSO session') 53 return npmFetch.json( 54 '/-/whoami', opts.concat({registry, forceAuth: {token}}) 55 ).then( 56 ({username}) => username, 57 err => { 58 if (err.code === 'E401') { 59 return sleep(opts['sso-poll-frequency']).then(() => { 60 return pollForSession(registry, token, opts) 61 }) 62 } else { 63 throw err 64 } 65 } 66 ) 67} 68 69function sleep (time) { 70 return new BB((resolve) => { 71 setTimeout(resolve, time) 72 }) 73} 74