diff --git a/lib/db.js b/lib/db.js
index 3521c2ccc4cffd5cbaaf9e75312f1d0debdb0008..7caf732f50790814dd69b15684ffbbab5df8ebd1 100644
--- a/lib/db.js
+++ b/lib/db.js
@@ -131,7 +131,8 @@ exports.onReady = function(f) {
   'removeEmail',
   'cancelAccount',
   'updatePassword',
-  'createUserWithPrimaryEmail'
+  'createUserWithPrimaryEmail',
+  'addPrimaryEmailToAccount'
 ].forEach(function(fn) {
   exports[fn] = function() {
     if (!config.get('database').may_write) {
diff --git a/lib/db/json.js b/lib/db/json.js
index 4af6a54536730b1eb8d1291dcdd9b64b1af0e6f3..9d0133880c31a01a545ab9dbac3b4047866598f3 100644
--- a/lib/db/json.js
+++ b/lib/db/json.js
@@ -166,14 +166,19 @@ exports.emailsBelongToSameAccount = function(lhs, rhs, cb) {
   });
 };
 
-function addEmailToAccount(existing_email, email, cb) {
+function addEmailToAccount(existing_email, email, type, cb) {
+  // validate 'type' isn't bogus
+  if ([ 'secondary', 'primary' ].indexOf(type) === -1) {
+    return process.nextTick(function() {
+      cb("invalid type");
+    });
+  }
+
   emailToUserID(existing_email, function(userID) {
     if (userID == undefined) {
       cb("no such email: " + existing_email, undefined);
     } else {
-      db.users[userID].emails[email] = {
-        type: 'secondary'
-      };
+      db.users[userID].emails[email] = { type: type };
       flush();
       cb();
     }
@@ -280,7 +285,7 @@ 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, cb);
+        addEmailToAccount(o.existing_email, o.email, 'secondary', cb);
       }
       if (known) {
         exports.removeEmail(o.email, o.email, function (err) {
@@ -296,6 +301,24 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
   }
 };
 
+exports.addPrimaryEmailToAccount = function(acctEmail, emailToAdd, cb) {
+  sync();
+
+  exports.emailKnown(emailToAdd, function(known) {
+    function addIt() {
+      addEmailToAccount(acctEmail, emailToAdd, 'primary', cb);
+    }
+    if (known) {
+      exports.removeEmail(emailToAdd, emailToAdd, function (err) {
+        if (err) cb(err);
+        else addIt();
+      });
+    } else {
+      addIt();
+    }
+  });
+};
+
 exports.checkAuth = function(email, cb) {
   sync();
   var m = jsel.match(":root > object:has(.emails > ." + ESC(email) + ") > .password", db.users);
diff --git a/lib/db/mysql.js b/lib/db/mysql.js
index 7c46cd64b795aa25a9e442ccc2f6c4a7adf0065d..660de0b5afda46a7920162963c7174fdf215020a 100644
--- a/lib/db/mysql.js
+++ b/lib/db/mysql.js
@@ -298,6 +298,29 @@ exports.verificationSecretForEmail = function(email, cb) {
     });
 };
 
+function addEmailToUser(userID, email, type, cb) {
+  // issue #170 - delete any old records with the same
+  // email address.  this is necessary because
+  // gotVerificationSecret is invoked both for
+  // forgotten password flows and for new user signups.
+  client.query(
+    "DELETE FROM email WHERE address = ?",
+    [ email ],
+    function(err, info) {
+      if (err) { logUnexpectedError(err); cb(err); return; }
+      else {
+        client.query(
+          "INSERT INTO email(user, address, type) VALUES(?, ?, ?)",
+          [ userID, email, type ],
+          function(err, info) {
+            if (err) logUnexpectedError(err);
+            cb(err ? err : undefined, email);
+          });
+      }
+    });
+}
+
+
 exports.gotVerificationSecret = function(secret, hash, cb) {
   client.query(
     "SELECT * FROM staged WHERE secret = ?", [ secret ],
@@ -309,30 +332,6 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
       else {
         var o = rows[0];
 
-        function addEmailToUser(userID) {
-          // issue #170 - delete any old records with the same
-          // email address.  this is necessary because
-          // gotVerificationSecret is invoked both for
-          // forgotten password flows and for new user signups.
-          // We could add an `ON DUPLICATE KEY` clause, however
-          // We actually want to invalidate all old public keys.
-          client.query(
-            "DELETE FROM email WHERE address = ?",
-            [ o.email ],
-            function(err, info) {
-              if (err) { logUnexpectedError(err); cb(err); return; }
-              else {
-                client.query(
-                  "INSERT INTO email(user, address) VALUES(?, ?)",
-                  [ userID, o.email ],
-                  function(err, info) {
-                    if (err) logUnexpectedError(err);
-                    cb(err ? err : undefined, o.email);
-                  });
-              }
-            });
-        }
-
         // delete the record
         client.query("DELETE LOW_PRIORITY FROM staged WHERE secret = ?", [ secret ]);
 
@@ -343,7 +342,7 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
             [ hash ],
             function(err, info) {
               if (err) { logUnexpectedError(err); cb(err); return; }
-              addEmailToUser(info.insertId);
+              addEmailToUser(info.insertId, o.email, 'secondary', cb);
             });
         } else {
           // we're adding an email address to an existing user account.  add appropriate entries into
@@ -354,7 +353,7 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
               if (err) { logUnexpectedError(err); cb(err); }
               else if (rows.length === 0) cb("cannot find email address: " + o.existing);
               else {
-                addEmailToUser(rows[0].user);
+                addEmailToUser(rows[0].user, o.email, 'secondary', cb);
               }
             });
         }
@@ -363,6 +362,20 @@ exports.gotVerificationSecret = function(secret, hash, cb) {
   );
 }
 
+exports.addPrimaryEmailToAccount = function(acctEmail, emailToAdd, cb) {
+  // we're adding an email address to an existing user account.  add appropriate entries into
+  // email table
+  client.query(
+    "SELECT user FROM email WHERE address = ?", [ acctEmail ],
+    function(err, rows) {
+      if (err) { logUnexpectedError(err); cb(err); }
+      else if (rows.length === 0) cb("cannot find email address: " + acctEmail);
+      else {
+        addEmailToUser(rows[0].user, emailToAdd, 'primary', cb);
+      }
+    });
+}
+
 exports.createUserWithPrimaryEmail = function(email, cb) {
   // create a new user acct with no password
   client.query(
diff --git a/tests/db-test.js b/tests/db-test.js
index f917c67b7622ded99c930948f712956eae033194..f9ae288704b614a7b0c6a7b5cc79c1c22ca91cb6 100755
--- a/tests/db-test.js
+++ b/tests/db-test.js
@@ -245,7 +245,6 @@ suite.addBatch({
   }
 });
 
-
 suite.addBatch({
   "emailType of lloyd@anywhe.re": {
     topic: function() {
@@ -293,35 +292,43 @@ suite.addBatch({
 });
 
 suite.addBatch({
-  "canceling an account": {
+  "creating a primary account": {
     topic: function() {
-      db.cancelAccount("lloyd@somewhe.re", this.callback);
+      db.createUserWithPrimaryEmail("lloyd@primary.domain", this.callback);
     },
     "returns no error": function(r) {
       assert.isUndefined(r);
     },
     "causes emailKnown": {
       topic: function() {
-        db.emailKnown('lloyd@somewhe.re', this.callback);
+        db.emailKnown('lloyd@primary.domain', this.callback);
       },
-      "to return false": function (r) {
-        assert.strictEqual(r, false);
+      "to return true": function (r) {
+        assert.strictEqual(r, true);
+      }
+    },
+    "causes emailType": {
+      topic: function() {
+        db.emailType('lloyd@primary.domain', this.callback);
+      },
+      "to return 'primary'": function (r) {
+        assert.strictEqual(r, 'primary');
       }
     }
   }
 });
 
 suite.addBatch({
-  "creating a primary account": {
+  "adding a primary email to that account": {
     topic: function() {
-      db.createUserWithPrimaryEmail("lloyd@primary.domain", this.callback);
+      db.addPrimaryEmailToAccount("lloyd@primary.domain", "lloyd2@primary.domain", this.callback);
     },
     "returns no error": function(r) {
       assert.isUndefined(r);
     },
     "causes emailKnown": {
       topic: function() {
-        db.emailKnown('lloyd@primary.domain', this.callback);
+        db.emailKnown('lloyd2@primary.domain', this.callback);
       },
       "to return true": function (r) {
         assert.strictEqual(r, true);
@@ -335,6 +342,92 @@ suite.addBatch({
         assert.strictEqual(r, 'primary');
       }
     }
+  },
+  "adding a primary email to an account with only secondaries": {
+    topic: function() {
+      db.addPrimaryEmailToAccount("lloyd@somewhe.re", "lloyd3@primary.domain", this.callback);
+    },
+    "returns no error": function(r) {
+      assert.isUndefined(r);
+    },
+    "causes emailKnown": {
+      topic: function() {
+        db.emailKnown('lloyd3@primary.domain', this.callback);
+      },
+      "to return true": function (r) {
+        assert.strictEqual(r, true);
+      }
+    },
+    "causes emailType": {
+      topic: function() {
+        db.emailType('lloyd3@primary.domain', this.callback);
+      },
+      "to return 'primary'": function (r) {
+        assert.strictEqual(r, 'primary');
+      }
+    }
+  }
+});
+
+suite.addBatch({
+  "adding a registered primary email to an account": {
+    topic: function() {
+      db.addPrimaryEmailToAccount("lloyd@primary.domain", "lloyd3@primary.domain", this.callback);
+    },
+    "returns no error": function(r) {
+      assert.isUndefined(r);
+    },
+    "and emailKnown": {
+      topic: function() {
+        db.emailKnown('lloyd3@primary.domain', this.callback);
+      },
+      "still returns true": function (r) {
+        assert.strictEqual(r, true);
+      }
+    },
+    "and emailType": {
+      topic: function() {
+        db.emailType('lloyd@primary.domain', this.callback);
+      },
+      "still returns 'primary'": function (r) {
+        assert.strictEqual(r, 'primary');
+      }
+    },
+    "and email is removed": {
+      topic: function() {
+        db.emailsBelongToSameAccount('lloyd3@primary.domain', 'lloyd@somewhe.re', this.callback);
+      },
+      "from original account": function(r) {
+        assert.isFalse(r);
+      }
+    },
+    "and email is added": {
+      topic: function() {
+        db.emailsBelongToSameAccount('lloyd3@primary.domain', 'lloyd@primary.domain', this.callback);
+      },
+      "to new account": function(r) {
+        assert.isTrue(r);
+      }
+    }
+  }
+});
+
+suite.addBatch({
+  "canceling an account": {
+    topic: function() {
+      db.cancelAccount("lloyd@somewhe.re", this.callback);
+    },
+    "returns no error": function(r) {
+      assert.isUndefined(r);
+    },
+    "causes emailKnown": {
+      topic: function() {
+        db.emailKnown('lloyd@somewhe.re', this.callback);
+      },
+      "to return false": function (r) {
+        assert.strictEqual(r, false);
+      }
+    }
   }
 });