From 238e83de720760afc3614128c175927a2e525a3b Mon Sep 17 00:00:00 2001
From: Lloyd Hilaiel <lloyd@hilaiel.com>
Date: Fri, 16 Mar 2012 18:35:33 -0600
Subject: [PATCH] ensure login/logout events are not erroneously sent multiple
 times.

---
 example/rp/index.html                         |  9 ++++++++
 .../static/communication_iframe/start.js      | 19 ++++++++++++++-
 .../static/dialog/controllers/actions.js      |  4 ++--
 resources/static/dialog/resources/state.js    |  2 +-
 resources/static/include_js/include.js        | 23 +++++++++++++++----
 5 files changed, 48 insertions(+), 9 deletions(-)

diff --git a/example/rp/index.html b/example/rp/index.html
index 17270fcb0..924e8f350 100644
--- a/example/rp/index.html
+++ b/example/rp/index.html
@@ -97,6 +97,12 @@ pre {
 <script src="https://browserid.org/include.js"></script>
 <script>
 
+function loggit() {
+  try {
+    console.log.apply(console, arguments);
+  } catch(e) {}
+}
+
 // a function to check an assertion against the server
 function checkAssertion(assertion) {
   $.ajax({
@@ -118,15 +124,18 @@ function checkAssertion(assertion) {
 };
 
 navigator.id.addEventListener('login', function(event) {
+  loggit("login event");
   checkAssertion(event.assertion);
 });
 
 navigator.id.addEventListener('logout', function(event) {
+  loggit("logout event");
   var txt = 'got event at ' + (new Date).toString();
   $(".logoutEvents > pre").text(txt);
 });
 
 navigator.id.addEventListener('loginCanceled', function(event) {
+  loggit("loginCanceled");
   var txt = 'got event at ' + (new Date).toString();
   $(".loginCanceledEvents > pre").text(txt);
 });
diff --git a/resources/static/communication_iframe/start.js b/resources/static/communication_iframe/start.js
index e213d0261..2c87525c6 100644
--- a/resources/static/communication_iframe/start.js
+++ b/resources/static/communication_iframe/start.js
@@ -28,9 +28,17 @@
 
   var loggedInUser = undefined;
 
+  // the controlling page may "pause" the iframe when someone else (the dialog)
+  // is supposed to emit events
+  var pause = false;
+
   function checkAndEmit(oncomplete) {
-    // XXX: seemless re-certification!
+    console.log('checking', pause, localStorage.loggedIn);
+    if (pause) return;
+
+    // this will re-certify the user if neccesary
     user.getSilentAssertion(loggedInUser, function(email, assertion) {
+      console.log(email, assertion);
       if (email) {
         // only send login events when the assertion is defined - when
         // the 'loggedInUser' is already logged in, it's false - that is
@@ -75,4 +83,13 @@
       chan.notify({ method: 'logout' });
     }
   });
+
+  chan.bind("dialog_running", function(trans, params) {
+    pause = true;
+  });
+
+  chan.bind("dialog_complete", function(trans, params) {
+    pause = false;
+    checkAndEmit();
+  });
 }());
diff --git a/resources/static/dialog/controllers/actions.js b/resources/static/dialog/controllers/actions.js
index 334bcbd15..98876183f 100644
--- a/resources/static/dialog/controllers/actions.js
+++ b/resources/static/dialog/controllers/actions.js
@@ -113,12 +113,12 @@ BrowserID.Modules.Actions = (function() {
       }, self.getErrorDialog(errors.getAssertion));
     },
 
-    doAssertionGenerated: function(assertion) {
+    doAssertionGenerated: function(o) {
       // Clear onerror before the call to onsuccess - the code to onsuccess
       // calls window.close, which would trigger the onerror callback if we
       // tried this afterwards.
       onerror = null;
-      if(onsuccess) onsuccess(assertion);
+      if(onsuccess) onsuccess(o);
     },
 
     doNotMe: function() {
diff --git a/resources/static/dialog/resources/state.js b/resources/static/dialog/resources/state.js
index 11ebf81e0..7a3e7d1d5 100644
--- a/resources/static/dialog/resources/state.js
+++ b/resources/static/dialog/resources/state.js
@@ -260,7 +260,7 @@ BrowserID.State = (function() {
         }
         else {
           storage.setLoggedIn(user.getOrigin(), self.email);
-          startAction("doAssertionGenerated", info.assertion);
+          startAction("doAssertionGenerated", { assertion: info.assertion, email: self.email });
         }
       }
       else {
diff --git a/resources/static/include_js/include.js b/resources/static/include_js/include.js
index d9dfc7821..01a67fe39 100644
--- a/resources/static/include_js/include.js
+++ b/resources/static/include_js/include.js
@@ -918,10 +918,6 @@
     };
   }());
 
-  /**
-   * The meat and potatoes of the verified email protocol
-   */
-
   if (!navigator.id) {
     navigator.id = {};
   }
@@ -1123,6 +1119,10 @@
         return;
       }
 
+      // notify the iframe that the dialog is running so we
+      // don't do duplicative work
+      if (commChan) commChan.notify({ method: 'dialog_running' });
+
       w = WinChan.open({
         url: ipServer + '/sign_in',
         relay_url: ipServer + '/relay',
@@ -1132,9 +1132,22 @@
           params: options
         }
       }, function(err, r) {
+        // unpause the iframe to detect future changes in login state
+        if (commChan) {
+          // update the loggedInUser in the case that an assertion was generated, as
+          // this will prevent the comm iframe from thinking that state has changed
+          // and generating a new assertion.  IF, however, this request is not a success,
+          // then we do not change the loggedInUser - and we will let the comm frame determine
+          // if generating a logout event is the right thing to do
+          if (!err && r && r.email) {
+            commChan.notify({ method: 'loggedInUser', params: r.email });
+          }
+          commChan.notify({ method: 'dialog_complete' });
+        }
+
         // clear the window handle
         w = undefined;
-        if (!err && r) emitEvent('login', { assertion: r });
+        if (!err && r && r.assertion) emitEvent('login', { assertion: r.assertion });
         else emitEvent('loginCanceled');
       });
     };
-- 
GitLab