diff --git a/resources/static/common/css/style.css b/resources/static/common/css/style.css
index c29a95bffb2b1b82c387529fbe41b5b656a71e2f..dfd8cd6d3bfbeb86b3bf7b3814a612276db5b020 100644
--- a/resources/static/common/css/style.css
+++ b/resources/static/common/css/style.css
@@ -415,6 +415,20 @@ footer .help {
   color: #333;
 }
 
+.submit {
+    margin-top: 10px;
+    line-height: 14px;
+}
+
+.submit > p {
+  margin-top: 10px;
+}
+
+.tospp {
+  line-height: 1.2;
+}
+
+
 #showDevelopment {
   position: absolute;
   top: 0;
diff --git a/resources/static/common/js/lib/dom-jquery.js b/resources/static/common/js/lib/dom-jquery.js
index 6438fec068304658384f58d1e86e2c0c1c309dcb..0faeb1ccc0db906754e3461716bbfd2b8c3bd1dc 100644
--- a/resources/static/common/js/lib/dom-jquery.js
+++ b/resources/static/common/js/lib/dom-jquery.js
@@ -313,7 +313,7 @@ BrowserID.DOM = ( function() {
         },
 
         /**
-         * Show an element/elements
+         * Show an element
          * @method show
          * @param {selector || element} elementToShow
          */
@@ -322,15 +322,13 @@ BrowserID.DOM = ( function() {
         },
 
         /**
-         * Hide an element/elements
+         * Hide an element
          * @method hide
          * @param {selector || element} elementToHide
          */
         hide: function( elementToHide ) {
           return jQuery( elementToHide ).hide();
         }
-
-
     };
 
     function isValBased( target ) {
diff --git a/resources/static/common/js/modules/page_module.js b/resources/static/common/js/modules/page_module.js
index 36aa78c7c29a72aae98d2d6552b08100467c54b2..2afa4634e9e26725e67933e5c2ab0cd20148162e 100644
--- a/resources/static/common/js/modules/page_module.js
+++ b/resources/static/common/js/modules/page_module.js
@@ -119,6 +119,8 @@ BrowserID.Modules.PageModule = (function() {
       self.hideError();
       self.hideDelay();
 
+      dom.removeClass("body", "rptospp");
+
       screens.form.show(template, data);
       dom.focus("input:visible:not(:disabled):eq(0)");
       // XXX jQuery.  bleck.
diff --git a/resources/static/common/js/user.js b/resources/static/common/js/user.js
index f39d3935d9bdae5f9bcdb3cf966f1212769c2318..95b7c0893c34fd7d36fab3942fa6bc589a6ab36f 100644
--- a/resources/static/common/js/user.js
+++ b/resources/static/common/js/user.js
@@ -264,6 +264,14 @@ BrowserID.User = (function() {
       return origin;
     },
 
+    setOriginEmail: function(email) {
+      storage.site.set(origin, "email", email);
+    },
+
+    getOriginEmail: function() {
+      return storage.site.get(origin, "email");
+    },
+
     /**
      * Get the hostname for the set origin
      * @method getHostname
diff --git a/resources/static/dialog/css/style.css b/resources/static/dialog/css/style.css
index 5edef21780867c23f7855a6b5981cb36fc3e1cd7..fc402c0884959919c6f8c50a1913755e6d201f11 100644
--- a/resources/static/dialog/css/style.css
+++ b/resources/static/dialog/css/style.css
@@ -284,24 +284,24 @@ section > .contents {
 #favicon img {
     display: block;
     margin: 0 auto;
-    max-height: 128px;
-    max-width: 128px;
+    max-height: 100px;
+    max-width: 100px;
 }
 
 #favicon h2, #favicon h3 {
     white-space: nowrap;
     text-overflow: ellipsis;
-    line-height: 1.3;  /* the 1.3em is to keep y, g, j, etc from having their bottoms chopped off */
+    line-height: 1.2em;  /* the 1.2em is to keep y, g, j, etc from having their bottoms chopped off */
     overflow: hidden;
+    margin: 10px 0 0 0;
 }
 
 #favicon h2 {
-    margin: 10px 0 0 0;
+    font-size: 28px;
 }
 
 #favicon h3 {
-    font-size: 19px;
-    margin-top: 0;
+    font-size: 14px;
 }
 
 #favicon .vertical {
@@ -379,19 +379,24 @@ div#required_email {
 }
 
 .submit {
-    margin-top: 10px;
     color: #333;
     font-size: 11px;
-    line-height: 14px;
 }
 
-.submit > p {
-  margin-top: 10px;
-}
 
 .tospp {
-    color: #787878;
-    clear: right;
+  font-size: 11px;
+  color: #787878;
+}
+
+#rptospp {
+  display: none;
+  margin: 10px auto;
+  max-width: 280px;
+}
+
+.rptospp #rptospp {
+  display: block;
 }
 
 a.emphasize {
@@ -430,6 +435,7 @@ a.emphasize:active {
 #back {
   color: #000;
   border-bottom: 1px dotted;
+  font-weight: normal;
 }
 
 .submit > button {
diff --git a/resources/static/dialog/js/misc/helpers.js b/resources/static/dialog/js/misc/helpers.js
index af595a5338e5dba843f45572329d1c648496daf7..23831652fef2a4e8c5807a1f89b2d7b354faf4da 100644
--- a/resources/static/dialog/js/misc/helpers.js
+++ b/resources/static/dialog/js/misc/helpers.js
@@ -11,7 +11,8 @@
       complete = helpers.complete,
       user = bid.User,
       tooltip = bid.Tooltip,
-      errors = bid.Errors;
+      errors = bid.Errors,
+      dom = bid.DOM;
 
   function animateClose(callback) {
     var body = $("body"),
@@ -135,6 +136,10 @@
     }, self.getErrorDialog(errors.addEmail, callback));
   }
 
+  function showRPTosPP() {
+    dom.addClass("body", "rptospp");
+  }
+
   helpers.Dialog = helpers.Dialog || {};
 
   helpers.extend(helpers.Dialog, {
@@ -145,7 +150,8 @@
     addSecondaryEmail: addSecondaryEmail,
     resetPassword: resetPassword,
     cancelEvent: helpers.cancelEvent,
-    animateClose: animateClose
+    animateClose: animateClose,
+    showRPTosPP: showRPTosPP
   });
 
 }());
diff --git a/resources/static/dialog/js/misc/state.js b/resources/static/dialog/js/misc/state.js
index 032bd85f683c1f9a9698fcecf87af9a9ad2c5af5..dc228b145d1713516c67e615e9d8307266943cf7 100644
--- a/resources/static/dialog/js/misc/state.js
+++ b/resources/static/dialog/js/misc/state.js
@@ -1,9 +1,11 @@
-/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*jshint browser:true, jquery: true, forin: true, laxbreak:true */
 /*global BrowserID: true */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 BrowserID.State = (function() {
+  "use strict";
+
   var bid = BrowserID,
       storage = bid.Storage,
       network = bid.Network,
@@ -42,8 +44,7 @@ BrowserID.State = (function() {
 
     handleState("start", function(msg, info) {
       self.hostname = info.hostname;
-      self.privacyURL = info.privacyURL;
-      self.tosURL = info.tosURL;
+      self.siteTOSPP = !!(info.privacyPolicy && info.termsOfService);
       requiredEmail = info.requiredEmail;
 
       startAction(false, "doRPInfo", info);
@@ -80,8 +81,10 @@ BrowserID.State = (function() {
         self.email = requiredEmail;
         startAction("doAuthenticateWithRequiredEmail", {
           email: requiredEmail,
-          privacyURL: self.privacyURL,
-          tosURL: self.tosURL
+          // New users are handled by either the "new_user" flow or the
+          // "primary_user" flow. The Persona TOS/PP will be shown to users in
+          // these stages.
+          siteTOSPP: self.siteTOSPP && !user.getOriginEmail()
         });
       }
       else if (authenticated) {
@@ -92,8 +95,7 @@ BrowserID.State = (function() {
     });
 
     handleState("authenticate", function(msg, info) {
-      info.privacyURL = self.privacyURL;
-      info.tosURL = self.tosURL;
+      info.siteTOSPP = self.siteTOSPP;
       startAction("doAuthenticate", info);
     });
 
@@ -104,9 +106,23 @@ BrowserID.State = (function() {
       // know when we are losing users due to the email verification.
       mediator.publish("kpi_data", { new_account: true });
 
-      // cancel is disabled if the user is doing the initial password set
-      // for a requiredEmail.
-      info.cancelable = !requiredEmail;
+      _.extend(info, {
+        // cancel is disabled if the user is doing the initial password set
+        // for a requiredEmail.
+        cancelable: !requiredEmail,
+
+        // New users in the requiredEmail flow are sent directly to the
+        // set_password screen.  If this happens, they have not yet seen the
+        // TOS/PP agreement.
+
+        // Always show the Persona TOS/PP to new requiredEmail users.
+        personaTOSPP: !!requiredEmail,
+
+        // The site TOS/PP must be shown to new requiredEmail users if there is
+        // a site TOS/PP
+        siteTOSPP: !!requiredEmail && self.siteTOSPP
+      });
+
       startAction(false, "doSetPassword", info);
     });
 
@@ -176,12 +192,24 @@ BrowserID.State = (function() {
     });
 
     handleState("primary_user_unauthenticated", function(msg, info) {
-      info = helpers.extend(info, {
+      // a user who lands here is not authenticated with their identity
+      // provider.
+      _.extend(info, {
         add: !!addPrimaryUser,
         email: email,
         requiredEmail: !!requiredEmail,
-        privacyURL: self.privacyURL,
-        tosURL: self.tosURL
+
+        // In the requiredEmail flow, a user who is not authenticated with
+        // their primary will be sent directly to the "you must verify
+        // with your IdP" screen.
+        //
+        // Show the siteTOSPP to all requiredEmail users who have never visited
+        // the site before.
+        siteTOSPP: requiredEmail && self.siteTOSPP && !user.getOriginEmail(),
+
+        // Show the persona TOS/PP only to requiredEmail users who are creating
+        // a new account.
+        personaTOSPP: requiredEmail && !addPrimaryUser
       });
 
       if (primaryVerificationInfo) {
@@ -218,8 +246,7 @@ BrowserID.State = (function() {
     handleState("pick_email", function() {
       startAction("doPickEmail", {
         origin: self.hostname,
-        privacyURL: self.privacyURL,
-        tosURL: self.tosURL
+        siteTOSPP: self.siteTOSPP && !user.getOriginEmail()
       });
     });
 
@@ -260,8 +287,12 @@ BrowserID.State = (function() {
               startAction("doAuthenticateWithRequiredEmail", {
                 email: email,
                 secondary_auth: true,
-                privacyURL: self.privacyURL,
-                tosURL: self.tosURL
+
+                // This is a user is already authenticated to the assertion
+                // level who has chosen a secondary email address from the
+                // pick_email screen. They would have been shown the
+                // siteTOSPP there.
+                siteTOSPP: false
               });
             }
             else {
@@ -359,12 +390,26 @@ BrowserID.State = (function() {
       redirectToState("email_chosen", info);
     });
 
-    handleState("add_email", function(msg, info) {
-      info = helpers.extend(info, {
-        privacyURL: self.privacyURL,
-        tosURL: self.tosURL
-      });
+    handleState("reset_password", function(msg, info) {
+      info = info || {};
+      // reset_password says the user has confirmed that they want to
+      // reset their password.  doResetPassword will attempt to invoke
+      // the create_user wsapi.  If the wsapi call is successful,
+      // the user will be shown the "go verify your account" message.
+
+      // We have to save the staged email address here for when the user
+      // verifies their account and user_confirmed is called.
+      self.stagedEmail = info.email;
+      startAction(false, "doResetPassword", info);
+    });
 
+    handleState("add_email", function(msg, info) {
+      // add_email indicates the user wishes to add an email to the account,
+      // the add_email screen must be displayed.  After the user enters the
+      // email address they wish to add, add_email will trigger
+      // either 1) primary_user or 2) email_staged. #1 occurs if the email
+      // address is a primary address, #2 occurs if the address is a secondary
+      // and the verification email has been sent.
       startAction("doAddEmail", info);
     });
 
@@ -372,9 +417,26 @@ BrowserID.State = (function() {
       user.passwordNeededToAddSecondaryEmail(function(passwordNeeded) {
         if(passwordNeeded) {
           self.addEmailEmail = info.email;
-          // cancel is disabled if the user is doing the initial password set
-          // for a requiredEmail.
-          info.cancelable = !requiredEmail;
+
+          _.extend(info, {
+            // cancel is disabled if the user is doing the initial password set
+            // for a requiredEmail.
+            cancelable: !requiredEmail,
+
+            // stage_email is called to add an email to an already existing
+            // account.  Since it is an already existing account, the
+            // personaTOSPP does not need to be shown.
+            personaTOSPP: false,
+
+            // requiredEmail users who are adding an email but must set their
+            // password will be redirected here without seeing any other
+            // screens. non-requiredEmail users will have already seen the site
+            // TOS/PP in the pick-email screen if it was necessary.  Since
+            // requiredEmail users may not have seen the screen before, show it
+            // here if there is no originEmail.
+            siteTOSPP: self.siteTOSPP && requiredEmail && !user.getOriginEmail()
+          });
+
           startAction(false, "doSetPassword", info);
         }
         else {
diff --git a/resources/static/dialog/js/modules/actions.js b/resources/static/dialog/js/modules/actions.js
index 3a95c89155d98f12c71b9f8eb11231f0a1f82bfe..9144b37521a54ad9dae4245493476addd06cde77 100644
--- a/resources/static/dialog/js/modules/actions.js
+++ b/resources/static/dialog/js/modules/actions.js
@@ -57,10 +57,6 @@ BrowserID.Modules.Actions = (function() {
       if(data.ready) _.defer(data.ready);
     },
 
-    doRPInfo: function(info) {
-      startService("rp_info", info);
-    },
-
     doCancel: function() {
       if(onsuccess) onsuccess(null);
     },
@@ -156,6 +152,10 @@ BrowserID.Modules.Actions = (function() {
 
     doGenerateAssertion: function(info) {
       startService("generate_assertion", info);
+    },
+
+    doRPInfo: function(info) {
+      startService("rp_info", info);
     }
   });
 
diff --git a/resources/static/dialog/js/modules/add_email.js b/resources/static/dialog/js/modules/add_email.js
index da642e0d2ced87d3cdf769b0d2389e0b2ad46e9a..07f6242caf302bed2cb3aaa49ee7416522ffb811 100644
--- a/resources/static/dialog/js/modules/add_email.js
+++ b/resources/static/dialog/js/modules/add_email.js
@@ -1,5 +1,5 @@
 /*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
-/*global _: true, BrowserID: true, PageController: true */
+/*global _: true, BrowserID: true, PageController: true, gettext: true */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -9,6 +9,7 @@ BrowserID.Modules.AddEmail = (function() {
   var bid = BrowserID,
       dom = bid.DOM,
       helpers = bid.Helpers,
+      user = bid.User,
       dialogHelpers = helpers.Dialog,
       errors = bid.Errors,
       complete = helpers.complete,
@@ -54,12 +55,9 @@ BrowserID.Modules.AddEmail = (function() {
   var Module = bid.Modules.PageModule.extend({
     start: function(options) {
       var self=this,
-          templateData = helpers.extend({}, options, {
-            privacy_url: options.privacyURL || null,
-            tos_url: options.tosURL || null
-          });
+          originEmail = user.getOriginEmail();
 
-      self.renderDialog("add_email", templateData);
+      self.renderDialog("add_email", options);
       hideHint("addressInfo");
 
       self.click("#cancel", cancelAddEmail);
diff --git a/resources/static/dialog/js/modules/authenticate.js b/resources/static/dialog/js/modules/authenticate.js
index c316e55a5e5822825c2ad2bb69f1b32a6bf106fa..d1c13818901917c5ae93120aa100ce0cc6e579c8 100644
--- a/resources/static/dialog/js/modules/authenticate.js
+++ b/resources/static/dialog/js/modules/authenticate.js
@@ -33,6 +33,7 @@ BrowserID.Modules.Authenticate = (function() {
     }
     else {
       showHint("start");
+      enterEmailState.call(self);
       complete(info.ready);
     }
   }
@@ -98,7 +99,7 @@ BrowserID.Modules.Authenticate = (function() {
   function showHint(showSelector, callback) {
     _.each(hints, function(className) {
       if(className != showSelector) {
-        $("." + className).not("." + showSelector).hide();
+        dom.hide("." + className + ":not(." + showSelector + ")");
       }
     });
 
@@ -112,8 +113,8 @@ BrowserID.Modules.Authenticate = (function() {
     });
   }
 
-  function enterEmailState(el) {
-    if (!$("#email").is(":disabled")) {
+  function enterEmailState() {
+    if (!dom.is("#email", ":disabled")) {
       this.submit = checkEmail;
       showHint("start");
     }
@@ -129,6 +130,8 @@ BrowserID.Modules.Authenticate = (function() {
     showHint("returning", function() {
       dom.focus("#password");
     });
+
+
     complete(callback);
   }
 
@@ -158,12 +161,19 @@ BrowserID.Modules.Authenticate = (function() {
       var self=this;
       self.renderDialog("authenticate", {
         sitename: user.getHostname(),
-        email: lastEmail,
-        privacy_url: options.privacyURL,
-        tos_url: options.tosURL
+        email: lastEmail
       });
 
-      $(".returning,.start").hide();
+      dom.hide(".returning,.start");
+
+      // We have to show the TOS/PP agreements to *all* users here. Users who
+      // are already authenticated to their IdP but do not have a Persona
+      // account automatically have an account created with no further
+      // interaction.  To make sure they see the TOS/PP agreement, show it
+      // here.
+      if (options.siteTOSPP) {
+        dialogHelpers.showRPTosPP.call(self);
+      }
 
       self.bind("#email", "keyup", emailKeyUp);
       self.click("#forgotPassword", forgotPassword);
diff --git a/resources/static/dialog/js/modules/dialog.js b/resources/static/dialog/js/modules/dialog.js
index 1a426a580653df1bfa93e288c8e0b8fa6306ad3f..8a4169fa8e5a3467885c6740109beb6b7ff1a9bc 100644
--- a/resources/static/dialog/js/modules/dialog.js
+++ b/resources/static/dialog/js/modules/dialog.js
@@ -74,10 +74,6 @@ BrowserID.Modules.Dialog = (function() {
     channel && channel.detach();
   }
 
-  function setOrigin(origin) {
-    user.setOrigin(origin);
-  }
-
   function onWindowUnload() {
     this.publish("window_unload");
   }
@@ -140,7 +136,7 @@ BrowserID.Modules.Dialog = (function() {
       var self=this,
           hash = win.location.hash;
 
-      setOrigin(origin_url);
+      user.setOrigin(origin_url);
 
 
       if (startExternalDependencies) {
@@ -168,8 +164,8 @@ BrowserID.Modules.Dialog = (function() {
         if (paramsFromRP.privacyURL) paramsFromRP.privacyPolicy = paramsFromRP.privacyURL;
 
         if (paramsFromRP.termsOfService && paramsFromRP.privacyPolicy) {
-          params.tosURL = fixupURL(origin_url, paramsFromRP.termsOfService);
-          params.privacyURL = fixupURL(origin_url, paramsFromRP.privacyPolicy);
+          params.termsOfService = fixupURL(origin_url, paramsFromRP.termsOfService);
+          params.privacyPolicy = fixupURL(origin_url, paramsFromRP.privacyPolicy);
         }
 
         if (paramsFromRP.siteLogo) {
diff --git a/resources/static/dialog/js/modules/pick_email.js b/resources/static/dialog/js/modules/pick_email.js
index b666c95e90d2d95f26bb9ca71f65695f1f581466..c28a23babeeefe78540ac346f93f96e5212894b1 100644
--- a/resources/static/dialog/js/modules/pick_email.js
+++ b/resources/static/dialog/js/modules/pick_email.js
@@ -93,10 +93,13 @@ BrowserID.Modules.PickEmail = (function() {
 
       self.renderDialog("pick_email", {
         identities: identities,
-        siteemail: storage.site.get(origin, "email"),
-        privacy_url: options.privacyURL,
-        tos_url: options.tosURL
+        siteemail: user.getOriginEmail()
       });
+
+      if (options.siteTOSPP) {
+        dialogHelpers.showRPTosPP.call(self);
+      }
+
       dom.getElements("body").css("opacity", "1");
       if (dom.getElements("#selectEmail input[type=radio]:visible").length === 0) {
         // If there is only one email address, the radio button is never shown,
diff --git a/resources/static/dialog/js/modules/required_email.js b/resources/static/dialog/js/modules/required_email.js
index 6e5f97e59d25682fbda2022dba60da2a733440e8..037621924d000b0b2c5925f1ad34b22efd58f550 100644
--- a/resources/static/dialog/js/modules/required_email.js
+++ b/resources/static/dialog/js/modules/required_email.js
@@ -147,7 +147,13 @@ BrowserID.Modules.RequiredEmail = (function() {
               // We know the user has control of this address, give them
               // a chance to hit "sign in" before we kick them off to the
               // primary flow account.
-              showTemplate({ signin: true, primary: true });
+
+              // Show the Persona TOS/PP to any primary user who is authed with
+              // their IdP but not with Persona.  Unfortunately, addressInfo
+              // does not tell us whether a primary address already has an
+              // account, so we have to show the personaTOSPP to any user who
+              // is not authenticated.
+              showTemplate({ signin: true, primary: true, personaTOSPP: !auth_level });
             }
             else if(info.type === "primary" && !info.authed) {
               // User who does not control a primary address.
@@ -213,12 +219,15 @@ BrowserID.Modules.RequiredEmail = (function() {
           password: false,
           secondary_auth: false,
           primary: false,
-          privacy_url: options.privacyURL || null,
-          tos_url: options.tosURL || null
+          personaTOSPP: false
         }, templateData);
 
         self.renderDialog("required_email", templateData);
 
+        if (options.siteTOSPP) {
+          dialogHelpers.showRPTosPP.call(self);
+        }
+
         self.click("#sign_in", signIn);
         self.click("#verify_address", verifyAddress);
         self.click("#forgotPassword", forgotPassword);
diff --git a/resources/static/dialog/js/modules/rp_info.js b/resources/static/dialog/js/modules/rp_info.js
index 3bc9b20edd9efb34d9466a815024b6e09c0775e9..55d91d276edf7e861841dc1c3243920fd44f536b 100644
--- a/resources/static/dialog/js/modules/rp_info.js
+++ b/resources/static/dialog/js/modules/rp_info.js
@@ -7,7 +7,8 @@
 
 /**
  * Purpose:
- *  Display to the user RP related data such as hostname, name, and logo.
+ *  Display to the user RP related data such as hostname, sitename, logo,
+ *  TOS/PP, etc.
  */
 BrowserID.Modules.RPInfo = (function() {
   "use strict";
@@ -32,7 +33,9 @@ BrowserID.Modules.RPInfo = (function() {
       renderer.render("#rp_info", "rp_info", {
         hostname: options.hostname,
         siteName: options.siteName,
-        siteLogo: options.siteLogo
+        siteLogo: options.siteLogo,
+        privacyPolicy: options.privacyPolicy,
+        termsOfService: options.termsOfService
       });
 
       sc.start.call(this, options);
diff --git a/resources/static/dialog/js/modules/set_password.js b/resources/static/dialog/js/modules/set_password.js
index 7bc6848c59663f9f30ea5f8023feb0e97c9cb984..02c6de1038dd03df26c63c2dc377a9708e8af66f 100644
--- a/resources/static/dialog/js/modules/set_password.js
+++ b/resources/static/dialog/js/modules/set_password.js
@@ -36,9 +36,14 @@ BrowserID.Modules.SetPassword = (function() {
 
       self.renderDialog("set_password", {
         password_reset: !!options.password_reset,
-        cancelable: options.cancelable !== false
+        cancelable: options.cancelable !== false,
+        personaTOSPP: options.personaTOSPP
       });
 
+      if (options.siteTOSPP) {
+        dialogHelpers.showRPTosPP.call(self);
+      }
+
       self.click("#cancel", cancel);
 
       sc.start.call(self, options);
diff --git a/resources/static/dialog/js/modules/verify_primary_user.js b/resources/static/dialog/js/modules/verify_primary_user.js
index b12827f7c6a99f4290df6f81a3483c5c26594371..8a35914e2665c6a07d5814f439da440e5699a752 100644
--- a/resources/static/dialog/js/modules/verify_primary_user.js
+++ b/resources/static/dialog/js/modules/verify_primary_user.js
@@ -12,7 +12,9 @@ BrowserID.Modules.VerifyPrimaryUser = (function() {
       add,
       email,
       auth_url,
+      dom = bid.DOM,
       helpers = bid.Helpers,
+      dialogHelpers = helpers.Dialog,
       complete = helpers.complete;
 
   function verify(callback) {
@@ -46,12 +48,16 @@ BrowserID.Modules.VerifyPrimaryUser = (function() {
       email = data.email;
       auth_url = data.auth_url;
 
-      var templateData = helpers.extend({}, data, {
+      self.renderDialog("verify_primary_user", {
+        email: data.email,
+        auth_url: data.auth_url,
         requiredEmail: data.requiredEmail || false,
-        privacy_url: data.privacyURL || null,
-        tos_url: data.tosURL || null
+        personaTOSPP: data.personaTOSPP
       });
-      self.renderDialog("verify_primary_user", templateData);
+
+      if (data.siteTOSPP) {
+        dialogHelpers.showRPTosPP.call(self);
+      }
 
       self.click("#cancel", cancel);
 
diff --git a/resources/static/dialog/views/add_email.ejs b/resources/static/dialog/views/add_email.ejs
index b98b22ab8708478c0dd7392de90e4bd2a78632eb..15ebbad134b0779d228dc3f5cbb9102a1193fa2f 100644
--- a/resources/static/dialog/views/add_email.ejs
+++ b/resources/static/dialog/views/add_email.ejs
@@ -34,22 +34,8 @@
       </ul>
 
       <div class="submit cf">
-        <% if (privacy_url && tos_url) { %>
-          <p class="tospp">
-            <%= format(
-                  gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'),
-                       [ gettext('add'),
-                         format(' href="%s" target="_new"', [tos_url]),
-                         format(' href="%s" target="_new"', [privacy_url])
-                       ]) %>
-          </p>
-          <p>
-        <% } %>
           <button id="addNewEmail"><%= gettext('add') %></button>
           <a href="#" id="cancel" class="action"><%= gettext('cancel') %></a>
-        <% if (privacy_url && tos_url) { %>
-          </p>
-        <% } %>
       </div>
 
   </div>
diff --git a/resources/static/dialog/views/authenticate.ejs b/resources/static/dialog/views/authenticate.ejs
index 55e461d5ceaed29e52ef2f148466d2698beb7c7e..a95c45b441638b50424e8ca14dc1dc21df6fa421 100644
--- a/resources/static/dialog/views/authenticate.ejs
+++ b/resources/static/dialog/views/authenticate.ejs
@@ -58,16 +58,15 @@
           <button class="start addressInfo" tabindex="3"><%= gettext('next') %></button>
           <button class="returning" tabindex="3"><%= gettext('sign in') %></button>
         </p>
-        <% if (privacy_url && tos_url) { %>
-           <p class="tospp">
-            <%= format(
-                  gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'),
-                       [ gettext('next'),
-                         format(' href="%s" target="_new"', [tos_url]),
-                         format(' href="%s" target="_new"', [privacy_url])
-                       ]) %>
+
+        <p class="tospp">
+           <%= format(
+                gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'),
+                     [ "Persona",
+                       ' href="https://login.persona.org/tos" target="_new"',
+                       ' href="https://login.persona.org/privacy" target="_new"',
+                     ]) %>
           </p>
-      <% } %>
 
       </div>
   </div>
diff --git a/resources/static/dialog/views/pick_email.ejs b/resources/static/dialog/views/pick_email.ejs
index c189f521055d156ee397366df6d6306a22645f98..059072425b93d6be854125b4290d444f26c019ad 100644
--- a/resources/static/dialog/views/pick_email.ejs
+++ b/resources/static/dialog/views/pick_email.ejs
@@ -23,18 +23,8 @@
 
 
       <div class="submit add cf">
-      <p class="cf">
-        <button id="signInButton"><%= gettext('sign in') %></button>
-      </p>
-      <% if (privacy_url && tos_url) { %>
-        <p class="tospp">
-          <%= format(
-            gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'),
-                    [ gettext('sign in'),
-                      format(' href="%s" target="_new"', [tos_url]),
-                      format(' href="%s" target="_new"', [privacy_url])
-                    ]) %>
+        <p class="cf">
+          <button id="signInButton"><%= gettext('sign in') %></button>
         </p>
-      <% } %>
       </div>
   </div>
diff --git a/resources/static/dialog/views/required_email.ejs b/resources/static/dialog/views/required_email.ejs
index a1e0fdace2304f322d97fcac29b3cf65a664c10e..5245319c2e48ee6849561757f1a8d9b7b9e7ef83 100644
--- a/resources/static/dialog/views/required_email.ejs
+++ b/resources/static/dialog/views/required_email.ejs
@@ -50,17 +50,7 @@
       </ul>
 
       <div class="submit cf">
-        <% if (privacy_url && tos_url) { %>
-          <p class="tospp">
-            <%= format(
-                  gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'),
-                       [ gettext('sign in'),
-                         format(' href="%s" target="_new"', [tos_url]),
-                         format(' href="%s" target="_new"', [privacy_url])
-                       ]) %>
-          </p>
-          <p>
-        <% } %>
+          <p class="cf">
             <% if (signin) { %>
               <button id="sign_in" tabindex="3"><%= gettext("sign in") %></button>
             <% } else if (verify) { %>
@@ -70,8 +60,16 @@
             <% if (secondary_auth) { %>
               <a href="#" id="cancel" class="action" tabindex="4"><%= gettext("cancel") %></a>
             <% } %>
-        <% if (privacy_url && tos_url) { %>
           </p>
-        <% } %>
+          <% if (personaTOSPP) { %>
+            <p class="tospp">
+               <%= format(
+                    gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'),
+                         [ "Persona",
+                           ' href="https://login.persona.org/tos" target="_new"',
+                           ' href="https://login.persona.org/privacy" target="_new"',
+                         ]) %>
+            </p>
+          <% } %>
       </div>
   </div>
diff --git a/resources/static/dialog/views/rp_info.ejs b/resources/static/dialog/views/rp_info.ejs
index 5928a18d6c945d726598017558fbb69f1f4d16e1..724a3e1925dfbf9a1c5d58d6e8bfc67278d31c44 100644
--- a/resources/static/dialog/views/rp_info.ejs
+++ b/resources/static/dialog/views/rp_info.ejs
@@ -11,11 +11,18 @@
   <h2 id="rp_name"><%= siteName %></h2>
 <% } %>
 
-<% if(hostname) { %>
-  <% if(siteName) { %>
-    <h3 id="rp_hostname"><%= hostname %></h3>
-  <% } else { %>
-    <h2 id="rp_hostname"><%= hostname %></h2>
-  <% } %>
+<% if(siteName) { %>
+  <h3 id="rp_hostname"><%= hostname %></h3>
+<% } else { %>
+  <h2 id="rp_hostname"><%= hostname %></h2>
 <% } %>
 
+<% if(privacyPolicy && termsOfService) { %>
+  <p id="rptospp" class="tospp">
+    <%= format(gettext("By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>."),
+    [ siteName || hostname,
+     ' href="' + termsOfService + '" id="rp_tos" target="_blank"',
+     ' href="' + privacyPolicy + '" id="rp_pp" target="_blank"'
+     ]) %>
+   </p>
+<% } %>
diff --git a/resources/static/dialog/views/set_password.ejs b/resources/static/dialog/views/set_password.ejs
index d244293925fc8e8f299af68f59774d156bc32e87..c16a1f2d9fc6378601fde28faeefe159cbb416e5 100644
--- a/resources/static/dialog/views/set_password.ejs
+++ b/resources/static/dialog/views/set_password.ejs
@@ -48,11 +48,23 @@
       </ul>
 
       <div class="submit cf">
+        <p class="cf">
           <button id="<%= password_reset ? "password_reset" : "verify_user" %>">
             <%= password_reset ? gettext('reset password') : gettext('verify email') %>
           </button>
           <% if(cancelable) { %>
             <a id="cancel" class="action" href="#"><%= gettext('cancel') %></a>
           <% } %>
+        </p>
+        <% if (personaTOSPP) { %>
+          <p id="persona_tospp" class="tospp">
+             <%= format(
+                gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'),
+                     [ "Persona",
+                       ' href="https://login.persona.org/tos" target="_new"',
+                       ' href="https://login.persona.org/privacy" target="_new"',
+                     ]) %>
+          </p>
+        <% } %>
       </div>
   </div>
diff --git a/resources/static/dialog/views/verify_primary_user.ejs b/resources/static/dialog/views/verify_primary_user.ejs
index 0a3de6851ed2412d1e3fdefbddf12548b777bb66..c2d4da92de3cdeb7a34d18a1be6a242a1823f370 100644
--- a/resources/static/dialog/views/verify_primary_user.ejs
+++ b/resources/static/dialog/views/verify_primary_user.ejs
@@ -4,7 +4,7 @@
 
 <% if (requiredEmail) { %>
   <strong><%= gettext('The site requested you sign in using') %></strong>
-  <div class="form_section">
+  <div id="verify_primary_user" class="cf form_section">
       <ul class="inputs">
 
           <li>
@@ -15,7 +15,7 @@
               </div>
           </li>
 
-          <li>
+          <li class="small">
               <%= gettext("You must sign in with your email provider to verify ownership of this address. This window will be redirected to") %>
               <p>
                   <strong><%= auth_url %></strong>.
@@ -24,48 +24,47 @@
       </ul>
 
       <div class="submit cf">
-        <% if (privacy_url && tos_url) { %>
-          <p class="tospp">
-            <%= format(
-                  gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'),
-                       [ gettext('verify'),
-                         format(' href="%s" target="_new"', [tos_url]),
-                         format(' href="%s" target="_new"', [privacy_url])
-                       ]) %>
-          </p>
-          <p>
-        <% } %>
-          <button id="verifyWithPrimary"><%= gettext("verify") %></button>
-        <% if (privacy_url && tos_url) { %>
+          <p class="cf">
+            <button id="verifyWithPrimary"><%= gettext("verify") %></button>
           </p>
-        <% } %>
+
+          <% if (personaTOSPP) { %>
+            <p id="persona_tospp" class="tospp">
+               <%= format(
+                  gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'),
+                       [ "Persona",
+                         ' href="https://login.persona.org/tos" target="_new"',
+                         ' href="https://login.persona.org/privacy" target="_new"',
+                       ]) %>
+            </p>
+          <% } %>
       </div>
   </div>
 
 <% } else { %>
   <strong><%= gettext("Verify With Email Provider") %></strong>
 
-  <div class="cf form_section">
-    <%= gettext("You must sign in with your email provider to verify ownership of this address. This window will be redirected to") %>
-    <p>
-      <strong><%= auth_url %></strong>.
-    </p>
+  <div id="verify_primary_user" class="cf form_section">
+    <div class="small">
+      <%= gettext("You must sign in with your email provider to verify ownership of this address. This window will be redirected to") %>
+      <p>
+        <strong><%= auth_url %></strong>.
+      </p>
+    </div>
 
     <div class="submit cf">
-        <% if (privacy_url && tos_url) { %>
-          <p class="tospp">
-            <%= format(
-                  gettext('By clicking %s, you confirm that you accept this site\'s <a %s>Terms of Use</a> and <a %s>Privacy Policy</a>.'),
-                       [ gettext('verify'),
-                         format(' href="%s" target="_new"', [tos_url]),
-                         format(' href="%s" target="_new"', [privacy_url])
-                       ]) %>
-          </p>
-          <p>
-        <% } %>
+        <p class="cf">
           <button id="verifyWithPrimary"><%= gettext("verify") %></button>
           <a href="#" id="cancel" class="action"><%= gettext("cancel") %></a>
-        <% if (privacy_url && tos_url) { %>
+        </p>
+        <% if (personaTOSPP) { %>
+          <p id="persona_tospp" class="tospp">
+             <%= format(
+                gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'),
+                     [ "Persona",
+                       ' href="https://login.persona.org/tos" target="_new"',
+                       ' href="https://login.persona.org/privacy" target="_new"',
+                     ]) %>
           </p>
         <% } %>
     </div>
diff --git a/resources/static/pages/css/style.css b/resources/static/pages/css/style.css
index 96034587943cb6247e9a382cd8149d6b020691e8..d73c65a9f1fbfe26c3f45fbffef015accc062cf3 100644
--- a/resources/static/pages/css/style.css
+++ b/resources/static/pages/css/style.css
@@ -447,10 +447,6 @@ button.delete:active {
   padding: 0;
 }
 
-#signUpForm .submit {
-  height: 28px;
-}
-
 #signUpForm > .siteinfo {
   margin-bottom: 10px;
 }
@@ -459,10 +455,18 @@ button.delete:active {
   display: none;
 }
 
+.submit > p {
+  line-height: 28px;
+}
+
 .submit .remember {
   float: left;
 }
 
+.tospp {
+  font-size: 13px;
+}
+
 .enter_password .password_entry, .enter_verify_password #verify_password, .known_secondary .password_entry,
 .unknown_secondary #unknown_secondary, .verify_primary #verify_primary {
   display: block;
diff --git a/resources/static/test/cases/common/js/user.js b/resources/static/test/cases/common/js/user.js
index 26c47010104631cc42cdd9ac2c77caff04b6c9e5..26813f1815568a5b5626606aeedabed04e7ae751 100644
--- a/resources/static/test/cases/common/js/user.js
+++ b/resources/static/test/cases/common/js/user.js
@@ -92,6 +92,22 @@ var jwcrypto = require("./lib/jwcrypto");
     equal(lib.getReturnTo(), returnTo, "get/setReturnTo work as expected");
   });
 
+  test("setOriginEmail/getOriginEmail", function() {
+    storage.addEmail("testuser@testuser.com", { type: "primary" });
+    storage.addEmail("testuser2@testuser.com", { type: "primary" });
+
+    lib.setOrigin("http://testdomain.org");
+
+    lib.setOriginEmail("testuser@testuser.com");
+    equal(lib.getOriginEmail(), "testuser@testuser.com", "correct email");
+
+    lib.setOrigin("http://othertestdomain.org");
+    lib.setOriginEmail("testuser2@testuser.com");
+
+    lib.setOrigin("http://testdomain.org");
+    equal(lib.getOriginEmail(), "testuser@testuser.com", "correct email");
+  });
+
   test("getStoredEmailKeypairs without key - return all identities", function() {
     var identities = lib.getStoredEmailKeypairs();
     equal("object", typeof identities, "object returned");
diff --git a/resources/static/test/cases/dialog/js/misc/state.js b/resources/static/test/cases/dialog/js/misc/state.js
index 8d6850d399428fb18c5348332367ffc39a9db33d..3d384a0eb72c216073c080033acbfdf4d148d6aa 100644
--- a/resources/static/test/cases/dialog/js/misc/state.js
+++ b/resources/static/test/cases/dialog/js/misc/state.js
@@ -34,6 +34,14 @@
     }
   }
 
+  function testActionStarted(actionName, requiredOptions) {
+    ok(actions.called[actionName], actionName + "called");
+    for(var key in requiredOptions) {
+      equal(actions.info[actionName][key], requiredOptions[key],
+          actionName + " called with " + key + "=" + requiredOptions[key]);
+    }
+  }
+
   function createMachine() {
     machine = bid.State.create();
     actions = new ActionsMock();
@@ -127,6 +135,16 @@
     equal(actions.info.doResetPassword.email, TEST_EMAIL, "correct email sent to doResetPassword");
   });
 
+  test("start - RPInfo always started", function() {
+    mediator.publish("start", {
+      termsOfService: "https://browserid.org/TOS.html",
+      privacyPolicy: "https://browserid.org/priv.html"
+    });
+
+    ok(actions.info.doRPInfo.termsOfService, "doRPInfo called with termsOfService set");
+    ok(actions.info.doRPInfo.privacyPolicy, "doRPInfo called with privacyPolicy set");
+  });
+
   test("user_staged - call doConfirmUser", function() {
     mediator.publish("user_staged", { email: TEST_EMAIL });
 
@@ -254,13 +272,13 @@
     mediator.publish("authenticated", { email: TEST_EMAIL });
   });
 
-  test("forgot_password", function() {
+  test("forgot_password - call doForgotPassword with correct options", function() {
+    mediator.publish("start", { privacyPolicy: "priv.html", termsOfService: "tos.html" });
     mediator.publish("forgot_password", {
       email: TEST_EMAIL,
       requiredEmail: true
     });
-    equal(actions.info.doForgotPassword.email, TEST_EMAIL, "correct email passed");
-    equal(actions.info.doForgotPassword.requiredEmail, true, "correct requiredEmail passed");
+    testActionStarted("doForgotPassword", { email: TEST_EMAIL, requiredEmail: true });
   });
 
   test("password_reset to user_confirmed - call doUserStaged then doEmailConfirmed", function() {
@@ -379,12 +397,11 @@
     ok(actions.called.doNotMe, "doNotMe has been called");
   });
 
-  test("authenticate", function() {
-    mediator.publish("authenticate", {
-      email: TEST_EMAIL
-    });
+  test("authenticate - call doAuthenticate with the correct options", function() {
+    mediator.publish("start", { privacyPolicy: "priv.html", termsOfService: "tos.html" });
+    mediator.publish("authenticate", { email: TEST_EMAIL });
 
-    equal(actions.info.doAuthenticate.email, TEST_EMAIL, "authenticate with testuser@testuser.com");
+    testActionStarted("doAuthenticate", { email: TEST_EMAIL, siteTOSPP: true });
   });
 
   test("start with no special parameters - go straight to checking auth", function() {
@@ -410,16 +427,23 @@
   });
 
 
+  test("add_email - call doAddEmail with correct options", function() {
+    mediator.publish("start", { privacyPolicy: "priv.html", termsOfService: "tos.html" });
+    mediator.publish("add_email");
+    testActionStarted("doAddEmail");
+  });
+
   asyncTest("email_chosen with secondary email, user must authenticate - call doAuthenticateWithRequiredEmail", function() {
     var email = TEST_EMAIL;
     storage.addEmail(email, { type: "secondary" });
 
     xhr.setContextInfo("auth_level", "assertion");
 
+    mediator.publish("start", { privacyPolicy: "priv.html", termsOfService: "tos.html" });
     mediator.publish("email_chosen", {
       email: email,
       complete: function() {
-        equal(actions.called.doAuthenticateWithRequiredEmail, true, "doAuthenticateWithRequiredEmail called");
+        testActionStarted("doAuthenticateWithRequiredEmail", { siteTOSPP: false });
         start();
       }
     });
@@ -469,15 +493,14 @@
   test("null assertion generated - preserve original options in doPickEmail", function() {
     mediator.publish("start", {
       hostname: "http://example.com",
-      privacyURL: "http://example.com/priv.html",
-      tosURL: "http://example.com/tos.html"
+      privacyPolicy: "http://example.com/priv.html",
+      termsOfService: "http://example.com/tos.html"
     });
     mediator.publish("assertion_generated", { assertion: null });
 
     equal(actions.called.doPickEmail, true, "doPickEmail callled");
     equal(actions.info.doPickEmail.origin, "http://example.com", "hostname preserved");
-    equal(actions.info.doPickEmail.privacyURL, "http://example.com/priv.html", "privacyURL preserved");
-    equal(actions.info.doPickEmail.tosURL, "http://example.com/tos.html", "tosURL preserved");
+    equal(actions.info.doPickEmail.siteTOSPP, true, "siteTOSPP preserved");
   });
 
   test("add_email - call doAddEmail", function() {
diff --git a/resources/static/test/cases/dialog/js/modules/actions.js b/resources/static/test/cases/dialog/js/modules/actions.js
index 8d90d69f88dfd2bcd2b72865732ff267e1593e3a..27c54f781f818759fa4527d1c1c0910675b5bcee 100644
--- a/resources/static/test/cases/dialog/js/modules/actions.js
+++ b/resources/static/test/cases/dialog/js/modules/actions.js
@@ -88,7 +88,6 @@
     testActionStartsModule('doGenerateAssertion', { email: TEST_EMAIL }, "generate_assertion");
   });
 
-
   asyncTest("doStageUser with successful creation - trigger user_staged", function() {
     createController({
       ready: function() {
@@ -109,5 +108,21 @@
   asyncTest("doForgotPassword - call the set_password controller with reset_password true", function() {
     testActionStartsModule('doForgotPassword', { email: TEST_EMAIL }, "set_password");
   });
+
+  asyncTest("doRPInfo - start the rp_info service", function() {
+    createController({
+      ready: function() {
+        var error;
+        try {
+          controller.doRPInfo({ name: "browserid.org" });
+        } catch(e) {
+          error = e;
+        }
+
+        equal(error, "module not registered for rp_info", "correct service started");
+        start();
+      }
+    });
+  });
 }());
 
diff --git a/resources/static/test/cases/dialog/js/modules/add_email.js b/resources/static/test/cases/dialog/js/modules/add_email.js
index 6d635e82286fcb606da9e1fe8dd8c99307b40b9e..43f19e385b3091fcdd8411ee30f0c6891f38a123 100644
--- a/resources/static/test/cases/dialog/js/modules/add_email.js
+++ b/resources/static/test/cases/dialog/js/modules/add_email.js
@@ -41,16 +41,6 @@
     controller.start(options || {});
   }
 
-  test("privacyURL and tosURL specified - show TOS/PP", function() {
-    equal($(".tospp").length, 0, "tospp has not yet been added to the DOM");
-    createController({
-      privacyURL: "http://testuser.com/priv.html",
-      tosURL: "http://testuser.com/tos.html",
-    });
-
-    equal($(".tospp").length, 1, "tospp has been added to the DOM");
-  });
-
   test("addEmail with specified email address - fill in email", function() {
     createController({ email: "testuser@testuser.com" });
     ok($("#newEmail").val(), "testuser@testuser.com", "email prepopulated");
diff --git a/resources/static/test/cases/dialog/js/modules/dialog.js b/resources/static/test/cases/dialog/js/modules/dialog.js
index 0a6c14d5f3ba78f1e537426c2ede9608fd1ece13..2519af8c14c3c13e0c77ae4b66b80fcd156036fc 100644
--- a/resources/static/test/cases/dialog/js/modules/dialog.js
+++ b/resources/static/test/cases/dialog/js/modules/dialog.js
@@ -208,7 +208,8 @@
     });
   });
 
-  asyncTest("get with relative tosURL & valid privacyURL - print error screen", function() {
+
+  asyncTest("get with relative termsOfService & valid privacyPolicy - print error screen", function() {
     createController({
       ready: function() {
         mediator.subscribe("start", function(msg, info) {
@@ -216,8 +217,8 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: "relative.html",
-          privacyURL: "/privacy.html"
+          termsOfService: "relative.html",
+          privacyPolicy: "/privacy.html"
         });
         equal(retval, "relative urls not allowed: (relative.html)", "expected error");
         testErrorVisible();
@@ -226,7 +227,7 @@
     });
   });
 
-  asyncTest("get with script containing tosURL - print error screen", function() {
+  asyncTest("get with script containing termsOfService - print error screen", function() {
     createController({
       ready: function() {
         mediator.subscribe("start", function(msg, info) {
@@ -234,11 +235,11 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: "relative.html<script>window.scriptRun=true;</script>",
-          privacyURL: "/privacy.html"
+          termsOfService: "relative.html<script>window.scriptRun=true;</script>",
+          privacyPolicy: "/privacy.html"
         });
 
-        // If tosURL is not properly escaped, scriptRun will be true.
+        // If termsOfService is not properly escaped, scriptRun will be true.
         equal(typeof window.scriptRun, "undefined", "script was not run");
         equal(retval, "relative urls not allowed: (relative.html<script>window.scriptRun=true;</script>)", "expected error");
         testErrorVisible();
@@ -247,7 +248,7 @@
     });
   });
 
-  asyncTest("get with valid tosURL & relative privacyURL - print error screen", function() {
+  asyncTest("get with valid termsOfService & relative privacyPolicy - print error screen", function() {
     createController({
       ready: function() {
         mediator.subscribe("start", function(msg, info) {
@@ -255,8 +256,8 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: "/tos.html",
-          privacyURL: "relative.html"
+          termsOfService: "/tos.html",
+          privacyPolicy: "relative.html"
         });
         equal(retval, "relative urls not allowed: (relative.html)", "expected error");
         testErrorVisible();
@@ -265,7 +266,7 @@
     });
   });
 
-  asyncTest("get with script containing privacyURL - print error screen", function() {
+  asyncTest("get with script containing privacyPolicy - print error screen", function() {
     createController({
       ready: function() {
         mediator.subscribe("start", function(msg, info) {
@@ -273,11 +274,11 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: "/tos.html",
-          privacyURL: "relative.html<script>window.scriptRun=true;</script>"
+          termsOfService: "/tos.html",
+          privacyPolicy: "relative.html<script>window.scriptRun=true;</script>"
         });
 
-        // If privacyURL is not properly escaped, scriptRun will be true.
+        // If privacyPolicy is not properly escaped, scriptRun will be true.
         equal(typeof window.scriptRun, "undefined", "script was not run");
         equal(retval, "relative urls not allowed: (relative.html<script>window.scriptRun=true;</script>)", "expected error");
         testErrorVisible();
@@ -286,7 +287,7 @@
     });
   });
 
-  asyncTest("get with privacyURL - print error screen", function() {
+  asyncTest("get with privacyPolicy - print error screen", function() {
     createController({
       ready: function() {
         mediator.subscribe("start", function(msg, info) {
@@ -294,11 +295,11 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: "/tos.html",
-          privacyURL: "relative.html<script>window.scriptRun=true;</script>"
+          termsOfService: "/tos.html",
+          privacyPolicy: "relative.html<script>window.scriptRun=true;</script>"
         });
 
-        // If privacyURL is not properly escaped, scriptRun will be true.
+        // If privacyPolicy is not properly escaped, scriptRun will be true.
         equal(typeof window.scriptRun, "undefined", "script was not run");
         equal(retval, "relative urls not allowed: (relative.html<script>window.scriptRun=true;</script>)", "expected error");
         testErrorVisible();
@@ -307,7 +308,7 @@
     });
   });
 
-  asyncTest("get with javascript protocol for privacyURL - print error screen", function() {
+  asyncTest("get with javascript protocol for privacyPolicy - print error screen", function() {
     createController({
       ready: function() {
         mediator.subscribe("start", function(msg, info) {
@@ -315,8 +316,8 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: "/tos.html",
-          privacyURL: "javascript:alert(1)"
+          termsOfService: "/tos.html",
+          privacyPolicy: "javascript:alert(1)"
         });
 
         equal(retval, "relative urls not allowed: (javascript:alert(1))", "expected error");
@@ -326,7 +327,7 @@
     });
   });
 
-  asyncTest("get with invalid httpg protocol for privacyURL - print error screen", function() {
+  asyncTest("get with invalid httpg protocol for privacyPolicy - print error screen", function() {
     createController({
       ready: function() {
         mediator.subscribe("start", function(msg, info) {
@@ -334,8 +335,8 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: "/tos.html",
-          privacyURL: "httpg://testdomain.com/privacy.html"
+          termsOfService: "/tos.html",
+          privacyPolicy: "httpg://testdomain.com/privacy.html"
         });
 
         equal(retval, "relative urls not allowed: (httpg://testdomain.com/privacy.html)", "expected error");
@@ -346,7 +347,7 @@
   });
 
 
-  asyncTest("get with valid absolute tosURL & privacyURL - go to start", function() {
+  asyncTest("get with valid absolute termsOfService & privacyPolicy - go to start", function() {
     createController({
       ready: function() {
         var startInfo;
@@ -355,13 +356,13 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: "/tos.html",
-          privacyURL: "/privacy.html"
+          termsOfService: "/tos.html",
+          privacyPolicy: "/privacy.html"
         });
 
         testHelpers.testObjectValuesEqual(startInfo, {
-          tosURL: HTTP_TEST_DOMAIN + "/tos.html",
-          privacyURL: HTTP_TEST_DOMAIN + "/privacy.html"
+          termsOfService: HTTP_TEST_DOMAIN + "/tos.html",
+          privacyPolicy: HTTP_TEST_DOMAIN + "/privacy.html"
         });
 
         equal(typeof retval, "undefined", "no error expected");
@@ -371,7 +372,7 @@
     });
   });
 
-  asyncTest("get with valid fully qualified http tosURL & privacyURL - go to start", function() {
+  asyncTest("get with valid fully qualified http termsOfService & privacyPolicy - go to start", function() {
     createController({
       ready: function() {
         var startInfo;
@@ -380,13 +381,13 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: HTTP_TEST_DOMAIN + "/tos.html",
-          privacyURL: HTTP_TEST_DOMAIN + "/privacy.html"
+          termsOfService: HTTP_TEST_DOMAIN + "/tos.html",
+          privacyPolicy: HTTP_TEST_DOMAIN + "/privacy.html"
         });
 
         testHelpers.testObjectValuesEqual(startInfo, {
-          tosURL: HTTP_TEST_DOMAIN + "/tos.html",
-          privacyURL: HTTP_TEST_DOMAIN + "/privacy.html"
+          termsOfService: HTTP_TEST_DOMAIN + "/tos.html",
+          privacyPolicy: HTTP_TEST_DOMAIN + "/privacy.html"
         });
 
         equal(typeof retval, "undefined", "no error expected");
@@ -397,7 +398,7 @@
   });
 
 
-  asyncTest("get with valid fully qualified https tosURL & privacyURL - go to start", function() {
+  asyncTest("get with valid fully qualified https termsOfService & privacyPolicy - go to start", function() {
     createController({
       ready: function() {
         var startInfo;
@@ -406,13 +407,13 @@
         });
 
         var retval = controller.get(HTTP_TEST_DOMAIN, {
-          tosURL: HTTPS_TEST_DOMAIN + "/tos.html",
-          privacyURL: HTTPS_TEST_DOMAIN + "/privacy.html"
+          termsOfService: HTTPS_TEST_DOMAIN + "/tos.html",
+          privacyPolicy: HTTPS_TEST_DOMAIN + "/privacy.html"
         });
 
         testHelpers.testObjectValuesEqual(startInfo, {
-          tosURL: HTTPS_TEST_DOMAIN + "/tos.html",
-          privacyURL: HTTPS_TEST_DOMAIN + "/privacy.html"
+          termsOfService: HTTPS_TEST_DOMAIN + "/tos.html",
+          privacyPolicy: HTTPS_TEST_DOMAIN + "/privacy.html"
         });
         equal(typeof retval, "undefined", "no error expected");
         testErrorNotVisible();
diff --git a/resources/static/test/cases/dialog/js/modules/required_email.js b/resources/static/test/cases/dialog/js/modules/required_email.js
index 53fbed0c1c6c2290a752ec66f4c88cd2de427581..97800512f5c0df71e88c4529c20d7fc3e71b3658 100644
--- a/resources/static/test/cases/dialog/js/modules/required_email.js
+++ b/resources/static/test/cases/dialog/js/modules/required_email.js
@@ -95,17 +95,16 @@
   }
 
 
-  asyncTest("privacyURL and tosURL specified - show TOS/PP", function() {
+  asyncTest("siteTOSPP specified - show TOS/PP", function() {
     var email = "registered@testuser.com";
     xhr.useResult("known_secondary");
+    xhr.setContextInfo("auth_level", "password");
 
-    equal($(".tospp").length, 0, "tospp has not yet been added to the DOM");
     createController({
-      email: "registered@testuser.com",
-      privacyURL: "http://testuser.com/priv.html",
-      tosURL: "http://testuser.com/tos.html",
+      email: email,
+      siteTOSPP: true,
       ready: function() {
-        equal($(".tospp").length, 1, "tospp has been added to the DOM");
+        testHelpers.testRPTosPPShown();
         start();
       }
     });
diff --git a/resources/static/test/cases/dialog/js/modules/rp_info.js b/resources/static/test/cases/dialog/js/modules/rp_info.js
index 0b89868ab973653fcec4d665f707b5f77b5580dc..c50a3127c4253f84ef0663a2e1836b3db32ecfb5 100644
--- a/resources/static/test/cases/dialog/js/modules/rp_info.js
+++ b/resources/static/test/cases/dialog/js/modules/rp_info.js
@@ -13,8 +13,11 @@
       register = bid.TestHelpers.register,
       WindowMock = bid.Mocks.WindowMock,
       RP_HOSTNAME = "hostname.org",
-      RP_NAME = "RP Name",
-      RP_HTTPS_LOGO = "https://en.gravatar.com/userimage/6966791/c4feac761b8544cce13e0406f36230aa.jpg";
+      RP_NAME = "The Planet's Most Awesome Site",
+      RP_TOS_URL = "https://browserid.org/TOS.html",
+      RP_PP_URL = "https://browserid.org/priv.html",
+      RP_HTTPS_LOGO = "https://en.gravatar.com/userimage/6966791/c4feac761b8544cce13e0406f36230aa.jpg",
+      mediator = bid.Mediator;
 
   module("controllers/rp_info", {
     setup: testHelpers.setup,
@@ -84,5 +87,17 @@
     equal($("#rp_logo").attr("src"), RP_HTTPS_LOGO, "rp logo shown");
   });
 
+  test("privacyPolicy, termsOfService specified - show TOS/PP info", function() {
+    createController({
+      siteName: RP_NAME,
+      privacyPolicy: RP_PP_URL,
+      termsOfService: RP_TOS_URL
+    });
+
+    equal($("#rp_name").text(), RP_NAME, "RP's name is set");
+    equal($("#rp_tos").attr("href"), RP_TOS_URL, "RP's TOS is set");
+    equal($("#rp_pp").attr("href"), RP_PP_URL, "RP's Privacy Policy is set");
+  });
+
 }());
 
diff --git a/resources/static/test/cases/dialog/js/modules/set_password.js b/resources/static/test/cases/dialog/js/modules/set_password.js
index a1ae3b3c683434ebf000678e3b77943fbaf15d82..0d64302f07c16ac0042558e60dd26840fdc97db3 100644
--- a/resources/static/test/cases/dialog/js/modules/set_password.js
+++ b/resources/static/test/cases/dialog/js/modules/set_password.js
@@ -10,6 +10,8 @@
       el = $("body"),
       bid = BrowserID,
       testHelpers = bid.TestHelpers,
+      testElementExists = testHelpers.testElementExists,
+      testElementNotExists = testHelpers.testElementDoesNotExist,
       register = testHelpers.register,
       controller;
 
@@ -33,22 +35,29 @@
 
   test("create with no options - show template, user must verify email, can cancel", function() {
     ok($("#set_password").length, "set_password template added");
-    equal($("#verify_user").length, 1, "correct button shown");
-    equal($("#cancel").length, 1, "cancel button shown");
+    testElementExists("#verify_user");
+    testElementExists("#cancel");
+    testElementNotExists("#persona_tospp");
   });
 
   test("create with password_reset option - show template, show reset password button", function() {
     controller.destroy();
     createController({ password_reset: true });
-    ok($("#set_password").length, "set_password template added");
-    equal($("#password_reset").length, 1, "correct button shown");
-    equal($("#cancel").length, 1, "cancel button shown");
+    testElementExists("#set_password");
+    testElementExists("#password_reset");
+    testElementExists("#cancel");
+  });
+
+  test("create with personaTOSPP option - show Persona TOS/PP", function() {
+    controller.destroy();
+    createController({ personaTOSPP: true });
+    testElementExists("#persona_tospp");
   });
 
   test("create with cancelable=false option - cancel button not shown", function() {
     controller.destroy();
     createController({ cancelable: false });
-    equal($("#cancel").length, 0, "cancel button not shown");
+    testElementNotExists("#cancel");
   });
 
   asyncTest("submit with good password/vpassword - password_set message raised", function() {
diff --git a/resources/static/test/cases/dialog/js/modules/verify_primary_user.js b/resources/static/test/cases/dialog/js/modules/verify_primary_user.js
index 0bd75f72e9456e2fc72097969fc46d4a2b7766a4..08083a9fe9d3d0ef7908a0d18c1e958a8bed00e9 100644
--- a/resources/static/test/cases/dialog/js/modules/verify_primary_user.js
+++ b/resources/static/test/cases/dialog/js/modules/verify_primary_user.js
@@ -10,6 +10,8 @@
       controller,
       el,
       testHelpers = bid.TestHelpers,
+      testElementExists = testHelpers.testElementExists,
+      testElementNotExists = testHelpers.testElementDoesNotExist,
       WindowMock = bid.Mocks.WindowMock,
       win,
       mediator = bid.Mediator;
@@ -33,31 +35,30 @@
     }
   });
 
-  test("create with privacyURL and tosURL defined - show TOS/PP", function() {
+  test("personaTOSPP true, requiredEmail: true - show TOS/PP", function() {
     createController({
       window: win,
       add: false,
       email: "unregistered@testuser.com",
       auth_url: "http://testuser.com/sign_in",
-      privacyURL: "http://testuser.com/priv.html",
-      tosURL: "http://testuser.com/tos.html"
+      requiredEmail: true,
+      personaTOSPP: false
     });
 
-    equal($(".tospp").length, 1, "tospp has been added to the DOM");
+    testElementNotExists("#persona_tospp");
   });
 
-  test("create with requiredEmail, privacyURL and tosURL defined - show TOS/PP", function() {
+  test("personaTOSPP true, requiredEmail: false - show TOS/PP", function() {
     createController({
       window: win,
       add: false,
-      requiredEmail: "unregistered@testuser.com",
       email: "unregistered@testuser.com",
       auth_url: "http://testuser.com/sign_in",
-      privacyURL: "http://testuser.com/priv.html",
-      tosURL: "http://testuser.com/tos.html"
+      requiredEmail: false,
+      personaTOSPP: false
     });
 
-    equal($(".tospp").length, 1, "tospp has been added to the DOM");
+    testElementNotExists("#persona_tospp");
   });
 
   asyncTest("submit with `add: false` option opens a new tab with proper URL (updated for sessionStorage)", function() {
@@ -66,8 +67,12 @@
       window: win,
       add: false,
       email: "unregistered@testuser.com",
-      auth_url: "http://testuser.com/sign_in"
+      auth_url: "http://testuser.com/sign_in",
+      personaTOSPP: true
     });
+
+    testElementExists("#persona_tospp");
+
     mediator.subscribe("primary_user_authenticating", function() {
       messageTriggered = true;
     });
@@ -88,9 +93,12 @@
       window: win,
       add: true,
       email: "unregistered@testuser.com",
-      auth_url: "http://testuser.com/sign_in"
+      auth_url: "http://testuser.com/sign_in",
+      personaTOSPP: true
     });
 
+    testElementExists("#persona_tospp");
+
     // Also checking to make sure the NATIVE is stripped out.
     win.document.location.href = "sign_in";
     win.document.location.hash = "#NATIVE";
diff --git a/resources/static/test/testHelpers/helpers.js b/resources/static/test/testHelpers/helpers.js
index 119bd73f64a8f23eec6b6655e63781e11459b751..66868076a8c8cd477b25b2db0e5e1e6023595037 100644
--- a/resources/static/test/testHelpers/helpers.js
+++ b/resources/static/test/testHelpers/helpers.js
@@ -221,10 +221,6 @@ BrowserID.TestHelpers = (function() {
       }
     },
 
-    testHasClass: function(selector, className, msg) {
-      ok($(selector).hasClass(className), msg || selector + " has className: " + className);
-    },
-
     testUndefined: function(toTest, msg) {
       equal(typeof toTest, "undefined", msg || "object is undefined");
     },
@@ -235,8 +231,33 @@ BrowserID.TestHelpers = (function() {
 
     testVisible: function(selector, msg) {
       ok($(selector).is(":visible"), msg || selector + " should be visible");
-    }
+    },
 
+    testHasClass: function(selector, className, msg) {
+      ok($(selector).hasClass(className),
+          msg || (selector + " has className " + className));
+    },
+
+    testNotHasClass: function(selector, className, msg) {
+      ok(!$(selector).hasClass(className),
+          msg || (selector + " does not have className " + className));
+    },
+
+    testElementExists: function(selector, msg) {
+      ok($(selector).length, msg || ("element '" + selector + "' exists"));
+    },
+
+    testElementDoesNotExist: function(selector, msg) {
+      ok(!$(selector).length, msg || ("element '" + selector + "' does not exist"));
+    },
+
+    testRPTosPPShown: function(msg) {
+      TestHelpers.testHasClass("body", "rptospp", msg || "RP TOS/PP shown");
+    },
+
+    testRPTosPPNotShown: function(msg) {
+      TestHelpers.testNotHasClass("body", "rptospp", msg || "RP TOS/PP not shown");
+    }
   };
 
   return TestHelpers;
diff --git a/resources/views/signup.ejs b/resources/views/signup.ejs
index dcf66e0702c932803c7ca991217b509a4e69323e..9e92bd83b6e5be5865af429ece9f1b226ddd9cb6 100644
--- a/resources/views/signup.ejs
+++ b/resources/views/signup.ejs
@@ -82,10 +82,20 @@
             </ul>
 
             <div class="submit cf forminputs">
-                <button>Verify Email</button>
-                <div class="remember cf">
-                    <a class="action" href="/signin" tabindex="2">Existing account? Sign in.</a>
-                </div>
+                <p class="cf">
+                  <button>Verify Email</button>
+                  <a class="action remember" href="/signin" tabindex="2">Existing account? Sign in.</a>
+                </p>
+
+                <p class="tospp">
+                   <%- format(
+                        gettext('By proceeding, you agree to %s\'s <a %s>Terms</a> and <a %s>Privacy Policy</a>.'),
+                             [ "Persona",
+                               ' href="https://login.persona.org/tos" target="_new"',
+                               ' href="https://login.persona.org/privacy" target="_new"',
+                             ]) %>
+                  </p>
+
             </div>
 
             <ul class="notifications">