diff --git a/lib/primary.js b/lib/primary.js
index 67d8cf6dd66a4c1c04968a25a7c40a865a9a1c2d..aee2de492ff67ee70db326e1221f530e76f18b65 100644
--- a/lib/primary.js
+++ b/lib/primary.js
@@ -19,19 +19,53 @@ config = require("./configuration.js");
 
 const WELL_KNOWN_URL = "/.well-known/browserid";
 
+// Protect from stack overflows and network DDOS attacks
+const MAX_AUTHORITY_DELEGATIONS = 6;
+
 const HOSTNAME = urlparse(config.get('public_url')).host;
 
 var g_shim_cache = {};
 
-function parseWellKnownBody(body, domain) {
+// This becomes async
+function parseWellKnownBody(body, domain, delegates, cb) {
   var v = JSON.parse(body);
-
   const want = [ 'public-key', 'authentication', 'provisioning' ];
   var got = Object.keys(v);
-
+  var bail = false;
+  got.forEach(function (k) {
+    if ('authority' === k) {
+      // Recursion
+      var dels = Object.keys(delegates);
+      if (delegates[domain] !== undefined) {
+        // return to break out of function, but callbacks are actual program flow
+        bail = true;
+        return cb("Circular reference in delegating authority " + JSON.stringify(delegates));
+      }
+      if (Object.keys(delegates).length > MAX_AUTHORITY_DELEGATIONS) {
+        bail = true;
+        return cb("Too many hops while delegating authority " + JSON.stringify(dels));
+      }
+      logger.debug(domain,' is delegating to', v[k]);
+      // recurse into low level get /.well-known/browserid and parse again?
+      // If everything goes well, finally call our original callback
+      delegates[domain] = dels.length;
+      getWellKnown(v[k], delegates, function (err, nbody, ndomain, ndelegates) {
+        if (err) {
+          cb(err);
+        }
+        parseWellKnownBody(nbody, ndomain, ndelegates, cb);
+      });
+      bail = true;;
+    }
+  });
+  if (bail) return;
   want.forEach(function(k) {
-    if (-1 === got.indexOf(k)) throw "missing required key: " + k;
+    if (-1 === got.indexOf(k)) {
+      cb("missing required key: " + k);
+      bail = true;
+    }
   });
+  if (bail) return;
 
   // Allow SHIMMED_PRIMARIES to change example.com into 127.0.0.1:10005
   var url_prefix = 'https://' + domain;
@@ -49,25 +83,13 @@ function parseWellKnownBody(body, domain) {
   urlparse(urls.prov).validate();
 
   // parse the public key
-  return {
+  return cb(null, {
     publicKey: jwk.PublicKey.fromSimpleObject(v['public-key']),
     urls: urls
-  };
+  });
 }
 
-exports.checkSupport = function(domain, cb) {
-  if (!cb) throw "missing required callback function";
-
-  if (config.get('disable_primary_support')) {
-    return process.nextTick(function() { cb(null, false); });
-  }
 
-  if (typeof domain !== 'string' || !domain.length) {
-    return process.nextTick(function() { cb("invalid domain"); });
-  }
-
-  getWellKnown(domain, cb);
-};
 
 // Support "shimmed primaries" for local development.  That is an environment variable that is any number of
 // CSV values of the form:
@@ -93,9 +115,8 @@ if (process.env['SHIMMED_PRIMARIES']) {
   });
 }
 
-var getWellKnown = function (domain, cb) {
+var getWellKnown = function (domain, delegates, cb) {
   function handleResponse(res) {
-    var msg;
     if (res.statusCode !== 200) {
       logger.debug(domain + ' is not a browserid primary - non-200 response code to ' + WELL_KNOWN_URL);
       return cb(null, false, null);
@@ -108,22 +129,12 @@ var getWellKnown = function (domain, cb) {
     var body = "";
     res.on('data', function(chunk) { body += chunk; });
     res.on('end', function() {
-      try {
-        var r = parseWellKnownBody(body, domain);
-        logger.info(domain + ' is a valid browserid primary');
-        return cb(null, r.urls, r.publicKey);
-      } catch(e) {
-        msg = domain + ' is a broken browserid primary, malformed dec of support: ' + e.toString();
-        logger.debug(msg);
-        return cb(msg);
-      }
+      cb(null, body, domain, delegates);
     });
   };
 
   if (g_shim_cache[domain]) {
-    var body = g_shim_cache[domain].body,
-        r = parseWellKnownBody(body, domain);
-    return cb(null, r.urls, r.publicKey);
+    return cb(null, g_shim_cache[domain].body, domain, delegates);
   }
 
   // now we need to check to see if domain purports to being a primary
@@ -156,6 +167,48 @@ var getWellKnown = function (domain, cb) {
   });
 };
 
+exports.checkSupport = function(domain, cb, delegates) {
+
+  // Delegates will be populatd via recursion to detect cycles
+  if (! delegates) {
+    delegates = {};
+  }
+  if (!cb) throw "missing required callback function";
+
+  if (config.get('disable_primary_support')) {
+    return process.nextTick(function() { cb(null, false); });
+  }
+
+  if (typeof domain !== 'string' || !domain.length) {
+    return process.nextTick(function() { cb("invalid domain"); });
+  }
+  getWellKnown(domain, delegates, function (err, body, domain, delegates) {
+    if (err) {
+      logger.debug(err);
+      return cb(err);
+    } else {
+      try {
+        var r = parseWellKnownBody(body, domain, delegates, function (err, r) {
+          if (err) {
+            logger.debug(err);
+            cb(err);
+          } else {
+            logger.info(domain + ' is a valid browserid primary');
+            return cb(null, r.urls, r.publicKey);
+          }
+
+        });
+
+      } catch(e) {
+        var msg = domain + ' is a broken browserid primary, malformed dec of support: ' + e.toString();
+        logger.debug(msg);
+        return cb(msg);
+      }
+    }
+  });
+};
+
+
 exports.getPublicKey = function(domain, cb) {
   exports.checkSupport(domain, function(err, urls, publicKey) {
     if (publicKey === null) {
@@ -183,6 +236,7 @@ exports.verifyAssertion = function(assertion, cb) {
       if (issuer === HOSTNAME) {
         cb("cannot authenticate to browserid with a certificate issued by it.");
       } else {
+
         exports.getPublicKey(issuer, function(err, pubKey) {
           if (err) return cb(err);
           next(pubKey);
diff --git a/tests/data/cycle.domain/.well-known/browserid b/tests/data/cycle.domain/.well-known/browserid
new file mode 100644
index 0000000000000000000000000000000000000000..3c3c21814d2d95774f093bbad0186d9d4826a49c
--- /dev/null
+++ b/tests/data/cycle.domain/.well-known/browserid
@@ -0,0 +1 @@
+{ "authority": "cycle2.domain" }
diff --git a/tests/data/cycle2.domain/.well-known/browserid b/tests/data/cycle2.domain/.well-known/browserid
new file mode 100644
index 0000000000000000000000000000000000000000..22334d71fb7dd8cd2d6552612031c4b3ebae4361
--- /dev/null
+++ b/tests/data/cycle2.domain/.well-known/browserid
@@ -0,0 +1 @@
+{ "authority": "cycle.domain" }
\ No newline at end of file
diff --git a/tests/data/delegate0.domain/.well-known/browserid b/tests/data/delegate0.domain/.well-known/browserid
new file mode 100644
index 0000000000000000000000000000000000000000..d12273097e4a22993026cee7cfa2cbfefaa32a42
--- /dev/null
+++ b/tests/data/delegate0.domain/.well-known/browserid
@@ -0,0 +1 @@
+{ "authority": "delegate1.domain" }
diff --git a/tests/data/delegate1.domain/.well-known/browserid b/tests/data/delegate1.domain/.well-known/browserid
new file mode 100644
index 0000000000000000000000000000000000000000..f5af2e8e81a0f1584f587f7f312263b661c26843
--- /dev/null
+++ b/tests/data/delegate1.domain/.well-known/browserid
@@ -0,0 +1 @@
+{ "authority": "delegate2.domain" }
diff --git a/tests/data/delegate10.domain/.well-known/browserid b/tests/data/delegate10.domain/.well-known/browserid
new file mode 100644
index 0000000000000000000000000000000000000000..88c678cea1948dbba0dc7b505d8263d9b008158f
--- /dev/null
+++ b/tests/data/delegate10.domain/.well-known/browserid
@@ -0,0 +1 @@
+{ "authority": "delegate11.domain" }
diff --git a/tests/data/delegate2.domain/.well-known/browserid b/tests/data/delegate2.domain/.well-known/browserid
new file mode 100644
index 0000000000000000000000000000000000000000..a526a7552d37c4ca6e3b05d3fd21f3a14c8c3ce9
--- /dev/null
+++ b/tests/data/delegate2.domain/.well-known/browserid
@@ -0,0 +1 @@
+{ "authority": "delegate3.domain" }
diff --git a/tests/data/delegate3.domain/.well-known/browserid b/tests/data/delegate3.domain/.well-known/browserid
new file mode 100644
index 0000000000000000000000000000000000000000..41e59a5b1058520dc7ab6176fc325940b2f5cfc7
--- /dev/null
+++ b/tests/data/delegate3.domain/.well-known/browserid
@@ -0,0 +1 @@
+{ "authority": "delegate4.domain" }
diff --git a/tests/data/delegate4.domain/.well-known/browserid b/tests/data/delegate4.domain/.well-known/browserid
new file mode 100644
index 0000000000000000000000000000000000000000..c43664eefb8c8666a77a5476e0df8f42d9d2bfc0
--- /dev/null
+++ b/tests/data/delegate4.domain/.well-known/browserid
@@ -0,0 +1 @@
+{ "authority": "delegate5.domain" }
diff --git a/tests/data/delegate5.domain/.well-known/browserid b/tests/data/delegate5.domain/.well-known/browserid
new file mode 100644
index 0000000000000000000000000000000000000000..71ea103e56726f9b9cc284edc742919491002c76
--- /dev/null
+++ b/tests/data/delegate5.domain/.well-known/browserid
@@ -0,0 +1 @@
+{ "authority": "delegate6.domain" }
diff --git a/tests/data/delegate6.domain/.well-known/browserid b/tests/data/delegate6.domain/.well-known/browserid
new file mode 100644
index 0000000000000000000000000000000000000000..1193daef7d0c4b8d69c4366ef039cea7e25fc674
--- /dev/null
+++ b/tests/data/delegate6.domain/.well-known/browserid
@@ -0,0 +1 @@
+{ "authority": "delegate7.domain" }
diff --git a/tests/data/delegate7.domain/.well-known/browserid b/tests/data/delegate7.domain/.well-known/browserid
new file mode 100644
index 0000000000000000000000000000000000000000..811565beed12687518288b44f14188902cbb9791
--- /dev/null
+++ b/tests/data/delegate7.domain/.well-known/browserid
@@ -0,0 +1 @@
+{ "authority": "delegate8.domain" }
diff --git a/tests/data/delegate8.domain/.well-known/browserid b/tests/data/delegate8.domain/.well-known/browserid
new file mode 100644
index 0000000000000000000000000000000000000000..78e2041bb5f96ad97c65a034fbcc4c27962961a1
--- /dev/null
+++ b/tests/data/delegate8.domain/.well-known/browserid
@@ -0,0 +1 @@
+{ "authority": "delegate9.domain" }
diff --git a/tests/data/delegate9.domain/.well-known/browserid b/tests/data/delegate9.domain/.well-known/browserid
new file mode 100644
index 0000000000000000000000000000000000000000..8189ff31c984a8cbca1a69bbe6c8ffc9f59a1a96
--- /dev/null
+++ b/tests/data/delegate9.domain/.well-known/browserid
@@ -0,0 +1 @@
+{ "authority": "delegate10.domain" }
diff --git a/tests/data/hozed.domain/.well-known/browserid b/tests/data/hozed.domain/.well-known/browserid
new file mode 100644
index 0000000000000000000000000000000000000000..936d032fc0b4c3da6b6a053bcf775b744dbe22c7
--- /dev/null
+++ b/tests/data/hozed.domain/.well-known/browserid
@@ -0,0 +1 @@
+{ "authority": "hozed.domain" }
diff --git a/tests/delegated-primary-test.js b/tests/delegated-primary-test.js
new file mode 100644
index 0000000000000000000000000000000000000000..a9a286a231c8866d6a9ff8cc2f4a5acfbe6a0b22
--- /dev/null
+++ b/tests/delegated-primary-test.js
@@ -0,0 +1,123 @@
+require('./lib/test_env.js');
+
+const
+assert = require('assert'),
+vows = require('vows'),
+path = require('path'),
+util = require('util');
+
+const TEST_DOMAIN = 'example.domain',
+      TEST_DOMAIN_PATH = path.join(__dirname,
+        '..', 'example', 'primary', '.well-known', 'browserid'),
+      TEST_ORIGIN = 'http://127.0.0.1:10002',
+      TEST_DELEGATE_DOMAIN = 'delegate.example.domain',
+      TEST_DELEGATE_DOMAIN_PATH = path.join(__dirname,
+        '..', 'example', 'delegated_primary', '.well-known', 'browserid');
+
+// Good examples
+process.env['SHIMMED_PRIMARIES'] =
+  'example.domain|http://127.0.0.1:10005|' + TEST_DOMAIN_PATH;
+process.env['SHIMMED_PRIMARIES'] += "," +
+  'delegate.example.domain|http://127.0.0.1:10005|' + TEST_DELEGATE_DOMAIN_PATH;
+
+// A series of redirects delegate0.domain -> delegate1.domain -> ... delegate11.domain
+function mk_delegate(i) {
+  var f = util.format;
+  var p = path.join(__dirname, 'data', f('delegate%s.domain', i), '.well-known', 'browserid');
+  process.env['SHIMMED_PRIMARIES'] += "," +
+  f("delegate%s.domain|http://127.0.0.1:10005|%s", i, p);
+}
+for (var i=0; i <= 10; i++) {
+  mk_delegate(i);
+}
+
+// delegates to hozed.domain
+process.env['SHIMMED_PRIMARIES'] += "," +
+  util.format("hozed.domain|http://127.0.0.1:10005|%s", path.join(__dirname, 'data',
+    'hozed.domain', '.well-known', 'browserid'));
+
+// Next two delegate to each other forming a cycle
+process.env['SHIMMED_PRIMARIES'] += "," +
+  util.format("cycle.domain|http://127.0.0.1:10005|%s", path.join(__dirname, 'data',
+    'cycle.domain', '.well-known', 'browserid'));
+
+process.env['SHIMMED_PRIMARIES'] += "," +
+  util.format("cycle2.domain|http://127.0.0.1:10005|%s", path.join(__dirname, 'data',
+    'cycle2.domain', '.well-known', 'browserid'));
+
+
+var primary = require('../lib/primary.js');
+
+var suite = vows.describe('delegated-primary');
+
+// DB test look
+
+// Tests related to domains that delegate their authority to another
+// primary.
+
+// now let's generate an assertion using this user
+
+suite.addBatch({
+  "Retrieving a public key is straight forward": {
+    topic: function() {
+      return primary.getPublicKey(TEST_DOMAIN, this.callback);
+    },
+    "succeeds": function(err, pubKey) {
+      assert.equal(pubKey.keysize, '256');
+      assert.equal(pubKey.algorithm, 'RS');
+    }
+  }
+});
+
+suite.addBatch({
+  "Retrieving a public key should follow authority delegation": {
+    topic: function() {
+      return primary.getPublicKey(TEST_DELEGATE_DOMAIN, this.callback);
+    },
+    "succeeds": function(err, pubKey) {
+      assert.equal(pubKey.keysize, '256');
+      assert.equal(pubKey.algorithm, 'RS');
+    }
+  }
+});
+
+suite.addBatch({
+  "Cycles should be detected": {
+    topic: function() {
+      return primary.getPublicKey('cycle.domain', this.callback);
+    },
+    "succeeds": function(err, pubKey) {
+      assert.strictEqual(err,
+        'Circular reference in delegating authority {"cycle.domain":0,"cycle2.domain":1}');
+    }
+  }
+});
+
+suite.addBatch({
+  "We should not follow an infinite series of delegations of authority": {
+    topic: function() {
+      return primary.getPublicKey('delegate0.domain', this.callback);
+    },
+    "succeeds": function(err, pubKey) {
+      assert.strictEqual(err,
+        'Too many hops while delegating authority ["delegate0.domain","delegate1.domain",' +
+        '"delegate2.domain","delegate3.domain","delegate4.domain","delegate5.domain",' +
+        '"delegate6.domain"]');
+    }
+  }
+});
+
+suite.addBatch({
+  "A domain delegating to itself is hozed...": {
+    topic: function() {
+      return primary.getPublicKey('hozed.domain', this.callback);
+    },
+    "succeeds": function(err, pubKey) {
+      assert.strictEqual(err.indexOf('Circular reference in delegating authority '), 0);
+    }
+  }
+});
+
+// run or export the suite.
+if (process.argv[1] === __filename) suite.run();
+else suite.export(module);