diff --git a/lib/db.js b/lib/db.js
index 22fc1da0021d7cc5d004a64397234040f73f617d..ea1ccd7977e61f1ff12eed45e49e0fd86792ba71 100644
--- a/lib/db.js
+++ b/lib/db.js
@@ -105,6 +105,7 @@ exports.onReady = function(f) {
 // these are read only database calls
 [
   'emailKnown',
+  'userKnown',
   'isStaged',
   'emailsBelongToSameAccount',
   'emailForVerificationSecret',
@@ -114,7 +115,9 @@ exports.onReady = function(f) {
   'listEmails',
   'lastStaged',
   'ping',
-  'emailType'
+  'emailType',
+  'userOwnsEmail',
+  'emailToUID'
 ].forEach(function(fn) {
   exports[fn] = function() {
     checkReady();
diff --git a/lib/db/json.js b/lib/db/json.js
index 2523d647676bcf39403fc472e16bb463707dd2a8..9b9510bb01caddb3396c3eaba9c486c827625cc2 100644
--- a/lib/db/json.js
+++ b/lib/db/json.js
@@ -55,6 +55,7 @@ var dbPath = path.join(configuration.get('var_path'), "authdb.json");
 /* The JSON database. The structure is thus:
  *  [
  *    {
+ *      id: <numerical user id>
  *      password: "somepass",
  *      emails: {
  *        "lloyd@hilaiel.com": {
@@ -65,6 +66,14 @@ var dbPath = path.join(configuration.get('var_path'), "authdb.json");
  *  ]
  */
 
+function getNextUserID() {
+  var max = 1;
+  jsel.forEach(".id", db.users, function(id) {
+    if (id >= max) max = id + 1;
+  });
+  return max;
+};
+
 var db = {
   users: [ ],
   stagedEmails: { },
@@ -157,8 +166,8 @@ exports.emailsBelongToSameAccount = function(lhs, rhs, cb) {
   sync();
   emailToUserID(lhs, function(lhs_uid) {
     emailToUserID(rhs, function(rhs_uid) {
-      cb(lhs_uid === rhs_uid);
-    }, function (error) {
+      cb(lhs_uid === rhs_uid); 
+   }, function (error) {
       cb(false);
     });
   }, function (error) {
@@ -166,7 +175,25 @@ exports.emailsBelongToSameAccount = function(lhs, rhs, cb) {
   });
 };
 
-function addEmailToAccount(existing_email, email, type, cb) {
+exports.emailToUID = function(email, cb) {
+  sync();
+  var m = jsel.match(":root > object:has(.emails > ." + ESC(email) + ") > .id", db.users);
+  if (m.length === 0) m = undefined;
+  else m = m[0];
+  process.nextTick(function() {
+    cb(m);
+  });
+};
+
+exports.userOwnsEmail = function(uid, email, cb) {
+  sync();
+  var m = jsel.match(":root > object:has(:root > .id:expr(x=" + ESC(uid) + ")):has(.emails > ." + ESC(email) + ")", db.users);
+  process.nextTick(function() {
+    cb(!!m);
+  });
+};
+
+function addEmailToAccount(userID, email, type, cb) {
   // validate 'type' isn't bogus
   if ([ 'secondary', 'primary' ].indexOf(type) === -1) {
     return process.nextTick(function() {
@@ -174,14 +201,14 @@ function addEmailToAccount(existing_email, email, type, cb) {
     });
   }
 
-  emailToUserID(existing_email, function(userID) {
-    if (userID == undefined) {
-      cb("no such email: " + existing_email, undefined);
-    } else {
-      db.users[userID].emails[email] = { type: type };
+  process.nextTick(function() {
+    sync();
+    var emails = jsel.match(":has(.id:expr(x="+ ESC(userID) +")) > .emails", db.users);
+    if (emails && emails.length > 0) {
+      emails[0][email] = { type: type };
       flush();
-      cb();
     }
+    cb();
   });
 }
 
@@ -200,13 +227,13 @@ exports.stageUser = function(email, cb) {
   });
 };
 
-exports.stageEmail = function(existing_email, new_email, cb) {
+exports.stageEmail = function(existing_user, new_email, cb) {
   secrets.generate(48, function(secret) {
     // overwrite previously staged users
     sync();
     db.staged[secret] = {
       type: "add_email",
-      existing_email: existing_email,
+      existing_user: existing_user,
       email: new_email,
       when: (new Date()).getTime()
     };
@@ -221,6 +248,7 @@ exports.createUserWithPrimaryEmail = function(email, cb) {
   var emailVal = { };
   emailVal[email] = { type: 'primary' };
   db.users.push({
+    id: getNextUserID(),
     password: null,
     emails: emailVal
   });
@@ -242,7 +270,7 @@ exports.emailForVerificationSecret = function(secret, cb) {
   process.nextTick(function() {
     sync();
     if (!db.staged[secret]) return cb("no such secret");
-    exports.checkAuth(db.staged[secret].existing_email, function (hash) {
+    exports.checkAuth(db.staged[secret].existing_user, function (hash) {
       cb(undefined, {
         email: db.staged[secret].email,
         needs_password: !hash
@@ -273,6 +301,7 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
         var emailVal = {};
         emailVal[o.email] = { type: 'secondary' };
         db.users.push({
+          id: getNextUserID(),
           password: hash,
           emails: emailVal
         });
@@ -286,9 +315,8 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
       // NOTE: this might be sub-optimal, but it's a dead simple approach that mitigates many attacks
       // and gives us reasonable behavior (without explicitly supporting) in the face of shared email
       // addresses.
-
       if (known) {
-        exports.removeEmail(o.email, o.email, function (err) {
+        removeEmailNoCheck(o.email, function (err) {
           if (err) cb(err);
           else createAccount();
         });
@@ -299,10 +327,10 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
   } else if (o.type === 'add_email') {
     exports.emailKnown(o.email, function(known) {
       function addIt() {
-        addEmailToAccount(o.existing_email, o.email, 'secondary', cb);
+        addEmailToAccount(o.existing_user, o.email, 'secondary', cb);
       }
       if (known) {
-        exports.removeEmail(o.email, o.email, function (err) {
+        removeEmailNoCheck(o.email, function (err) {
           if (err) cb(err);
           else addIt();
         });
@@ -315,15 +343,14 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
   }
 };
 
-exports.addPrimaryEmailToAccount = function(acctEmail, emailToAdd, cb) {
+exports.addPrimaryEmailToAccount = function(userID, emailToAdd, cb) {
   sync();
-
   exports.emailKnown(emailToAdd, function(known) {
     function addIt() {
-      addEmailToAccount(acctEmail, emailToAdd, 'primary', cb);
+      addEmailToAccount(userID, emailToAdd, 'primary', cb);
     }
     if (known) {
-      exports.removeEmail(emailToAdd, emailToAdd, function (err) {
+      removeEmailNoCheck(emailToAdd, function (err) {
         if (err) cb(err);
         else addIt();
       });
@@ -333,22 +360,33 @@ exports.addPrimaryEmailToAccount = function(acctEmail, emailToAdd, cb) {
   });
 };
 
-exports.checkAuth = function(email, cb) {
+exports.checkAuth = function(userID, cb) {
+  sync();
+  var m = undefined;
+  if (userID) {
+    m = jsel.match(":root > object:has(:root > .id:expr(x=" + ESC(userID) + ")) > .password", db.users);
+    if (m.length === 0) m = undefined;
+    else m = m[0];
+  }
+  process.nextTick(function() { cb(m) });
+};
+
+exports.userKnown = function(userID, cb) {
   sync();
-  var m = jsel.match(":root > object:has(.emails > ." + ESC(email) + ") > .password", db.users);
+  var m = jsel.match(":root > object:has(:root > .id:expr(x=" + ESC(userID) + "))", db.users);
   if (m.length === 0) m = undefined;
   else m = m[0];
-  setTimeout(function() { cb(m) }, 0);
+  process.nextTick(function() { cb(m) });
 };
 
-exports.updatePassword = function(email, hash, cb) {
+exports.updatePassword = function(userID, hash, cb) {
   sync();
-  var m = jsel.match(":root > object:has(.emails > ." + ESC(email) + ")", db.users);
+  var m = jsel.match(":root > object:has(.id:expr(x=" + ESC(userID) + "))", db.users);
   var err = undefined;
   if (m.length === 0) err = "no such email address";
   else m[0].password = hash;
   flush();
-  setTimeout(function() { cb(err) }, 0);
+  process.nextTick(function() { cb(err) });
 };
 
 function emailToUserID(email, cb) {
@@ -366,22 +404,21 @@ function emailToUserID(email, cb) {
   setTimeout(function() { cb(id); }, 0);
 }
 
-exports.listEmails = function(email, cb) {
+exports.listEmails = function(uid, cb) {
   sync();
-  // get the user id associated with this account
-  emailToUserID(email, function(userID) {
-    if (userID === undefined) {
-      cb("no such email: " + email);
+  var emails = jsel.match(":has(.id:expr(x="+ ESC(uid) +")) > .emails", db.users);
+  process.nextTick(function() {
+    if (!emails || emails.length != 1) {
+      cb("no such user: " + uid);
       return;
     }
-    var emails = jsel.match(".emails", db.users[userID]);
     cb(null, emails[0]);
   });
 };
 
-exports.removeEmail = function(authenticated_email, email, cb) {
+exports.removeEmail = function(authenticated_user, email, cb) {
   sync();
-  var m = jsel.match(".emails:has(."+ESC(authenticated_email)+"):has(."+ESC(email)+")", db.users);
+  var m = jsel.match(":has(.id:expr(x=" + ESC(authenticated_user) + ")) .emails:has(."+ESC(email)+")", db.users);
 
   if (m.length) {
     var emails = m[0];
@@ -391,20 +428,40 @@ exports.removeEmail = function(authenticated_email, email, cb) {
   setTimeout(function() { cb(); }, 0);
 };
 
-exports.cancelAccount = function(authenticated_email, cb) {
-  emailToUserID(authenticated_email, function(user_id) {
-    db.users.splice(user_id, 1);
+function removeEmailNoCheck(email, cb) {
+  sync();
+  var m = jsel.match(".emails:has(."+ESC(email)+")", db.users);
+  if (m.length) {
+    var emails = m[0];
+    delete emails[email];
     flush();
-    cb();
-  });
+  }
+  process.nextTick(function() { cb(); });
+};
+
+exports.cancelAccount = function(authenticated_uid, cb) {
+  sync();
+  var id = undefined;
+
+  for (var i = 0; i < db.users.length; i++) {
+    if (db.users[i].id === authenticated_uid) break;
+  }
+
+  if (i < db.users.length) {
+    db.users.splice(i, 1);
+    flush();
+  }
+
+  process.nextTick(function() { cb(); });
 };
 
 exports.addTestUser = function(email, hash, cb) {
   sync();
-  exports.removeEmail(email, email, function() {
+  removeEmailNoCheck(email, function() {
     var emailVal = {};
     emailVal[email] = { type: 'secondary' };
     db.users.push({
+      id: getNextUserID(),
       password: hash,
       emails: emailVal
     });
diff --git a/tests/db-test.js b/tests/db-test.js
index 59d4b70cf73457b3fb5ca426e13fd95bcafea579..74fa688ab9db3ebfb987d2f2994797f04cb8de04 100755
--- a/tests/db-test.js
+++ b/tests/db-test.js
@@ -180,7 +180,10 @@ suite.addBatch({
 suite.addBatch({
   "checkAuth returns": {
     topic: function() {
-      db.checkAuth('lloyd@nowhe.re', this.callback);
+      var cb = this.callback;
+      db.emailToUID('lloyd@nowhe.re', function(uid) {
+        db.checkAuth(uid, cb);
+      });
     },
     "the correct password": function(r) {
       assert.strictEqual(r, "fakepasswordhash");
@@ -189,34 +192,58 @@ suite.addBatch({
 });
 
 suite.addBatch({
-  "staging an email": {
+  "emailToUID": {
     topic: function() {
-      db.stageEmail('lloyd@nowhe.re', 'lloyd@somewhe.re', this.callback);
+      db.emailToUID('lloyd@nowhe.re', this.callback);
     },
-    "yields a valid secret": function(secret) {
-      assert.isString(secret);
-      assert.strictEqual(secret.length, 48);
+    "returns a valid userid": function(r) {
+      assert.isNumber(r);
     },
-    "then": {
-      topic: function(secret) {
-        var cb = this.callback;
-        db.isStaged('lloyd@somewhe.re', function(r) { cb(secret, r); });
+    "returns a UID": {
+      topic: function(uid) {
+        db.userOwnsEmail(uid, 'lloyd@nowhe.re', this.callback);
       },
-      "makes it visible via isStaged": function(sekret, r) { assert.isTrue(r); },
-      "lets you verify it": {
-        topic: function(secret, r) {
-          db.gotVerificationSecret(secret, undefined, this.callback);
-        },
-        "successfully": function(r) {
-          assert.isUndefined(r);
-        },
-        "and knownEmail": {
-          topic: function() { db.emailKnown('lloyd@somewhe.re', this.callback); },
-          "returns true": function(r) { assert.isTrue(r); }
+      "that owns the original email": function(r) {
+        assert.ok(r);
+      }
+    }
+  }
+});
+
+suite.addBatch({
+  "getting a UID, then": {
+    topic: function() {
+      db.emailToUID('lloyd@nowhe.re', this.callback);
+    },
+    "staging an email": {
+      topic: function(uid) {
+        db.stageEmail(uid, 'lloyd@somewhe.re', this.callback);
+      },
+      "yields a valid secret": function(secret) {
+        assert.isString(secret);
+        assert.strictEqual(secret.length, 48);
+      },
+      "then": {
+        topic: function(secret) {
+          var cb = this.callback;
+          db.isStaged('lloyd@somewhe.re', function(r) { cb(secret, r); });
         },
-        "and isStaged": {
-          topic: function() { db.isStaged('lloyd@somewhe.re', this.callback); },
-          "returns false": function(r) { assert.isFalse(r); }
+        "makes it visible via isStaged": function(sekret, r) { assert.isTrue(r); },
+        "lets you verify it": {
+          topic: function(secret, r) {
+            db.gotVerificationSecret(secret, undefined, this.callback);
+          },
+          "successfully": function(r) {
+            assert.isUndefined(r);
+          },
+          "and knownEmail": {
+            topic: function() { db.emailKnown('lloyd@somewhe.re', this.callback); },
+            "returns true": function(r) { assert.isTrue(r); }
+          },
+          "and isStaged": {
+            topic: function() { db.isStaged('lloyd@somewhe.re', this.callback); },
+            "returns false": function(r) { assert.isFalse(r); }
+          }
         }
       }
     }
@@ -275,7 +302,10 @@ suite.addBatch({
 suite.addBatch({
   "removing an existing email": {
     topic: function() {
-      db.removeEmail("lloyd@somewhe.re", "lloyd@nowhe.re", this.callback);
+      var cb = this.callback;
+      db.emailToUID("lloyd@somewhe.re", function(uid) {
+        db.removeEmail(uid, "lloyd@nowhe.re", cb);
+      });
     },
     "returns no error": function(r) {
       assert.isUndefined(r);
@@ -321,7 +351,10 @@ suite.addBatch({
 suite.addBatch({
   "adding a primary email to that account": {
     topic: function() {
-      db.addPrimaryEmailToAccount("lloyd@primary.domain", "lloyd2@primary.domain", this.callback);
+      var cb = this.callback;
+      db.emailToUID('lloyd@primary.domain', function(uid) {
+        db.addPrimaryEmailToAccount(uid, "lloyd2@primary.domain", cb);
+      });
     },
     "returns no error": function(r) {
       assert.isUndefined(r);
@@ -345,7 +378,10 @@ suite.addBatch({
   },
   "adding a primary email to an account with only secondaries": {
     topic: function() {
-      db.addPrimaryEmailToAccount("lloyd@somewhe.re", "lloyd3@primary.domain", this.callback);
+      var cb = this.callback;
+      db.emailToUID('lloyd@somewhe.re', function(uid) {
+        db.addPrimaryEmailToAccount(uid, "lloyd3@primary.domain", cb);
+      });
     },
     "returns no error": function(r) {
       assert.isUndefined(r);
@@ -372,7 +408,10 @@ suite.addBatch({
 suite.addBatch({
   "adding a registered primary email to an account": {
     topic: function() {
-      db.addPrimaryEmailToAccount("lloyd@primary.domain", "lloyd3@primary.domain", this.callback);
+      var cb = this.callback;
+      db.emailToUID('lloyd@primary.domain', function(uid) {
+        db.addPrimaryEmailToAccount(uid, "lloyd3@primary.domain", cb);
+      });
     },
     "returns no error": function(r) {
       assert.isUndefined(r);
@@ -415,7 +454,10 @@ suite.addBatch({
 suite.addBatch({
   "canceling an account": {
     topic: function() {
-      db.cancelAccount("lloyd@somewhe.re", this.callback);
+      var cb = this.callback;
+      db.emailToUID("lloyd@somewhe.re", function(uid) {
+        db.cancelAccount(uid, cb);
+      });
     },
     "returns no error": function(r) {
       assert.isUndefined(r);