walnut.js/scripts/controllers/login-v3.js

403 lines
12 KiB
JavaScript

'use strict';
angular.module('yololiumApp')
.controller('LoginController3', [
'$scope'
, '$q'
, '$timeout'
, '$modalInstance'
, 'Oauth3'
, 'DaplieApiSession'
, 'DaplieApiRequest'
, 'LdsAccount'
, 'myLoginOptions'
, function (
$scope
, $q
, $timeout
, $modalInstance
, Oauth3
, Oauth3ApiSession
, Oauth3ApiRequest
, LdsAccount
, opts
) {
opts = opts || {};
var scope = this;
var secretMinLen = scope.secretMinLength = Oauth3ApiSession.secretMinLength;
//var usernameMinLen = Oauth3ApiSession.usernameMinLength;
var mySession;
scope.hideSocial = opts.hideSocial;
scope.flashMessage = opts.flashMessage;
scope.flashMessageClass = opts.flashMessageClass;
scope.delta = { localLogin: {} };
function onDaplieLogin(oauth3Session) {
console.log('DEBUG onLogin');
console.log(oauth3Session);
// TODO if there is not a default account, show user-switching screen
// this will close both on user/pass and social login
scope.flashMessage = "You've logged in. Please wait while we download some ward and stake data...";
scope.flashMessageClass = 'alert-info';
return Oauth3ApiRequest.profile(oauth3Session).then(function (profile) {
console.log('DEBUG profile');
console.log(profile);
$modalInstance.close(oauth3Session);
/*
return LdsAccount.verifyAccount(ldsSession, profile).then(function () {
scope.flashMessage = "Done!";
scope.flashMessageClass = 'alert-success';
console.log('DEBUG verifiedAccount');
$modalInstance.close(ldsSession);
});
*/
});
}
function handleLoginException(err) {
scope.formState = 'login';
console.error("[Uknown Error] Either 'resource owner password' or 'delegated' login");
console.warn(err.stack);
scope.flashMessage = err.message || err.code;
scope.flashMessageClass = "alert-danger";
}
function handleSuccess(session) {
return Oauth3ApiSession.requireAccount(session).then(onDaplieLogin, function (err) {
if ('E_NO_LDSACCOUNT' !== err.code) {
throw err;
}
scope.hideSocial = true;
scope.flashMessage = err.message;
scope.flashMessageClass = "alert-warning";
}).catch(handleLoginException);
}
function handleLoginError(err) {
console.error('handleLoginError');
console.error(err);
console.warn(err.stack);
if (!err.message) {
throw err;
}
scope.formState = 'login';
scope.flashMessage = err.message || err.code;
scope.flashMessageClass = "alert-warning";
throw err;
}
scope.loginStrategies = [
{ label: 'Facebook'
, name: 'facebook'
, faImage: ""
, faClass: "fa-facebook"
, btnClass: "btn-facebook"
, login: function () {
var providerUri = (window.location.host + window.location.pathname).replace(/\/$/g, '');
var providerApiUri = 'https://oauth3.org';
return Oauth3ApiSession.login({
providerUri: providerUri // 'daplie.com/connect'
, scope: [ 'email' ]
, redirectUri: 'https://' + providerUri + '/oauth3.html'
, popup: true
// TODO XXX use whatever variable it is for apiHost
, authorizationRedirect: providerApiUri + '/api/org.oauth3.consumer/authorization_redirect/facebook.com'
}).then(handleSuccess, handleLoginError).catch(handleLoginException);
}
}
, { label: 'Google+'
, name: 'google'
, faImage: ""
, faClass: "fa-google-plus"
, btnClass: "btn-google-plus"
, login: function () {
return Oauth3ApiSession.logins.authorizationRedirect({
providerUri: 'google.com'
, scope: [ 'https://www.googleapis.com/auth/plus.login' ]
, redirectUri: 'https://beta.ldsconnect.org/oauth3.html'
, popup: true
}).then(handleSuccess, handleLoginError).catch(handleLoginException);
}
}
/*
, { label: 'LDS.org Account'
, faImage: "images/moroni-128px.png"
, faClass: ""
, btnClass: "openid"
, login: scope.loginWithLdsconnect
}
*/
];
scope.hideSocial = opts.hideSocial;
scope.flashMessage = opts.flashMessage;
scope.flashMessageClass = opts.flashMessageClass;
// This dialog is opened to update necessary account details
// It should be passed options to inform the dialog which
// missing fields are necessary to show at this time
//
// Examples:
// we want to get facebook but haven't connected yet, so we should show the connection dialog
// we just logged in for the first time and don't have an account or a local login
function onLogout() {
scope.session = null;
scope.account = null;
scope.accounts = null;
}
function onLogin(session) {
// session is always ensured as part of login
mySession = session;
var defaultAction = '';
var emails = [];
scope.account = scope.account || mySession.account || {};
if (scope.account.id) {
defaultAction = 'update';
}
scope.formAction = scope.formAction || defaultAction;
scope.account = scope.account || {};
scope.delta = scope.delta || {};
scope.deltaEmail = { type: 'email' };
scope.deltaPhone = { type: 'phone' };
scope.delta.localLogin = scope.delta.localLogin || {};
scope.logins = mySession.logins.map(function (login) {
return {
comment: ('local' === (login.provider || login.type)) ? (login.uid || 'username') : login.provider
, id: login.id
, uid: login.uid
, provider: login.provider
, type: login.type
, link: true
};
});
mySession.logins.some(function (login) {
if ('local' === (login.type || login.provider)) {
scope.account.localLoginId = login.id;
scope.delta.localLogin.id = login.id;
}
});
// login is always ensured prior to account
mySession.logins.forEach(function (l) {
(l.emails||[]).forEach(function (email) {
if (email && email.value) {
emails.push(email);
}
});
});
// TODO combo box for creating new logins
scope.emails = emails;
scope.deltaEmail.node = (emails[0] || {}).value;
if (!scope.deltaEmail.node) {
mySession.logins.some(function (login) {
scope.deltaEmail.node = (login.emails && login.emails[0] || {}).value;
return scope.deltaEmail.node;
});
}
/*
if (mySession.logins.length) {
scope.formAction = 'create';
}
*/
// TODO check mySession
if (mySession.logins.length >= 2) {
scope.recoverable = true;
}
}
scope.checkTotp = function (nodeObj) {
var tokenLen = 6;
var len = (nodeObj.totp||'').replace(/\D/g, '').length;
if (len === tokenLen) {
nodeObj.totpMessage = 'So far, so good.';
return;
}
nodeObj.totpMessage = 'Token is too short '
+ len + '/' + tokenLen
+ ' (needs to be ' + tokenLen + '+ digits)'
;
};
scope.checkSecret = function (nodeObj) {
var len = (nodeObj.secret||'').length;
var meetsLen = (len >= secretMinLen);
if (meetsLen) {
nodeObj.secretMessage = 'Login when ready, captain!';
return;
}
nodeObj.secretMessage = 'Passphrase is too short '
+ len + '/' + secretMinLen
+ ' (needs to be ' + secretMinLen + '+ characters)'
;
};
scope.submitLogin = function (nodeObj) {
var promise;
scope.flashMessage = "";
scope.flashMessageClass = "alert-danger";
if (scope._loginPromise) {
promise = scope._loginPromise;
} else {
promise = $q.when();
}
// TODO this must be implemented in all platforms
nodeObj.secret = nodeObj.secret.trim();
console.log('submitLogin nodeObj', nodeObj);
if ('create' === scope.formState) {
nodeObj.kdf = 'PBKDF2';
nodeObj.bits = 128;
nodeObj.algo = 'SHA-256';
nodeObj.iter = Math.floor(Math.random() * 100) + 1001;
}
promise = window.getProofOfSecret(nodeObj);
if ('create' === scope.formState) {
// TODO redo soft check
// TODO move to OAuth3
return promise.then(function (kdf) {
kdf.secret = null;
//console.log('Oauth3.logins', Object.keys(Oauth3.logins));
return Oauth3ApiSession.createLogin(kdf.node, kdf.type || 'email', null, {
kdf: kdf.kdf
, algo: kdf.algo
//, hash: kdf.algo
, iter: kdf.iter
, bits: kdf.bits
, salt: kdf.salt
, proof: kdf.proof
//, shadow: kdf.proof
}, null);
});
}
return promise.then(function (kdf) {
// TODO change the state to authenticating for social logins as well
scope.formState = 'authenticating';
// ALL THE SCOPES!!!
// TODO
//return Oauth3.requests.resourceOwnerPassword('https://daplie.com/connect', nodeObj.node, kdf.proof, { scope: '*', appId: 'ID__b5db805e27cc27a0ee8eddf42f46' }).then(function (session) {
return Oauth3ApiSession.login({
username: nodeObj.node
//, usernameType: nodeObj.nodeType
, password: kdf.proof
, totp: nodeObj.totp
//, mfa: kdf.mfa
, scope: '*'
}).then(function (session) {
console.log('[session]', session);
if (session.error) {
throw new Error("no oauth3 session");
}
return Oauth3ApiSession.requireAccount(session).then(onDaplieLogin);
}, handleLoginError).catch(handleLoginException);
});
};
scope.checkLdsLogin = function (nodeObj) {
var username;
var myPromise;
var nameError;
scope.formState = 'login';
nodeObj.claimable = false;
nodeObj.message = '';
scope.flashMessage = '';
scope.flashMessageClass = "alert-warning";
username = nodeObj.node;
$timeout.cancel(scope._loginTimeout);
if (!username || 'null' === username || 'undefined' === username) {
myPromise = false;
scope.formState = 'invalid';
nodeObj.message = '';
return;
}
// returns true or error object
nameError = Oauth3ApiSession.validateUsername(nodeObj.node);
if (nameError.message) {
myPromise = false;
scope.formState = 'invalid';
nodeObj.message = nameError.message;
return;
}
nodeObj.message = 'Checking username...';
myPromise = scope._loginTimeout = $timeout(function () {
scope._loginPromise = Oauth3ApiSession.getMeta(nodeObj.node, nodeObj.type || 'email').then(function (kdf) {
// kdf
nodeObj.kdf = kdf.kdf;
nodeObj.algo = kdf.algo;
nodeObj.bits = kdf.bits;
nodeObj.iter = kdf.iter;
nodeObj.salt = kdf.salt;
scope.showTotp = kdf.totpEnabledAt || kdf.totpEnabled || kdf.totp;
myPromise = false;
nodeObj.message = "'" + username + "' is already registered."
+ ' Welcome back!'
;
scope.formState = 'login';
}, function () {
// TODO test that error is simply not exists
// and not a server error
if (myPromise !== scope._loginTimeout) {
return;
}
scope.formState = 'create';
nodeObj.message = "'" + username + "' is available!";
}).catch(function (err) {
nodeObj.message = '';
scope.formState = 'login';
scope.flashMessage = "[Uknown Error] " + err.message
+ " (might need to wait a minute and try again)";
scope.flashMessageClass = "alert-danger";
throw err;
});
}, 250);
return scope._loginTimeout;
};
//
// Begin
//
//Oauth3ApiSession.onLogin($scope, onDaplieLogin);
Oauth3ApiSession.onLogout($scope, onLogout);
//Oauth3ApiSession.checkSession().then(onDaplieLogin, onLogout);
if (false) {
// prevent lint warnings until I figure out how I'll use this
onLogin();
}
}]);