diff --git a/example/rp/i/card.png b/example/rp/i/card.png
new file mode 100644
index 0000000000000000000000000000000000000000..eac93fc8481e85d226d934eaa30a2b3b4737837d
Binary files /dev/null and b/example/rp/i/card.png differ
diff --git a/example/rp/index.html b/example/rp/index.html
index 8487ef70dbd66ea2a9d69641f997ec76f6015052..1ff44aa9ed0e37be846258e0d91e30166d335665 100644
--- a/example/rp/index.html
+++ b/example/rp/index.html
@@ -69,6 +69,14 @@ pre {
       <input type="text" id="requiredEmail" width="80">
       <label for="requiredEmail">Require a specific email</label><br />
     </li>
+    </li><li>
+      <input type="text" id="name" width="80">
+      <label for="name">Site Name (optional)</label><br />
+    </li>
+    </li><li>
+      <input type="text" id="logoURL" width="80">
+      <label for="logoURL">Site Logo Path (optional)</label><br />
+    </li>
   </ul>
     <button class="assertion">Get an assertion</button>
     <button class="logout">logout</button>
@@ -178,12 +186,17 @@ $(document).ready(function() {
       return;
     }
 
+    var logoURL = $.trim($('#logoURL').val());
+    var name = $.trim($('#name').val());
+
     $(".specify button.assertion").attr('disabled', 'true');
 
     navigator.id.request({
       privacyURL: $('#privacy').attr('checked') ? "/privacy.html" : undefined,
       tosURL: $('#tos').attr('checked') ? "/TOS.html" : undefined,
       requiredEmail: requiredEmail,
+      name: name,
+      logoURL: logoURL,
       oncancel: function() {
         loggit("oncancel");
         $(".specify button.assertion").removeAttr('disabled');
diff --git a/lib/static_resources.js b/lib/static_resources.js
index ad17eac8848b58d1dd9a982e80f1ceabc1f2c652..bcf8a92f3cab73b52642165e8778379dc1795e81 100644
--- a/lib/static_resources.js
+++ b/lib/static_resources.js
@@ -104,7 +104,7 @@ var dialog_js = und.flatten([
     '/dialog/controllers/generate_assertion.js',
     '/dialog/controllers/is_this_your_computer.js',
     '/dialog/controllers/set_password.js',
-
+    '/dialog/controllers/rp_info.js',
     '/dialog/start.js'
   ]]);
 
diff --git a/resources/static/dialog/controllers/actions.js b/resources/static/dialog/controllers/actions.js
index e01cb576cc4b51a08526164834a982a55081b62b..80435e5220f6bafe3e5856ef3aeb358bc715a098 100644
--- a/resources/static/dialog/controllers/actions.js
+++ b/resources/static/dialog/controllers/actions.js
@@ -58,6 +58,10 @@ BrowserID.Modules.Actions = (function() {
       if(data.ready) _.defer(data.ready);
     },
 
+    doRPInfo: function(info) {
+      startService("rp_info", info);
+    },
+
     doCancel: function() {
       if(onsuccess) onsuccess(null);
     },
diff --git a/resources/static/dialog/controllers/dialog.js b/resources/static/dialog/controllers/dialog.js
index 8e5b8f1cd22e84a05175a9b7b1393c7c69e4a45f..740994a93ce35b50aa2b4736b6ef07cb9ebbef59 100644
--- a/resources/static/dialog/controllers/dialog.js
+++ b/resources/static/dialog/controllers/dialog.js
@@ -75,7 +75,6 @@ BrowserID.Modules.Dialog = (function() {
 
   function setOrigin(origin) {
     user.setOrigin(origin);
-    dom.setInner("#sitename", user.getHostname());
   }
 
   function onWindowUnload() {
@@ -93,6 +92,12 @@ BrowserID.Modules.Dialog = (function() {
     return encodeURI(u.validate().normalize().toString());
   }
 
+  function fixupAbsolutePath(origin_url, path) {
+    if (/^\//.test(path))  return fixupURL(origin_url, path);
+
+    throw "must be an absolute path: (" + path + ")";
+  }
+
   var Dialog = bid.Modules.PageModule.extend({
     start: function(options) {
       var self=this;
@@ -168,6 +173,17 @@ BrowserID.Modules.Dialog = (function() {
           params.privacyURL = fixupURL(origin_url, paramsFromRP.privacyPolicy);
         }
 
+        if (paramsFromRP.logoURL) {
+          // Until we have our head around the dangers of data uris and images
+          // that come from other domains, only allow absolute paths from the
+          // origin.
+          params.logoURL = fixupAbsolutePath(origin_url, paramsFromRP.logoURL);
+        }
+
+        if (paramsFromRP.name) {
+          params.name = _.escape(paramsFromRP.name);
+        }
+
         if (hash.indexOf("#CREATE_EMAIL=") === 0) {
           var email = hash.replace(/#CREATE_EMAIL=/, "");
           if (!bid.verifyEmail(email))
diff --git a/resources/static/dialog/controllers/rp_info.js b/resources/static/dialog/controllers/rp_info.js
new file mode 100644
index 0000000000000000000000000000000000000000..92384bfcac6d200781dbf47fd5ff887a698aee98
--- /dev/null
+++ b/resources/static/dialog/controllers/rp_info.js
@@ -0,0 +1,47 @@
+/*jshint browser:true, jQuery: true, forin: true, laxbreak:true */
+/*global _: true, BrowserID: true, PageController: 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/. */
+
+
+/**
+ * Purpose:
+ *  Display to the user RP related data such as hostname, name, and logo.
+ */
+BrowserID.Modules.RPInfo = (function() {
+  "use strict";
+
+  var bid = BrowserID,
+      renderer = bid.Renderer,
+      sc;
+
+  var Module = bid.Modules.PageModule.extend({
+    start: function(options) {
+      options = options || {};
+
+      /**
+       * Very important security info - it is assumed all parameters are
+       * already properly escaped before being passed here.  This is done
+       * in dialog.js.  Check it.
+       *
+       * hostname is set internally based on the RP URL,
+       * so it will not be escaped.  It is set initially in user.js at the very
+       * bottom for the main site, and then in dialog.js->get for the dialog.
+       */
+      renderer.render("#rp_info", "rp_info", {
+        hostname: options.hostname,
+        name: options.name,
+        logoURL: options.logoURL
+      });
+
+      sc.start.call(this, options);
+    }
+  });
+
+  sc = Module.sc;
+
+  return Module;
+
+}());
+
diff --git a/resources/static/dialog/css/m.css b/resources/static/dialog/css/m.css
index 6991dbe0296b5f6f2abce6bf2ee662a95e95aa98..efc23a635bce1793a4b77e06a86d84493c3c4109 100644
--- a/resources/static/dialog/css/m.css
+++ b/resources/static/dialog/css/m.css
@@ -43,7 +43,7 @@
   }
 
   #signIn {
-    top: 45px;  /* 45px is a magic number - the height of the favicon area */
+    top: auto;  /* this will be set in JS to be at the bottom of the header */
     right: 0;
     width: auto;
     padding: 0;
@@ -57,13 +57,32 @@
        * being partially cut off by the site URL bar
        */
       position: static;
+      padding: 10px;
+      border-bottom: 1px solid rgba(0,0,0,0.05);
+      background-image: url('/i/bg.png');
+      text-align: center;
+      left: 0;
   }
 
-  #favicon {
-    padding: 10px;
+  #favicon img {
+      max-width: 32px;
+      max-height: 32px;
+      display: inline;
+      margin: 0;
+      vertical-align: middle;
+  }
+
+  #favicon h2, #favicon h3 {
+      margin: 5px 0 0 0;
   }
 
-  #signIn .table {
+  #favicon .vertical {
+      height: auto;
+      line-height: 20px;
+  }
+
+
+  #signIn .table, #signIn .container {
       width: 100%;
   }
 
@@ -96,6 +115,10 @@
       line-height: 20px;
   }
 
+  #formWrap {
+      background-color: transparent;
+  }
+
   .form #formWrap, .waiting #wait, .delay #delay, #error #error {
       display: block;
   }
diff --git a/resources/static/dialog/css/popup.css b/resources/static/dialog/css/popup.css
index c55543e572188e2ce35d2a14b4f0102f31cd295b..b1d96da234a3a74fc4fc6aff7c7832a555c3ef8a 100644
--- a/resources/static/dialog/css/popup.css
+++ b/resources/static/dialog/css/popup.css
@@ -270,7 +270,8 @@ section > .contents {
     left: 400px;
     top: 0;
     bottom: 0;
-    right: 0;
+    right: 20px; /* The same as the left padding of the left hand side */
+    overflow: hidden;
     z-index: 10;
 }
 
@@ -282,14 +283,23 @@ section > .contents {
 
 #favicon img {
     display: block;
-    margin: 0 auto 10px;
+    margin: 0 auto;
+    max-height: 128px;
+    max-width: 128px;
+}
+
+#favicon h2, #favicon h3 {
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    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 .vertical {
     display: table-cell;
     text-align: center;
-    overflow: hidden;
-    text-overflow: ellipsis;
+    max-width: 0;
 }
 
 div#required_email {
diff --git a/resources/static/dialog/resources/screen_size_hacks.js b/resources/static/dialog/resources/screen_size_hacks.js
index 503d84852d9bfd982016a7968c396cf97053045e..196060f86907d89028e9c00994fb7801faf58969 100644
--- a/resources/static/dialog/resources/screen_size_hacks.js
+++ b/resources/static/dialog/resources/screen_size_hacks.js
@@ -8,13 +8,15 @@
    */
   function onResize() {
     var selectEmailEl = $("#selectEmail"),
-        contentEl = $("#content");
+        contentEl = $("#content"),
+        signInEl = $("#signIn");
 
     selectEmailEl.css("position", "static");
     if($(window).width() >= 640) {
       // First, remove the mobile hacks
       selectEmailEl.css("width", "");
       contentEl.css("min-height", "");
+      signInEl.css("top", "");
 
       // This is a hack for desktop mode which centers the form vertically in
       // the middle of its container.  We have to do this hack because we use
@@ -105,6 +107,11 @@
         contentEl.css("min-height", contentHeight + "px");
 
         $("section,#signIn").css("position", "");
+
+        favIconHeight = $("#favicon").outerHeight();
+
+        // Force the top of the main content area to be below the favicon area.
+        signInEl.css("top", (headerHeight + favIconHeight) + "px");
     }
 
     selectEmailEl.css("position", "");
diff --git a/resources/static/dialog/resources/state.js b/resources/static/dialog/resources/state.js
index 8a016d6f574e2af46e97906e78d08d5b9b65887c..c56eee0ddf6d95326c190dde76913f89c403073d 100644
--- a/resources/static/dialog/resources/state.js
+++ b/resources/static/dialog/resources/state.js
@@ -46,6 +46,8 @@ BrowserID.State = (function() {
       self.tosURL = info.tosURL;
       requiredEmail = info.requiredEmail;
 
+      startAction(false, "doRPInfo", info);
+
       if (info.email && info.type === "primary") {
         primaryVerificationInfo = info;
         redirectToState("primary_user", info);
diff --git a/resources/static/dialog/start.js b/resources/static/dialog/start.js
index 543136b023e673488b9e0d90c1fa9d1b34dc519c..70c574bad3f2e645532c48aee54fdec314b886fc 100644
--- a/resources/static/dialog/start.js
+++ b/resources/static/dialog/start.js
@@ -42,6 +42,7 @@
       moduleManager.register("xhr_delay", modules.XHRDelay);
       moduleManager.register("xhr_disable_form", modules.XHRDisableForm);
       moduleManager.register("set_password", modules.SetPassword);
+      moduleManager.register("rp_info", modules.RPInfo);
 
       moduleManager.start("xhr_delay");
       moduleManager.start("xhr_disable_form");
diff --git a/resources/static/dialog/views/rp_info.ejs b/resources/static/dialog/views/rp_info.ejs
new file mode 100644
index 0000000000000000000000000000000000000000..dd3be3f921fbefe2ae0cced7971b6de4edcf2980
--- /dev/null
+++ b/resources/static/dialog/views/rp_info.ejs
@@ -0,0 +1,21 @@
+<% /* 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/. */ %>
+
+<% if(logoURL) { %>
+  <img id="rp_logo" src="<%= logoURL %>" />
+<% } %>
+
+
+<% if(name) { %>
+  <h2 id="rp_name"><%= name %></h2>
+<% } %>
+
+<% if(hostname) { %>
+  <% if(name) { %>
+    <h3 id="rp_hostname"><%= hostname %></h3>
+  <% } else { %>
+    <h2 id="rp_hostname"><%= hostname %></h2>
+  <% } %>
+<% } %>
+
diff --git a/resources/static/test/cases/controllers/dialog.js b/resources/static/test/cases/controllers/dialog.js
index 4190cd915513306d18faa797a3a542aec718676c..6efbdefb2d58e2d83a979fddb57a203508c5155e 100644
--- a/resources/static/test/cases/controllers/dialog.js
+++ b/resources/static/test/cases/controllers/dialog.js
@@ -469,5 +469,123 @@
     });
   });
 
+  asyncTest("get with relative logoURL - not allowed", function() {
+    createController({
+      ready: function() {
+        mediator.subscribe("start", function(msg, info) {
+          ok(false, "start should not have been called");
+        });
+
+        var retval = controller.get(HTTP_TEST_DOMAIN, {
+          logoURL: "logo.png",
+        });
+
+        equal(retval, "must be an absolute path: (logo.png)", "expected error");
+        testErrorVisible();
+        start();
+      }
+    });
+  });
+
+  asyncTest("get with javascript: logoURL - not allowed", function() {
+    createController({
+      ready: function() {
+        mediator.subscribe("start", function(msg, info) {
+          ok(false, "start should not have been called");
+        });
+
+        var retval = controller.get(HTTP_TEST_DOMAIN, {
+          logoURL: "javascript:alert('xss')",
+        });
+
+        equal(retval, "must be an absolute path: (javascript:alert('xss'))", "expected error");
+        testErrorVisible();
+        start();
+      }
+    });
+  });
+
+  asyncTest("get with data-uri: logoURL - not allowed", function() {
+    createController({
+      ready: function() {
+        mediator.subscribe("start", function(msg, info) {
+          ok(false, "start should not have been called");
+        });
+
+        var retval = controller.get(HTTP_TEST_DOMAIN, {
+          logoURL: "data:image/png,FAKEDATA",
+        });
+
+        equal(retval, "must be an absolute path: (data:image/png,FAKEDATA)", "expected error");
+        testErrorVisible();
+        start();
+      }
+    });
+  });
+
+  asyncTest("get with http: logoURL - not allowed", function() {
+    createController({
+      ready: function() {
+        mediator.subscribe("start", function(msg, info) {
+          ok(false, "start should not have been called");
+        });
+
+        var retval = controller.get(HTTP_TEST_DOMAIN, {
+          logoURL: HTTP_TEST_DOMAIN + "://logo.png",
+        });
+
+        equal(retval, "must be an absolute path: (" + HTTP_TEST_DOMAIN + "://logo.png)", "expected error");
+        testErrorVisible();
+        start();
+      }
+    });
+  });
+
+  asyncTest("get with https: logoURL - not allowed", function() {
+    createController({
+      ready: function() {
+        mediator.subscribe("start", function(msg, info) {
+          ok(false, "start should not have been called");
+        });
+
+        var retval = controller.get(HTTP_TEST_DOMAIN, {
+          logoURL: HTTPS_TEST_DOMAIN + "://logo.png",
+        });
+
+        equal(retval, "must be an absolute path: (" + HTTPS_TEST_DOMAIN + "://logo.png)", "expected error");
+        testErrorVisible();
+        start();
+      }
+    });
+  });
+
+  asyncTest("get with absolute path - allowed URL but it must be properly escaped", function() {
+    createController({
+      ready: function() {
+        var startInfo;
+        mediator.subscribe("start", function(msg, info) {
+          startInfo = info;
+        });
+
+        var logoURL = '/i/card.png" onerror="alert(\'xss\')" <script>alert(\'more xss\')</script>';
+        var retval = controller.get(HTTP_TEST_DOMAIN, {
+          logoURL: logoURL
+        });
+
+        start();
+
+        testHelpers.testObjectValuesEqual(startInfo, {
+          logoURL: encodeURI(HTTP_TEST_DOMAIN + logoURL)
+        });
+        equal(typeof retval, "undefined", "no error expected");
+        testErrorNotVisible();
+        start();
+      }
+    });
+
+  });
+
+
+
 }());
 
diff --git a/resources/static/test/cases/controllers/rp_info.js b/resources/static/test/cases/controllers/rp_info.js
new file mode 100644
index 0000000000000000000000000000000000000000..07a57d1c48248844c52ad4528953518c140f47e5
--- /dev/null
+++ b/resources/static/test/cases/controllers/rp_info.js
@@ -0,0 +1,88 @@
+/*jshint browsers:true, forin: true, laxbreak: true */
+/*global test: true, start: true, stop: true, module: true, ok: true, equal: true, 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/. */
+(function() {
+  "use strict";
+
+  var controller,
+      bid = BrowserID,
+      user = bid.User,
+      testHelpers = bid.TestHelpers,
+      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";
+
+  module("controllers/rp_info", {
+    setup: testHelpers.setup,
+
+    teardown: function() {
+      if (controller) {
+        try {
+          controller.destroy();
+          controller = null;
+        } catch(e) {
+          // could already be destroyed from the close
+        }
+      }
+      window.scriptRun = null;
+      delete window.scriptRun;
+      testHelpers.teardown();
+    }
+  });
+
+
+  function createController(options) {
+    options = _.extend({ hostname: RP_HOSTNAME }, options);
+
+    controller = bid.Modules.RPInfo.create();
+    controller.start(options || {});
+  }
+
+  test("neither name nor logo specified - use site's rp_hostname as name", function() {
+    createController();
+    equal($("#rp_hostname").html(), RP_HOSTNAME, "rp_hostname filled in");
+    ok(!$("#rp_name").html(), "rp_name empty");
+    ok(!$("#rp_logo").attr("src"), "rp logo not shown");
+  });
+
+  test("name only specified - show specified name and rp_hostname", function() {
+    createController({
+      name: RP_NAME,
+    });
+
+    equal($("#rp_hostname").html(), RP_HOSTNAME, "rp_hostname filled in");
+    equal($("#rp_name").html(), RP_NAME, "rp_name filled in");
+    ok(!$("#rp_logo").attr("src"), "rp logo not shown");
+  });
+
+  test("logoURLs are allowed", function() {
+    var docMock = new WindowMock().document;
+    docMock.location.protocol = "http:";
+
+    createController({
+      document: docMock,
+      logoURL: RP_HTTPS_LOGO
+    });
+
+    equal($("#rp_logo").attr("src"), RP_HTTPS_LOGO, "rp logo shown");
+    equal($("#rp_hostname").html(), RP_HOSTNAME, "rp_hostname filled in");
+    ok(!$("#rp_name").html(), "rp_name empty");
+  });
+
+  test("both name and logo specified - show name, logo and rp_hostname", function() {
+    createController({
+      name: RP_NAME,
+      logoURL: RP_HTTPS_LOGO
+    });
+
+    equal($("#rp_hostname").html(), RP_HOSTNAME, "rp_hostname filled in");
+    equal($("#rp_name").html(), RP_NAME, "rp_name filled in");
+    equal($("#rp_logo").attr("src"), RP_HTTPS_LOGO, "rp logo shown");
+  });
+
+}());
+
diff --git a/resources/views/dialog.ejs b/resources/views/dialog.ejs
index 1752efdd3c5de51970154617b621af299d07881a..9bf2250be9fd1a0834b44c4843125e54fe8f2c7e 100644
--- a/resources/views/dialog.ejs
+++ b/resources/views/dialog.ejs
@@ -5,8 +5,7 @@
       <form novalidate>
         <div id="favicon">
             <div class="table">
-                <div class="vertical">
-                    <strong id="sitename"></strong>
+                <div class="vertical" id="rp_info">
                 </div>
             </div>
         </div>
diff --git a/resources/views/test.ejs b/resources/views/test.ejs
index dd25eca2f3ca107c19d0fe0776a669ed16ec0e06..8f6ea43fe4403fe499028fe55ee072f1b92ab9a5 100644
--- a/resources/views/test.ejs
+++ b/resources/views/test.ejs
@@ -25,6 +25,9 @@
         <a href="#" onclick="$('#contents').hide(); return false;">Close</a>
         <h3>Test Contents, this will be updated and can be safely ignored</h3>
 
+        <div id="rp_info">
+        </div>
+
         <div id="page_head">
         </div>
 
@@ -131,6 +134,7 @@
     <script src="/dialog/controllers/primary_user_provisioned.js"></script>
     <script src="/dialog/controllers/is_this_your_computer.js"></script>
     <script src="/dialog/controllers/set_password.js"></script>
+    <script src="/dialog/controllers/rp_info.js"></script>
 
     <script src="/pages/page_helpers.js"></script>
     <script src="/pages/verify_secondary_address.js"></script>
@@ -191,6 +195,7 @@
     <script src="cases/controllers/primary_user_provisioned.js"></script>
     <script src="cases/controllers/is_this_your_computer.js"></script>
     <script src="cases/controllers/set_password.js"></script>
+    <script src="cases/controllers/rp_info.js"></script>
 
     <!-- must go last or all other tests will fail. -->
     <script src="cases/controllers/dialog.js"></script>