From 04f07959a10e30ec92adbb4d8a4fb369babb7aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Robles?= Date: Mon, 15 Jul 2024 09:20:43 -0500 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=A8=20Separation=20of=20voting=20sess?= =?UTF-8?q?ion=20time=20and=20bearer=20token=20lifetime=20(#402)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parent issue: https://github.com/sequentech/meta/issues/762 --- avRegistration/auth-method-service.js | 158 +++++++++++++----- .../login-directive/login-directive.js | 26 +-- .../common-header-directive.js | 10 +- dist/appCommon-vmaster.js | 95 ++++++++--- 4 files changed, 207 insertions(+), 82 deletions(-) diff --git a/avRegistration/auth-method-service.js b/avRegistration/auth-method-service.js index ecd834f7..cbf106f3 100644 --- a/avRegistration/auth-method-service.js +++ b/avRegistration/auth-method-service.js @@ -36,6 +36,38 @@ angular.module('avRegistration') authmethod.captcha_status = ""; authmethod.admin = false; + authmethod.decodeToken = function(token) { + var parts = token.split("///"); + if (parts.length !== 2) { + throw new Error("Invalid token format"); + } + + var messagePart = parts[1]; + var messageComponents = messagePart.split("/"); + + if (messageComponents.length !== 2) { + throw new Error("Invalid message format"); + } + + var message = messageComponents[1]; + var subParts = message.split(":"); + + if (subParts.length < 4) { + throw new Error("Invalid message format"); + } + + var subMessage = subParts.slice(0, subParts.length - 3).join(":"); + var expiryTimestamp = parseInt(subParts[subParts.length - 3], 10); + var createTimestamp = parseInt(subParts[subParts.length - 1], 10); + + return { + message: subMessage, + create_timestamp: createTimestamp, + expiry_timestamp: expiryTimestamp, + expiry_secs_diff: expiryTimestamp - createTimestamp + }; + }; + authmethod.getAuthevent = function() { var adminId = ConfigService.freeAuthId + ''; var href = $location.path(); @@ -70,13 +102,33 @@ angular.module('avRegistration') }); } - // Function to get the difference in seconds between two Date objects - function getSecondsDifference(date1, date2) { - var millisecondsDifference = Math.abs(date2 - date1); - var secondsDifference = millisecondsDifference / 1000.0; - return secondsDifference; + function getAllTokens(isAdmin) { + var credentialsStr = $window.sessionStorage.getItem("vote_permission_tokens"); + var tokens = []; + if (credentialsStr) { + var credentials = JSON.parse(credentialsStr); + tokens = credentials.map(function (credential) { return credential.token; }); + return tokens; + } + if (isAdmin && $http.defaults.headers.common.Authorization) { + tokens.push($http.defaults.headers.common.Authorization); + } + return tokens; } + function hasPassedHalfLifeExpiry(now, isAdmin) { + var tokens = getAllTokens(isAdmin); + if (0 === tokens.length) { + return false; + } + var halfLifes = tokens.map(function (token) { + var decodedToken = authmethod.decodeToken(token); + return 1000 * (decodedToken.expiry_timestamp + decodedToken.create_timestamp)/2; + }); + var minHalfLife = Math.min.apply(null, halfLifes); + return minHalfLife < now; + } + authmethod.setAuth = function(auth, isAdmin, autheventid) { authmethod.admin = isAdmin; $http.defaults.headers.common.Authorization = auth; @@ -91,9 +143,7 @@ angular.module('avRegistration') // Only try to renew token when it's older than 50% of // the expiration time var now = new Date(); - var secsDiff = getSecondsDifference(authmethod.lastAuthDate, now); - var halfLife = ConfigService.authTokenExpirationSeconds * 0.5; - if (secsDiff <= halfLife) { + if (!hasPassedHalfLifeExpiry(now.getTime(), isAdmin)) { return; } authmethod.lastAuthDate = now; @@ -775,18 +825,26 @@ angular.module('avRegistration') return $http.post(url, data); }; + var lastRefreshMs = 0; authmethod.refreshAuthToken = function(autheventid) { var deferred = $q.defer(); + var jnow = Date.now(); + if (jnow - lastRefreshMs < 1000) { + deferred.reject("ongoing refresh"); + return deferred.promise; + } else { + lastRefreshMs = jnow; + } var postfix = "_authevent_" + autheventid; - // ping daemon is not active for normal users - if (!authmethod.admin) { + + /*if (!authmethod.admin) { var hasGracefulPeriod = window.sessionStorage.getItem('hasGracefulPeriod'); if (hasGracefulPeriod === "true") { deferred.reject("not an admin"); return deferred.promise; } - } + }*/ // if document is hidden, then do not update the cookie, and redirect // to admin logout if cookie expired if (document.visibilityState === 'hidden') { @@ -801,51 +859,56 @@ angular.module('avRegistration') return authmethod.ping(autheventid) .then(function(response) { var options = {}; - if (ConfigService.authTokenExpirationSeconds) { - options.expires = new Date(now + 1000 * ConfigService.authTokenExpirationSeconds); + var authToken = response.data['auth-token']; + if (authToken) { + var decodedToken = authmethod.decodeToken(authToken); + options.expires = new Date(now + 1000 * decodedToken.expiry_secs_diffs); + // update cookies expiration + $cookies.put( + "auth" + postfix, + response.data['auth-token'], + options + ); + $cookies.put( + "isAdmin" + postfix, + $cookies.get("isAdmin" + postfix), + options + ); + $cookies.put( + "userid" + postfix, + $cookies.get("userid" + postfix), + options + ); + $cookies.put( + "userid" + postfix, + $cookies.get("userid" + postfix), + options + ); + $cookies.put( + "user" + postfix, + $cookies.get("user" + postfix), + options + ); + authmethod.setAuth( + $cookies.get("auth" + postfix), + $cookies.get("isAdmin" + postfix), + autheventid + ); } - // update cookies expiration - $cookies.put( - "auth" + postfix, - response.data['auth-token'], - options - ); - $cookies.put( - "isAdmin" + postfix, - $cookies.get("isAdmin" + postfix), - options - ); - $cookies.put( - "userid" + postfix, - $cookies.get("userid" + postfix), - options - ); - $cookies.put( - "userid" + postfix, - $cookies.get("userid" + postfix), - options - ); - $cookies.put( - "user" + postfix, - $cookies.get("user" + postfix), - options - ); - authmethod.setAuth( - $cookies.get("auth" + postfix), - $cookies.get("isAdmin" + postfix), - autheventid - ); // if it's an election with no children elections if (angular.isDefined(response.data['vote-permission-token'])) { + var accessToken = response.data['vote-permission-token']; + var decodedAccessToken = authmethod.decodeToken(accessToken); $window.sessionStorage.setItem( "vote_permission_tokens", JSON.stringify([{ electionId: autheventid, token: response.data['vote-permission-token'], isFirst: true, - sessionStartedAtMs: sessionStartedAtMs + sessionStartedAtMs: sessionStartedAtMs, + sessionEndsAtMs: sessionStartedAtMs + 1000 * decodedAccessToken.expiry_secs_diff }]) ); $window.sessionStorage.setItem( @@ -860,6 +923,8 @@ angular.module('avRegistration') var tokens = _ .chain(response.data['vote-children-info']) .map(function (child, index) { + var accessToken = child['vote-permission-token']; + var decodedAccessToken = accessToken && authmethod.decodeToken(accessToken) || null; return { electionId: child['auth-event-id'], token: child['vote-permission-token'] || null, @@ -868,7 +933,8 @@ angular.module('avRegistration') numSuccessfulLoginsAllowed: child['num-successful-logins-allowed'], numSuccessfulLogins: child['num-successful-logins'], isFirst: index === 0, - sessionStartedAtMs: sessionStartedAtMs + sessionStartedAtMs: sessionStartedAtMs, + sessionEndsAtMs: sessionStartedAtMs + 1000 * (decodedAccessToken && decodedAccessToken.expiry_secs_diff || null) }; }) .value(); diff --git a/avRegistration/login-directive/login-directive.js b/avRegistration/login-directive/login-directive.js index 777057d2..7abb4d29 100644 --- a/avRegistration/login-directive/login-directive.js +++ b/avRegistration/login-directive/login-directive.js @@ -679,17 +679,17 @@ angular.module('avRegistration') if (response.data.status === "ok") { var postfix = "_authevent_" + autheventid; var options = {}; - if (ConfigService.authTokenExpirationSeconds) { - options.expires = new Date( - Date.now() + 1000 * ConfigService.authTokenExpirationSeconds - ); - } + var authToken = response.data['auth-token']; + var decodedToken = Authmethod.decodeToken(authToken); + options.expires = new Date( + sessionStartedAtMs + 1000 * decodedToken.expiry_secs_diff + ); $cookies.put("authevent_" + autheventid, autheventid, options); $cookies.put("userid" + postfix, response.data.username, options); $cookies.put("user" + postfix, scope.email || response.data.username || response.data.email, options); - $cookies.put("auth" + postfix, response.data['auth-token'], options); + $cookies.put("auth" + postfix, authToken, options); $cookies.put("isAdmin" + postfix, scope.isAdmin, options); - Authmethod.setAuth($cookies.get("auth" + postfix), scope.isAdmin, autheventid); + Authmethod.setAuth(authToken, scope.isAdmin, autheventid); if (scope.isAdmin) { Authmethod.getUserInfo() @@ -716,13 +716,16 @@ angular.module('avRegistration') // if it's an election with no children elections else if (angular.isDefined(response.data['vote-permission-token'])) { + var accessToken = response.data['vote-permission-token']; + var decodedAccessToken = Authmethod.decodeToken(accessToken); $window.sessionStorage.setItem( "vote_permission_tokens", JSON.stringify([{ electionId: autheventid, token: response.data['vote-permission-token'], isFirst: true, - sessionStartedAtMs: sessionStartedAtMs + sessionStartedAtMs: sessionStartedAtMs, + sessionEndsAtMs: sessionStartedAtMs + 1000 * decodedAccessToken.expiry_secs_diff }]) ); $window.sessionStorage.setItem( @@ -734,10 +737,12 @@ angular.module('avRegistration') // if it's an election with children elections then show access to them else if (angular.isDefined(response.data['vote-children-info'])) { - // assumes the iam response has the same children + // assumes the iam response has the same children var tokens = _ .chain(response.data['vote-children-info']) .map(function (child, index) { + var accessToken = child['vote-permission-token']; + var decodedAccessToken = accessToken && Authmethod.decodeToken(accessToken) || null; return { electionId: child['auth-event-id'], token: child['vote-permission-token'] || null, @@ -746,7 +751,8 @@ angular.module('avRegistration') numSuccessfulLoginsAllowed: child['num-successful-logins-allowed'], numSuccessfulLogins: child['num-successful-logins'], isFirst: index === 0, - sessionStartedAtMs: sessionStartedAtMs + sessionStartedAtMs: sessionStartedAtMs, + sessionEndsAtMs: sessionStartedAtMs + 1000 * (decodedAccessToken && decodedAccessToken.expiry_secs_diff || null) }; }) .value(); diff --git a/avUi/common-header-directive/common-header-directive.js b/avUi/common-header-directive/common-header-directive.js index 05730564..b99f9c67 100644 --- a/avUi/common-header-directive/common-header-directive.js +++ b/avUi/common-header-directive/common-header-directive.js @@ -67,6 +67,10 @@ angular // helper function for enableLogoutCountdown() function updateTimedown() { + if (scope.$parent.getSessionEndTime) { + scope.logoutTimeMs = scope.$parent.getSessionEndTime(); + } + scope.showCountdown = true; var now = Date.now(); scope.countdownSecs = Math.round((scope.logoutTimeMs - now) / 1000); @@ -118,7 +122,11 @@ angular election.presentation.booth_log_out__countdown_seconds : ConfigService.authTokenExpirationSeconds ) * 1000; - scope.logoutTimeMs = initialTimeMs + ConfigService.authTokenExpirationSeconds * 1000; + if (scope.$parent.getSessionEndTime) { + scope.logoutTimeMs = scope.$parent.getSessionEndTime(); + } else { + scope.logoutTimeMs = initialTimeMs + ConfigService.authTokenExpirationSeconds * 1000; + } scope.countdownStartTimeMs = scope.logoutTimeMs - scope.elapsedCountdownMs; scope.countdownPercent = calculateCountdownPercent(); updateProgressBar(scope.countdownPercent); diff --git a/dist/appCommon-vmaster.js b/dist/appCommon-vmaster.js index 9352ff7b..7475062c 100644 --- a/dist/appCommon-vmaster.js +++ b/dist/appCommon-vmaster.js @@ -5,8 +5,39 @@ function $buo_f() { if (angular.module("avRegistration", [ "ui.bootstrap", "ui.utils", "ui.router" ]), angular.module("avRegistration").config(function() {}), angular.module("avRegistration").factory("Authmethod", [ "$http", "$cookies", "$window", "ConfigService", "$interval", "$state", "$location", "$document", "$q", function($http, $cookies, $window, ConfigService, $interval, $state, $location, $document, $q) { var backendUrl = ConfigService.authAPI, authId = ConfigService.freeAuthId, authmethod = {}; - return authmethod.captcha_code = null, authmethod.captcha_image_url = "", authmethod.captcha_status = "", - authmethod.admin = !1, authmethod.getAuthevent = function() { + function hasPassedHalfLifeExpiry(now, halfLifes) { + halfLifes = function(isAdmin) { + var credentialsStr = $window.sessionStorage.getItem("vote_permission_tokens"), tokens = []; + return credentialsStr ? JSON.parse(credentialsStr).map(function(credential) { + return credential.token; + }) : (isAdmin && $http.defaults.headers.common.Authorization && tokens.push($http.defaults.headers.common.Authorization), + tokens); + }(halfLifes); + if (0 !== halfLifes.length) { + halfLifes = halfLifes.map(function(decodedToken) { + decodedToken = authmethod.decodeToken(decodedToken); + return 1e3 * (decodedToken.expiry_timestamp + decodedToken.create_timestamp) / 2; + }); + return Math.min.apply(null, halfLifes) < now; + } + } + authmethod.captcha_code = null, authmethod.captcha_image_url = "", authmethod.captcha_status = "", + authmethod.admin = !1, authmethod.decodeToken = function(createTimestamp) { + var subMessage = createTimestamp.split("///"); + if (2 !== subMessage.length) throw new Error("Invalid token format"); + var expiryTimestamp = subMessage[1].split("/"); + if (2 !== expiryTimestamp.length) throw new Error("Invalid message format"); + createTimestamp = expiryTimestamp[1].split(":"); + if (createTimestamp.length < 4) throw new Error("Invalid message format"); + subMessage = createTimestamp.slice(0, createTimestamp.length - 3).join(":"), expiryTimestamp = parseInt(createTimestamp[createTimestamp.length - 3], 10), + createTimestamp = parseInt(createTimestamp[createTimestamp.length - 1], 10); + return { + message: subMessage, + create_timestamp: createTimestamp, + expiry_timestamp: expiryTimestamp, + expiry_secs_diff: expiryTimestamp - createTimestamp + }; + }, authmethod.getAuthevent = function() { var adminId = ConfigService.freeAuthId + "", electionsMatch = $location.path(), authevent = "", adminMatch = electionsMatch.match(/^\/admin\//), boothMatch = electionsMatch.match(/^\/booth\/([0-9]+)\//), electionsMatch = electionsMatch.match(/^\/(elections|election)\/([0-9]+)\//); return _.isArray(adminMatch) ? authevent = adminId : _.isArray(boothMatch) && 2 === boothMatch.length ? authevent = boothMatch[1] : _.isArray(electionsMatch) && 3 === electionsMatch.length && (authevent = electionsMatch[2]), authevent; @@ -15,8 +46,8 @@ angular.module("avRegistration").config(function() {}), angular.module("avRegist if (authmethod.admin = isAdmin, $http.defaults.headers.common.Authorization = auth, authmethod.lastAuthDate = new Date(), !authmethod.iddleDetectionSetup) return authmethod.iddleDetectionSetup = !0, callback = function() { - var date1, date2, now = new Date(); - (date1 = authmethod.lastAuthDate, date2 = now, Math.abs(date2 - date1) / 1e3) <= .5 * ConfigService.authTokenExpirationSeconds || (authmethod.lastAuthDate = now, + var now = new Date(); + hasPassedHalfLifeExpiry(now.getTime(), isAdmin) && (authmethod.lastAuthDate = now, authmethod.refreshAuthToken(autheventid)); }, [ "click", "keypress", "mousemove", "mousedown", "touchstart", "touchmove" ].forEach(function(event) { document.addEventListener(event, callback); @@ -321,25 +352,33 @@ angular.module("avRegistration").config(function() {}), angular.module("avRegist mode: data }; return $http.post(url, data); - }, authmethod.refreshAuthToken = function(autheventid) { - var deferred = $q.defer(), postfix = "_authevent_" + autheventid; - if (!authmethod.admin && "true" === window.sessionStorage.getItem("hasGracefulPeriod")) return deferred.reject("not an admin"), - deferred.promise; + }; + var lastRefreshMs = 0; + return authmethod.refreshAuthToken = function(autheventid) { + var deferred = $q.defer(), jnow = Date.now(); + if (jnow - lastRefreshMs < 1e3) return deferred.reject("ongoing refresh"), deferred.promise; + lastRefreshMs = jnow; + var postfix = "_authevent_" + autheventid; if ("hidden" === document.visibilityState) return $cookies.get("auth" + postfix) || $state.go("admin.logout"), deferred.reject("tab not focused"), deferred.promise; var now = Date.now(), sessionStartedAtMs = now; return authmethod.ping(autheventid).then(function(tokens) { - var options = {}; - ConfigService.authTokenExpirationSeconds && (options.expires = new Date(now + 1e3 * ConfigService.authTokenExpirationSeconds)), - $cookies.put("auth" + postfix, tokens.data["auth-token"], options), $cookies.put("isAdmin" + postfix, $cookies.get("isAdmin" + postfix), options), - $cookies.put("userid" + postfix, $cookies.get("userid" + postfix), options), $cookies.put("userid" + postfix, $cookies.get("userid" + postfix), options), - $cookies.put("user" + postfix, $cookies.get("user" + postfix), options), authmethod.setAuth($cookies.get("auth" + postfix), $cookies.get("isAdmin" + postfix), autheventid), - angular.isDefined(tokens.data["vote-permission-token"]) ? ($window.sessionStorage.setItem("vote_permission_tokens", JSON.stringify([ { + var decodedAccessToken = {}, decodedToken = tokens.data["auth-token"]; + decodedToken && (decodedToken = authmethod.decodeToken(decodedToken), decodedAccessToken.expires = new Date(now + 1e3 * decodedToken.expiry_secs_diffs), + $cookies.put("auth" + postfix, tokens.data["auth-token"], decodedAccessToken), $cookies.put("isAdmin" + postfix, $cookies.get("isAdmin" + postfix), decodedAccessToken), + $cookies.put("userid" + postfix, $cookies.get("userid" + postfix), decodedAccessToken), + $cookies.put("userid" + postfix, $cookies.get("userid" + postfix), decodedAccessToken), + $cookies.put("user" + postfix, $cookies.get("user" + postfix), decodedAccessToken), + authmethod.setAuth($cookies.get("auth" + postfix), $cookies.get("isAdmin" + postfix), autheventid)), + angular.isDefined(tokens.data["vote-permission-token"]) ? (decodedAccessToken = tokens.data["vote-permission-token"], + decodedAccessToken = authmethod.decodeToken(decodedAccessToken), $window.sessionStorage.setItem("vote_permission_tokens", JSON.stringify([ { electionId: autheventid, token: tokens.data["vote-permission-token"], isFirst: !0, - sessionStartedAtMs: sessionStartedAtMs + sessionStartedAtMs: sessionStartedAtMs, + sessionEndsAtMs: sessionStartedAtMs + 1e3 * decodedAccessToken.expiry_secs_diff } ])), $window.sessionStorage.setItem("show-pdf", !!tokens.data["show-pdf"])) : angular.isDefined(tokens.data["vote-children-info"]) && (tokens = _.chain(tokens.data["vote-children-info"]).map(function(child, index) { + var decodedAccessToken = child["vote-permission-token"], decodedAccessToken = decodedAccessToken && authmethod.decodeToken(decodedAccessToken) || null; return { electionId: child["auth-event-id"], token: child["vote-permission-token"] || null, @@ -348,7 +387,8 @@ angular.module("avRegistration").config(function() {}), angular.module("avRegist numSuccessfulLoginsAllowed: child["num-successful-logins-allowed"], numSuccessfulLogins: child["num-successful-logins"], isFirst: 0 === index, - sessionStartedAtMs: sessionStartedAtMs + sessionStartedAtMs: sessionStartedAtMs, + sessionEndsAtMs: sessionStartedAtMs + 1e3 * (decodedAccessToken && decodedAccessToken.expiry_secs_diff || null) }; }).value(), $window.sessionStorage.setItem("vote_permission_tokens", JSON.stringify(tokens))); }); @@ -576,26 +616,29 @@ angular.module("avRegistration").config(function() {}), angular.module("avRegist scope.sendingData = !0, setError(null, null); var sessionStartedAtMs = Date.now(); Authmethod.login(data, autheventid).then(function(tokens) { - var postfix, options; + var postfix, options, decodedToken, decodedAccessToken; "ok" === tokens.data.status ? (postfix = "_authevent_" + autheventid, options = {}, - ConfigService.authTokenExpirationSeconds && (options.expires = new Date(Date.now() + 1e3 * ConfigService.authTokenExpirationSeconds)), + decodedAccessToken = tokens.data["auth-token"], decodedToken = Authmethod.decodeToken(decodedAccessToken), + options.expires = new Date(sessionStartedAtMs + 1e3 * decodedToken.expiry_secs_diff), $cookies.put("authevent_" + autheventid, autheventid, options), $cookies.put("userid" + postfix, tokens.data.username, options), $cookies.put("user" + postfix, scope.email || tokens.data.username || tokens.data.email, options), - $cookies.put("auth" + postfix, tokens.data["auth-token"], options), $cookies.put("isAdmin" + postfix, scope.isAdmin, options), - Authmethod.setAuth($cookies.get("auth" + postfix), scope.isAdmin, autheventid), - scope.isAdmin ? Authmethod.getUserInfo().then(function(response) { + $cookies.put("auth" + postfix, decodedAccessToken, options), $cookies.put("isAdmin" + postfix, scope.isAdmin, options), + Authmethod.setAuth(decodedAccessToken, scope.isAdmin, autheventid), scope.isAdmin ? Authmethod.getUserInfo().then(function(response) { var redirectUrl = $window.sessionStorage.getItem("redirect"); redirectUrl ? $window.sessionStorage.removeItem("redirect") : redirectUrl = "/admin/elections", $cookies.put("user" + postfix, response.data.email || scope.email || response.data.username, options), $window.location.href = redirectUrl; }, function(response) { $window.location.href = "/admin/elections"; - }) : angular.isDefined(tokens.data["redirect-to-url"]) ? $window.location.href = tokens.data["redirect-to-url"] : angular.isDefined(tokens.data["vote-permission-token"]) ? ($window.sessionStorage.setItem("vote_permission_tokens", JSON.stringify([ { + }) : angular.isDefined(tokens.data["redirect-to-url"]) ? $window.location.href = tokens.data["redirect-to-url"] : angular.isDefined(tokens.data["vote-permission-token"]) ? (decodedAccessToken = tokens.data["vote-permission-token"], + decodedAccessToken = Authmethod.decodeToken(decodedAccessToken), $window.sessionStorage.setItem("vote_permission_tokens", JSON.stringify([ { electionId: autheventid, token: tokens.data["vote-permission-token"], isFirst: !0, - sessionStartedAtMs: sessionStartedAtMs + sessionStartedAtMs: sessionStartedAtMs, + sessionEndsAtMs: sessionStartedAtMs + 1e3 * decodedAccessToken.expiry_secs_diff } ])), $window.sessionStorage.setItem("show-pdf", !!tokens.data["show-pdf"]), $window.location.href = "/booth/" + autheventid + "/vote") : angular.isDefined(tokens.data["vote-children-info"]) ? (tokens = _.chain(tokens.data["vote-children-info"]).map(function(child, index) { + var decodedAccessToken = child["vote-permission-token"], decodedAccessToken = decodedAccessToken && Authmethod.decodeToken(decodedAccessToken) || null; return { electionId: child["auth-event-id"], token: child["vote-permission-token"] || null, @@ -604,7 +647,8 @@ angular.module("avRegistration").config(function() {}), angular.module("avRegist numSuccessfulLoginsAllowed: child["num-successful-logins-allowed"], numSuccessfulLogins: child["num-successful-logins"], isFirst: 0 === index, - sessionStartedAtMs: sessionStartedAtMs + sessionStartedAtMs: sessionStartedAtMs, + sessionEndsAtMs: sessionStartedAtMs + 1e3 * (decodedAccessToken && decodedAccessToken.expiry_secs_diff || null) }; }).value(), $window.sessionStorage.setItem("vote_permission_tokens", JSON.stringify(tokens)), $window.location.href = "/booth/" + autheventid + "/vote") : setError("unrecognizedServerResponse", "avRegistration.loginError." + scope.method + ".unrecognizedServerResponse")) : (scope.sendingData = !1, @@ -1158,6 +1202,7 @@ angular.module("avRegistration").config(function() {}), angular.module("avRegist element && element.style.setProperty("width", percent); } function updateTimedown() { + scope.$parent.getSessionEndTime && (scope.logoutTimeMs = scope.$parent.getSessionEndTime()), scope.showCountdown = !0; var targetMins, targetNextTime, now = Date.now(); scope.countdownSecs = Math.round((scope.logoutTimeMs - now) / 1e3), scope.countdownMins = Math.round((scope.logoutTimeMs - now) / 6e4), @@ -1178,7 +1223,7 @@ angular.module("avRegistration").config(function() {}), angular.module("avRegist ConfigService.authTokenExpirationSeconds && election && election.presentation && _.isNumber(election.presentation.booth_log_out__countdown_seconds) && (scope.showCountdown = !1, scope.countdownSecs = 0, scope.countdownMins = 0, initialTimeMs = scope.$parent.getSessionStartTime && scope.$parent.getSessionStartTime() || Date.now(), scope.elapsedCountdownMs = 1e3 * (0 < election.presentation.booth_log_out__countdown_seconds ? election.presentation.booth_log_out__countdown_seconds : ConfigService.authTokenExpirationSeconds), - scope.logoutTimeMs = initialTimeMs + 1e3 * ConfigService.authTokenExpirationSeconds, + scope.$parent.getSessionEndTime ? scope.logoutTimeMs = scope.$parent.getSessionEndTime() : scope.logoutTimeMs = initialTimeMs + 1e3 * ConfigService.authTokenExpirationSeconds, scope.countdownStartTimeMs = scope.logoutTimeMs - scope.elapsedCountdownMs, scope.countdownPercent = calculateCountdownPercent(), updateProgressBar(scope.countdownPercent), scope.isDemo || scope.isPreview || setTimeout(updateTimedown, 0 < election.presentation.booth_log_out__countdown_seconds ? scope.countdownStartTimeMs - Date.now() : 0))); }, 0); From 6bd529ec80a0ca5c0cd3f316b400de9677249536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Robles?= Date: Fri, 19 Jul 2024 14:27:16 -0500 Subject: [PATCH 2/2] =?UTF-8?q?=E2=9C=A8=20Separation=20of=20voting=20sess?= =?UTF-8?q?ion=20time=20and=20bearer=20token=20lifetime=20(#404)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parent issue: https://github.com/sequentech/meta/issues/762 --- .../common-header-directive.js | 13 +++++++------ dist/appCommon-vmaster.js | 8 ++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/avUi/common-header-directive/common-header-directive.js b/avUi/common-header-directive/common-header-directive.js index b99f9c67..9fe2a0f4 100644 --- a/avUi/common-header-directive/common-header-directive.js +++ b/avUi/common-header-directive/common-header-directive.js @@ -50,7 +50,7 @@ angular scope.showVersionsModal = ShowVersionsModalService; - function calculateCountdownPercent() { + function calculateCountdownPercent() { var ratio = (scope.logoutTimeMs - Date.now())/(scope.logoutTimeMs - scope.countdownStartTimeMs); return Math.min(100, Math.round(10000*ratio)/100) + '%'; } @@ -71,6 +71,10 @@ angular scope.logoutTimeMs = scope.$parent.getSessionEndTime(); } + if (scope.$parent.getSessionStartTime) { + scope.countdownStartTimeMs = scope.$parent.getSessionStartTime(false); + } + scope.showCountdown = true; var now = Date.now(); scope.countdownSecs = Math.round((scope.logoutTimeMs - now) / 1000); @@ -81,12 +85,9 @@ angular if (scope.countdownSecs <= 1) { return; } - var targetMins = Math.floor((scope.logoutTimeMs - now) / (60 * 1000)); - var targetNextTime = scope.logoutTimeMs - targetMins * 60 * 1000; - var targetElapsedTime = targetNextTime - now; setTimeout( updateTimedown, - targetMins > 0? targetElapsedTime : 1000 + 1000 ); } @@ -116,7 +117,7 @@ angular scope.countdownSecs = 0; scope.countdownMins = 0; - var initialTimeMs = scope.$parent.getSessionStartTime && scope.$parent.getSessionStartTime() || Date.now(); + var initialTimeMs = scope.$parent.getSessionStartTime && scope.$parent.getSessionStartTime(true) || Date.now(); scope.elapsedCountdownMs = ( election.presentation.booth_log_out__countdown_seconds > 0? election.presentation.booth_log_out__countdown_seconds : diff --git a/dist/appCommon-vmaster.js b/dist/appCommon-vmaster.js index 7475062c..eeadc572 100644 --- a/dist/appCommon-vmaster.js +++ b/dist/appCommon-vmaster.js @@ -1203,12 +1203,12 @@ angular.module("avRegistration").config(function() {}), angular.module("avRegist } function updateTimedown() { scope.$parent.getSessionEndTime && (scope.logoutTimeMs = scope.$parent.getSessionEndTime()), + scope.$parent.getSessionStartTime && (scope.countdownStartTimeMs = scope.$parent.getSessionStartTime(!1)), scope.showCountdown = !0; - var targetMins, targetNextTime, now = Date.now(); + var now = Date.now(); scope.countdownSecs = Math.round((scope.logoutTimeMs - now) / 1e3), scope.countdownMins = Math.round((scope.logoutTimeMs - now) / 6e4), scope.countdownPercent = calculateCountdownPercent(), updateProgressBar(scope.countdownPercent), - scope.$apply(), scope.countdownSecs <= 1 || (targetMins = Math.floor((scope.logoutTimeMs - now) / 6e4), - targetNextTime = scope.logoutTimeMs - 60 * targetMins * 1e3, setTimeout(updateTimedown, 0 < targetMins ? targetNextTime - now : 1e3)); + scope.$apply(), scope.countdownSecs <= 1 || setTimeout(updateTimedown, 1e3); } scope.parentElection = scope.$parent.parentElection, scope.election = scope.$parent.election, scope.confirmLogoutModal = scope.$parent.confirmLogoutModal, scope.configService = ConfigService, @@ -1221,7 +1221,7 @@ angular.module("avRegistration").config(function() {}), angular.module("avRegist var election, initialTimeMs; scope.showCountdown = !1, scope.$parent.isStateCompatibleWithCountdown && !scope.$parent.isStateCompatibleWithCountdown() || (election = scope.parentElection || scope.election, ConfigService.authTokenExpirationSeconds && election && election.presentation && _.isNumber(election.presentation.booth_log_out__countdown_seconds) && (scope.showCountdown = !1, - scope.countdownSecs = 0, scope.countdownMins = 0, initialTimeMs = scope.$parent.getSessionStartTime && scope.$parent.getSessionStartTime() || Date.now(), + scope.countdownSecs = 0, scope.countdownMins = 0, initialTimeMs = scope.$parent.getSessionStartTime && scope.$parent.getSessionStartTime(!0) || Date.now(), scope.elapsedCountdownMs = 1e3 * (0 < election.presentation.booth_log_out__countdown_seconds ? election.presentation.booth_log_out__countdown_seconds : ConfigService.authTokenExpirationSeconds), scope.$parent.getSessionEndTime ? scope.logoutTimeMs = scope.$parent.getSessionEndTime() : scope.logoutTimeMs = initialTimeMs + 1e3 * ConfigService.authTokenExpirationSeconds, scope.countdownStartTimeMs = scope.logoutTimeMs - scope.elapsedCountdownMs, scope.countdownPercent = calculateCountdownPercent(),