Bug 1010623 - sign out when password was reset on web. r=jedp
authorSam Penrose <spenrose@mozilla.com>
Thu, 15 May 2014 16:52:52 -0700
changeset 183438 75b0548333cabf265cd99d13332af04e77b90b46
parent 183437 8392628b4232ac47a2d17753b47219e3bcef5134
child 183439 111c883ca3bf79c084de51d5c0f711fdddabd6b1
push idunknown
push userunknown
push dateunknown
reviewersjedp
bugs1010623
milestone32.0a1
Bug 1010623 - sign out when password was reset on web. r=jedp
services/fxaccounts/FxAccounts.jsm
services/fxaccounts/FxAccountsClient.jsm
services/fxaccounts/tests/xpcshell/test_accounts.js
--- a/services/fxaccounts/FxAccounts.jsm
+++ b/services/fxaccounts/FxAccounts.jsm
@@ -20,16 +20,17 @@ Cu.import("resource://gre/modules/FxAcco
 XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsClient",
   "resource://gre/modules/FxAccountsClient.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "jwcrypto",
   "resource://gre/modules/identity/jwcrypto.jsm");
 
 // All properties exposed by the public FxAccounts API.
 let publicProperties = [
+  "accountStatus",
   "getAccountsClient",
   "getAccountsSignInURI",
   "getAccountsSignUpURI",
   "getAssertion",
   "getKeys",
   "getSignedInUser",
   "loadAndPoll",
   "localtimeOffsetMsec",
@@ -506,16 +507,25 @@ FxAccountsInternal.prototype = {
       log.debug("Polling aborted; Another user signing in");
       clearTimeout(this.currentTimer);
       this.currentTimer = 0;
     }
     this.currentAccountState.abort();
     this.currentAccountState = new AccountState(this);
   },
 
+  accountStatus: function accountStatus() {
+    return this.currentAccountState.getUserAccountData().then(data => {
+      if (!data) {
+        return false;
+      }
+      return this.fxAccountsClient.accountStatus(data.uid);
+    });
+  },
+
   signOut: function signOut(localOnly) {
     let currentState = this.currentAccountState;
     let sessionToken;
     return currentState.getUserAccountData().then(data => {
       // Save the session token for use in the call to signOut below.
       sessionToken = data && data.sessionToken;
       return this._signOutLocal();
     }).then(() => {
--- a/services/fxaccounts/FxAccountsClient.jsm
+++ b/services/fxaccounts/FxAccountsClient.jsm
@@ -284,16 +284,34 @@ this.FxAccountsClient.prototype = {
             throw expectedError;
             break;
         }
       }
     );
   },
 
   /**
+   * Given the uid of an existing account (not an arbitrary email), ask
+   * the server if it still exists via /account/status.
+   *
+   * Used for differentiating between password change and account deletion.
+   */
+  accountStatus: function(uid) {
+    return this._request("/account/status?uid="+uid, "GET").then(
+      (result) => {
+        return result.exists;
+      },
+      (error) => {
+        log.error("accountStatus failed with: " + error);
+        return Promise.reject(error);
+      }
+    );
+  },
+
+  /**
    * The FxA auth server expects requests to certain endpoints to be authorized using Hawk.
    * Hawk credentials are derived using shared secrets, which depend on the context
    * (e.g. sessionToken vs. keyFetchToken).
    *
    * @param tokenHex
    *        The current session token encoded in hex
    * @param context
    *        A context for the credentials
--- a/services/fxaccounts/tests/xpcshell/test_accounts.js
+++ b/services/fxaccounts/tests/xpcshell/test_accounts.js
@@ -36,32 +36,39 @@ function run_test() {
  * lag time to simulate some latency.
  *
  * We add the _verified attribute to mock the change in verification
  * state on the FXA server.
  */
 function MockFxAccountsClient() {
   this._email = "nobody@example.com";
   this._verified = false;
+  this._deletedOnServer = false; // for testing accountStatus
 
   // mock calls up to the auth server to determine whether the
   // user account has been verified
   this.recoveryEmailStatus = function (sessionToken) {
     // simulate a call to /recovery_email/status
     let deferred = Promise.defer();
 
     let response = {
       email: this._email,
       verified: this._verified
     };
     deferred.resolve(response);
 
     return deferred.promise;
   };
 
+  this.accountStatus = function(uid) {
+    let deferred = Promise.defer();
+    deferred.resolve(!!uid && (!this._deletedOnServer));
+    return deferred.promise;
+  };
+
   this.accountKeys = function (keyFetchToken) {
     let deferred = Promise.defer();
 
     do_timeout(50, () => {
       let response = {
         kA: expandBytes("11"),
         wrapKB: expandBytes("22")
       };
@@ -500,16 +507,49 @@ add_task(function test_resend_email_not_
   } catch(err) {
     do_check_eq(err.message,
       "Cannot resend verification email; no signed-in user");
     return;
   }
   do_throw("Should not be able to resend email when nobody is signed in");
 });
 
+add_test(function test_accountStatus() {
+  let fxa = new MockFxAccounts();
+  let alice = getTestUser("alice");
+
+  // If we have no user, we have no account server-side
+  fxa.accountStatus().then(
+    (result) => {
+      do_check_false(result);
+    }
+  ).then(
+    () => {
+      fxa.setSignedInUser(alice).then(
+        () => {
+          fxa.accountStatus().then(
+            (result) => {
+               // FxAccounts.accountStatus() should match Client.accountStatus()
+               do_check_true(result);
+               fxa.internal.fxAccountsClient._deletedOnServer = true;
+               fxa.accountStatus().then(
+                 (result) => {
+                   do_check_false(result);
+                   fxa.internal.fxAccountsClient._deletedOnServer = false;
+                   run_next_test();
+                 }
+               );
+            }
+          )
+        }
+      );
+    }
+  );
+});
+
 add_test(function test_resend_email() {
   let fxa = new MockFxAccounts();
   let alice = getTestUser("alice");
 
   let initialState = fxa.internal.currentAccountState;
 
   // Alice is the user signing in; her email is unverified.
   fxa.setSignedInUser(alice).then(() => {