403 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			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(); | ||
|  |     } | ||
|  |   }]); |