'use strict'; // process.stdout.isTTY var form = require('terminal-forms.js').create(process.stdin, process.stdout); //var dns = form.PromiseA.promisifyAll(require('dns')); var OAUTH3 = require('../oauth3.node.js'); // TODO change to ._hooks OAUTH3._hooks = require('../oauth3.node.storage.js'); // directives = { get, set } // sessions = { get, set } /* OAUTH3._hooks.directives.get = require('../oauth3.node.storage.js').directives.get; OAUTH3._hooks.directives.set = require('../oauth3.node.storage.js').directives.set; OAUTH3._hooks.session.get = require('../oauth3.node.storage.js').sessions.get; OAUTH3._hooks.session.set = require('../oauth3.node.storage.js').sessions.set; */ // opts = { email, providerUri } module.exports.login = function (options) { options = options || {}; var url = require('url'); //console.log('stdin tty', process.stdin.isTTY); //console.log('stdout tty', process.stdout.isTTY); var oauth3; var opts = { email: options.email , providerUri: options.providerUri }; if (opts.form) { form = opts.form; } var email; var providerUrl; var providerUri; var sameProvider; var username; function getSession() { var username; // TODO lookup uuid locally before performing loginMeta // TODO lookup token locally before performing loginMeta / otp return OAUTH3.authn.loginMeta(oauth3._providerDirectives, { email: email }).then(function (/*result*/) { return { node: email, type: 'email' }; }, function (/*err*/) { // TODO require hashcash to create user account function confirmCreateAccount() { // TODO directives should specify private (invite-only) vs internal (request) vs public (allow) accounts return form.ask({ label: "We don't recognize that address. Do you want to create a new account? [Y/n] " , type: 'text' // TODO boolean with default Y or N }).then(function (result) { if (!result.input) { result.input = 'Y'; } // TODO needs backup address if email is on same domain as login result.input = result.input.toLowerCase(); if ('y' !== result.input) { return getCurrentUserEmail(); } if (!sameProvider) { return { node: email, type: 'email' }; } return form.ask({ label: "What's your recovery email (or cloud mail) address? ", type: 'email' }).then(function (recoveryResult) { return { node: email , type: 'name' , recovery: recoveryResult.result || recoveryResult.input }; }); }); } return confirmCreateAccount(); }).then(function (user) { // TODO skip if token exists locally var email = (user.recovery || user.node); form.println("Sending login code to '" + email + "'..."); return OAUTH3.authn.otp(oauth3._providerDirectives, { email: email }).then(function (otpResult) { return form.ask({ label: "What's your login code? " , help: "(it was sent to '" + email + "' and looks like 1234-5678-9012)" // onkeyup // ondebounce // onchange // regexp // html5 name? , onReturnAsync: function (rs, ws, input/*, ch*/) { var formatted = input.toLowerCase().replace(/[^\d]+/g, ''); if (12 !== formatted.length) { return form.PromiseA.reject(new Error("invalid code please try again in the format xxxx-yyyy-zzzz")); } formatted = formatted.match(/.{4,4}/g).join('-'); if (14 !== formatted.split('').length) { return form.PromiseA.reject(new Error("invalid code '" + formatted + "', please try again xxxx-yyyy-zzzz")); } var data = { username: email , username_type: 'email' , client_id: OAUTH3.uri.normalize(oauth3._providerDirectives.issuer) , client_uri: OAUTH3.uri.normalize(oauth3._providerDirectives.issuer) , otp_code: formatted , otp_uuid: otpResult.data.uuid }; // returns session instead of input var colors = require('colors'); form.setStatus(colors.dim("authenticating with server...")); return OAUTH3.authn.resourceOwnerPassword(oauth3._providerDirectives, data).then(function (result) { return result; }, function (/*err*/) { // TODO test error return form.PromiseA.reject(new Error("The code '" + formatted + "' is mistyped or incorrect. Double check and try again.")); }); } }); }); }); } function getCurrentUserEmail() { return form.ask({ label: "What's your email (or cloud mail) address? ", type: 'email', value: opts.email }).then(function (emailResult) { opts.email = undefined; email = (emailResult.result || emailResult.input); var emailParts = email.split('@'); var domain = emailParts[1]; username = emailParts[0]; providerUrl = 'https://' + domain; providerUri = domain; var urlObj = url.parse(providerUrl); // TODO get unique client id for bootstrapping app oauth3 = OAUTH3.create(urlObj); return oauth3.setProvider(domain).then(function () { sameProvider = true; // ignore }, function () { function askOauth3Url() { return form.ask({ label: "What's your OAuth3 Provider URL? ", type: 'url', value: opts.providerUri }).then(function (urlResult) { opts.providerUri = undefined; providerUrl = urlResult.result || urlResult.input; providerUri = OAUTH3.uri.normalize(providerUrl); var urlObj = url.parse(providerUrl); // TODO get unique client id for bootstrapping app oauth3 = OAUTH3.create(urlObj); return oauth3.setProvider(providerUri).then(function () { // ignore }, function (err) { form.println(err.stack || err.message || err.toString()); return askOauth3Url(); }); }); } return askOauth3Url(); }); }); } return getCurrentUserEmail().then(function () { return OAUTH3._hooks.meta.get(email).then(function (id) { if (!id) { return null; } return OAUTH3._hooks.sessions.get(providerUri, id).then(function (session) { if (session) { return session; } return null; }); }); }).then(function (session) { if (session) { return session; } return getSession().then(function (sessionResult) { var session = sessionResult.result; var id = require('crypto').createHash('sha256').update(session.token.sub || '').digest('hex'); return OAUTH3._hooks.sessions.set(providerUri, session, id).then(function (session) { return OAUTH3._hooks.meta.set(email, id).then(function () { return session; }); }); }); }).then(function (session) { oauth3.__session = session; return oauth3; }); };