'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(); } }]);