Skip to content
Browse files

integrating train 2011.10.20

  • Loading branch information...
2 parents 03e2ab0 + fe2a041 commit ab0307daa7459a5907340c2d59dec304553010ea @lloyd lloyd committed Oct 27, 2011
Showing with 677 additions and 408 deletions.
  1. +18 −0 ChangeLog
  2. +20 −0 browserid/app.js
  3. +1 −1 browserid/compress.sh
  4. +0 −1 browserid/lib/email.js
  5. +13 −11 browserid/static/css/style.css
  6. +19 −18 browserid/static/dialog/controllers/authenticate_controller.js
  7. +3 −3 browserid/static/dialog/controllers/checkregistration_controller.js
  8. +4 −1 browserid/static/dialog/controllers/dialog_controller.js
  9. +36 −29 browserid/static/dialog/controllers/page_controller.js
  10. +4 −5 browserid/static/dialog/controllers/pickemail_controller.js
  11. +19 −7 browserid/static/dialog/css/m.css
  12. +47 −38 browserid/static/dialog/css/popup.css
  13. +17 −4 browserid/static/dialog/qunit.html
  14. +14 −14 browserid/static/dialog/resources/error-messages.js
  15. +20 −29 browserid/static/dialog/resources/user.js
  16. +6 −15 browserid/static/dialog/resources/wait-messages.js
  17. +146 −0 browserid/static/dialog/test/qunit/controllers/page_controller_unit_test.js
  18. +14 −5 browserid/static/dialog/test/qunit/qunit.js
  19. 0 browserid/static/dialog/test/qunit/{ → resources}/network_unit_test.js
  20. 0 browserid/static/dialog/test/qunit/{ → resources}/storage_unit_test.js
  21. +80 −57 browserid/static/dialog/test/qunit/{ → resources}/user_unit_test.js
  22. 0 browserid/static/dialog/test/qunit/{ → resources}/validation_unit_test.js
  23. +41 −65 browserid/static/dialog/views/authenticate.ejs
  24. +1 −1 browserid/static/dialog/views/confirmemail.ejs
  25. +41 −58 browserid/static/dialog/views/pickemail.ejs
  26. +2 −0 browserid/static/dialog/views/testBodyTemplate.ejs
  27. +6 −14 browserid/static/include.js
  28. +1 −1 browserid/static/js/pages/manage_account.js
  29. +15 −2 browserid/static/js/pages/signin.js
  30. +1 −0 browserid/static/js/pages/signup.js
  31. +3 −3 browserid/views/about.ejs
  32. +32 −10 browserid/views/dialog.ejs
  33. +1 −1 browserid/views/index.ejs
  34. +1 −1 browserid/views/privacy.ejs
  35. +1 −1 browserid/views/verifyuser.ejs
  36. +1 −1 rp/index.html
  37. +4 −4 verifier/app.js
  38. +45 −8 verifier/lib/certassertion.js
View
18 ChangeLog
@@ -1,3 +1,21 @@
+train-2011.10.20:
+ * android < 3.0 now supported: #461
+ * properly set assertion expiration time to when they expire, not when they're issued: #433, #457, #458
+ * update privacy policy language to jive with new UI: #381
+ * add redirects for old URLs that no longer exist with the new UI: #376
+ * inside the minified include.js, link to uncompressed version for developer convenience and discovery: #432
+ * language tweaks: #437, #444
+ * improve button UI appearance on opera and IE: #435
+ * improve visual feedback for links: #440
+ * UI fixes for > 2 email addresses on iOS: #417
+ * smooth out screen transitions in dialog: #369
+ * improved "check your email" screen on mobile: #462
+ * no auto-caps nor auto-correct for iOS in add email field: #464
+ * improve event listening on input fields: #406
+ * remember email when moving user from signup to sign-in for known email address: #108
+ * don't call sync_emails more than necessary: #434
+ * assertions now include full origin (scheme+host+port). verifier accepts only host+port OR full origin, and returns whatever RP sends for back compat: #82
+
train-2011.10.13:
* fix verification of email in different browser than where verification is initiated: #336
* Android < 3.0 (browsers that can't handle JSON.parse("null")) now blocked explicitly (until we complete support)
View
20 browserid/app.js
@@ -137,6 +137,26 @@ function router(app) {
res.render('verifyemail.ejs', {title: 'Verify Email Address', fullpage: false});
});
+ // REDIRECTS
+ REDIRECTS = {
+ "/manage": "/",
+ "/users": "/",
+ "/users/": "/",
+ "/primaries" : "/developers",
+ "/primaries/" : "/developers",
+ "/developers" : "https://github.com/mozilla/browserid/wiki/How-to-Use-BrowserID-on-Your-Site"
+ };
+
+ // set up all the redirects
+ // oh my watch out for scope issues on var url - closure time
+ for (var url in REDIRECTS) {
+ (function(from,to) {
+ app.get(from, function(req, res) {
+ res.redirect(to);
+ });
+ })(url, REDIRECTS[url]);
+ }
+
// register all the WSAPI handlers
wsapi.setup(app);
View
2 browserid/compress.sh
@@ -20,7 +20,7 @@ echo ''
cd static
mv include.js include.orig.js
-$UGLIFY -nc include.orig.js > include.js
+$UGLIFY include.orig.js > include.js
echo ''
echo '****Building dialog HTML, CSS, and JS****'
View
1 browserid/lib/email.js
@@ -44,7 +44,6 @@ logger = require('../../libs/logging.js').logger;
/* if smtp parameters are configured, use them */
var smtp_params = config.get('smtp');
-console.log("SMTP", smtp_params);
if (smtp_params && smtp_params.host) {
emailer.SMTP = { host: smtp_params.host };
logger.info("delivering email via SMTP host: " + emailer.SMTP.host);
View
24 browserid/static/css/style.css
@@ -79,7 +79,7 @@ a {
text-decoration: none;
}
-a:hover {
+a:hover, button:hover, input[type=submit]:hover {
text-decoration: underline;
}
@@ -315,10 +315,6 @@ div.steps {
border-radius: 5px;
}
-#manage .edit a:hover {
- text-decoration: none;
-}
-
#manageAccounts {
background-color: #37A6FF;
border: 1px solid #37A6FF;
@@ -330,7 +326,8 @@ div.steps {
-o-box-shadow: 0 0 0 1px #76C2FF inset;
box-shadow: 0 0 0 1px #76C2FF inset;
- background-image: -moz-linear-gradient(center top , #76C2FF 0pt, #37A6FF 100%);
+ background-image: -moz-linear-gradient(#76C2FF 0pt, #37A6FF 100%);
+ background-image: -o-linear-gradient(#76C2FF 0pt, #37A6FF 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #76C2FF), color-stop(100%, #37A6FF));
}
@@ -346,7 +343,8 @@ div.steps {
-o-box-shadow: 0 0 5px #003763 inset;
box-shadow: 0 0 5px #003763 inset;
- background-image: -moz-linear-gradient(center top , #3AA7FF 0%, #006EC6 100%);
+ background-image: -moz-linear-gradient(#3AA7FF 0%, #006EC6 100%);
+ background-image: -o-linear-gradient(#3AA7FF 0%, #006EC6 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #3AA7FF), color-stop(100%, #006EC6));
}
@@ -402,7 +400,7 @@ div.steps {
}
button.delete {
- background-color: #37A6FF;
+ background-color: #EA7676;
height: 24px;
vertical-align: middle;
border: 1px solid #B13D3D;
@@ -423,7 +421,8 @@ button.delete {
-o-border-radius: 5px;
border-radius: 5px;
- background-image: -moz-linear-gradient(center top , #EA7676 0pt, #C84343 100%);
+ background-image: -moz-linear-gradient(#EA7676 0%, #C84343 100%);
+ background-image: -o-linear-gradient(#EA7676 0%, #C84343 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #EA7676), color-stop(100%, #C84343));
}
@@ -439,6 +438,7 @@ button.delete:active {
box-shadow: 0 0 5px #003763 inset;
background-image: -moz-linear-gradient(center top , #C84343 0%, #AA3D3D 100%);
+ background-image: -o-linear-gradient(#C84343 0%, #AA3D3D 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #C84343), color-stop(100%, #AA3D3D));
}
@@ -508,7 +508,8 @@ h1 {
-o-border-radius: 5px;
border-radius: 5px;
- background-image: -moz-linear-gradient(center top , #76C2FF 0pt, #37A6FF 100%);
+ background-image: -moz-linear-gradient(#76C2FF 0pt, #37A6FF 100%);
+ background-image: -o-linear-gradient(#76C2FF 0pt, #37A6FF 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #76C2FF), color-stop(100%, #37A6FF));
}
@@ -523,7 +524,8 @@ h1 {
-o-box-shadow: 0 0 5px #003763 inset;
box-shadow: 0 0 5px #003763 inset;
- background-image: -moz-linear-gradient(center top , #3AA7FF 0%, #006EC6 100%);
+ background-image: -moz-linear-gradient(#3AA7FF 0%, #006EC6 100%);
+ background-image: -o-linear-gradient(#3AA7FF 0%, #006EC6 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #3AA7FF), color-stop(100%, #006EC6));
}
View
37 browserid/static/dialog/controllers/authenticate_controller.js
@@ -40,20 +40,21 @@
var ANIMATION_TIME = 250,
bid = BrowserID,
user = bid.User,
- validation = bid.Validation;
+ validation = bid.Validation,
+ lastEmail = "";
function checkEmail(el, event) {
var email = $("#email").val(),
self = this;
cancelEvent(event);
- if(!validation.email(email)) {
+ if (!validation.email(email)) {
return;
}
user.isEmailRegistered(email, function onComplete(registered) {
- if(registered) {
+ if (registered) {
enterPasswordState.call(self);
}
else {
@@ -68,12 +69,12 @@
cancelEvent(event);
- if(!validation.email(email)) {
+ if (!validation.email(email)) {
return;
}
user.createUser(email, function(keypair) {
- if(keypair) {
+ if (keypair) {
self.close("user_staged", {
email: email,
keypair: keypair
@@ -92,16 +93,11 @@
cancelEvent(event);
- if(!validation.emailAndPassword(email, pass)) {
+ if (!validation.emailAndPassword(email, pass)) {
return;
}
- user.authenticateAndSync(email, pass,
- function onAuthenticate(authenticated) {
- if (authenticated) {
- self.doWait(bid.Wait.authentication);
- }
- },
+ user.authenticate(email, pass,
function onComplete(authenticated) {
if (authenticated) {
self.close("authenticated", {
@@ -145,7 +141,7 @@
}
function enterEmailState(el, event) {
- if(event && event.which === 13) {
+ if (event && event.which === 13) {
// Enter key, do nothing
return;
}
@@ -193,24 +189,29 @@
init: function(el, options) {
options = options || {};
- this._super({
+ this._super(el, {
bodyTemplate: "authenticate.ejs",
bodyVars: {
- sitename: user.getOrigin(),
- siteicon: "/i/times.gif",
+ sitename: user.getHostname(),
email: options.email || ""
}
});
this.submit = checkEmail;
// If we already have an email address, check if it is valid, if so, show
// password.
- if(options.email) {
+ if (options.email) {
this.submit();
}
},
- "#email keyup": enterEmailState,
+ "#email keyup": function(el, event) {
+ var newEmail = el.val();
+ if (newEmail !== lastEmail) {
+ lastEmail = newEmail;
+ enterEmailState.call(this, el);
+ }
+ },
"#forgotPassword click": forgotPasswordState,
"#cancel_forgot_password click": cancelForgotPassword
});
View
6 browserid/static/dialog/controllers/checkregistration_controller.js
@@ -42,9 +42,9 @@
PageController.extend("Checkregistration", {}, {
init: function(el, options) {
var me=this;
- me._super({
- bodyTemplate: "confirmemail.ejs",
- bodyVars: {
+ me._super(el, {
+ waitTemplate: "confirmemail.ejs",
+ waitVars: {
email: options.email
}
});
View
5 browserid/static/dialog/controllers/dialog_controller.js
@@ -61,6 +61,9 @@
this.onerror = onerror;
user.setOrigin(origin_url);
+
+ // get the cleaned origin.
+ $("#sitename").text(user.getHostname());
this.doCheckAuth();
@@ -207,7 +210,7 @@
user.checkAuthenticationAndSync(function onSuccess() {},
function onComplete(authenticated) {
if (authenticated) {
- self.doPickEmail();
+ self.doPickEmail();
} else {
self.doAuthenticate();
}
View
65 browserid/static/dialog/controllers/page_controller.js
@@ -42,13 +42,23 @@
$.Controller.extend("PageController", {
}, {
- init: function(options) {
+ init: function(el, options) {
+ options = options || {};
+
var me=this,
bodyTemplate = options.bodyTemplate,
- bodyVars = options.bodyVars;
+ bodyVars = options.bodyVars,
+ waitTemplate = options.waitTemplate,
+ waitVars = options.waitVars;
- me.renderTemplates(bodyTemplate, bodyVars);
+ if(bodyTemplate) {
+ me.renderDialog(bodyTemplate, bodyVars);
+ }
+
+ if(waitTemplate) {
+ me.renderWait(waitTemplate, waitVars);
+ }
// XXX move all of these, bleck.
$("form").bind("submit", me.onSubmit.bind(me));
@@ -69,18 +79,32 @@
this._super();
},
- renderTemplates: function(body, body_vars) {
-
+ renderTemplates: function(target, body, body_vars) {
if (body) {
var bodyHtml = $.View("//dialog/views/" + body, body_vars);
- var form = $("#formWrap > form");
- form.html(bodyHtml).hide().fadeIn(ANIMATION_TIME, function() {
- $("body").removeClass("waiting");
- form.find("input").eq(0).focus();
- });
+ target = $(target + " .contents");
+ target.html(bodyHtml).find("input").eq(0).focus();
}
},
+ renderDialog: function(body, body_vars) {
+ this.renderTemplates("#formWrap", body, body_vars);
+ $("body").removeClass("error").removeClass("waiting").addClass("form");
+ $("#wait, #error").stop().fadeOut(ANIMATION_TIME);
+ },
+
+ renderWait: function(body, body_vars) {
+ this.renderTemplates("#wait", body, body_vars);
+ $("body").removeClass("error").removeClass("form").addClass("waiting");
+ $("#wait").stop().css('opacity', 1).hide().fadeIn(ANIMATION_TIME);
+ },
+
+ renderError: function(error_vars) {
+ this.renderTemplates("#error", "wait.ejs", error_vars);
+ $("body").removeClass("waiting").removeClass("form").addClass("error");
+ $("#error").stop().css('opacity', 1).hide().fadeIn(ANIMATION_TIME);
+ },
+
onSubmit: function(event) {
event.stopPropagation();
event.preventDefault();
@@ -100,7 +124,7 @@
},
doWait: function(info) {
- this.renderTemplates("wait.ejs", {title: info.message, message: info.description});
+ this.renderWait("wait.ejs", info);
$("body").addClass("waiting");
},
@@ -113,31 +137,14 @@
},
/**
- * Immediately show the error dialog
- * @method errorDialog
- * @param {object} info - info to use for the error dialog. Should have
- * two fields, message, description.
- */
- errorDialog: function(info) {
- $("form").hide();
-
- $("#error_dialog .title").text(info.message);
- $("#error_dialog .content").text(info.description);
-
- $("body").removeClass("authenticated").addClass("error");
-
- $("#error_dialog").fadeIn(ANIMATION_TIME);
- },
-
- /**
* Get a curried function to an error dialog.
* @method getErrorDialog
* @method {object} info - info to use for the error dialog. Should have
* two fields, message, description.
*/
getErrorDialog: function(info) {
var self=this;
- return self.errorDialog.bind(self, info);
+ return self.renderError.bind(self, info);
},
onCancel: function(event) {
View
9 browserid/static/dialog/controllers/pickemail_controller.js
@@ -134,6 +134,7 @@
var valid = checkEmail.call(self, email);
if (valid) {
+// self.doWait(bid.Wait.generateKey);
getAssertion.call(self, email);
startAnimation.call(self);
}
@@ -172,13 +173,11 @@
PageController.extend("Pickemail", {}, {
- init: function(options) {
- this._super({
+ init: function(el, options) {
+ this._super(el, {
bodyTemplate: "pickemail.ejs",
bodyVars: {
- sitename: user.getOrigin(),
- siteicon: '/i/times.gif',
- identities: user.getStoredEmailKeypairs(),
+ identities: user.getStoredEmailKeypairs()
}
});
View
26 browserid/static/dialog/css/m.css
@@ -24,6 +24,11 @@
font-size: 17px;
}
+ section {
+ position: static;
+ overflow: visible;
+ }
+
.inputs > li > label {
font-size: 18px;
}
@@ -73,9 +78,8 @@
background-color: transparent;
}
- #formWrap,
- .vertical {
- height: auto;
+ .error #formWrap, .waiting #formWrap {
+ display: none;
}
.arrow {
@@ -86,10 +90,6 @@
height: 250px;
}
- #footer {
- padding: 10px;
- }
-
#signIn .vertical {
padding-bottom: 0;
}
@@ -101,4 +101,16 @@
#signIn .submit > p {
font-size: 14px;
}
+
+ #content, .pickemail .form_section, .pickemail .inputs, .vertical {
+ height: auto;
+ overflow: visible;
+ }
+
+ #wait .vertical {
+ height: 250px;
+ }
+
}
+
+
View
85 browserid/static/dialog/css/popup.css
@@ -65,9 +65,13 @@ h1 {
}
h2 {
- font-size: 1em;
+ margin-bottom: 20px;
+ font-size: 150%;
+ color: #222;
+ font-weight: bold;
}
+
.right {
text-align: right;
}
@@ -77,11 +81,6 @@ h2 {
position: relative;
}
-#error_dialog {
- text-align: center;
- margin: 100px auto;
- display: none;
-}
.table {
display: table;
@@ -95,37 +94,55 @@ h2 {
width: 100%;
}
-.authenticate #formWrap, .pickemail #formWrap {
- background-color: #fff;
- background-image: none;
-}
-
-#formWrap, .waiting #formWrap, .checkregistration #formWrap {
- background-color: rgba(0, 0, 0, 0.035);
- background-image: url('/i/bg.png');
+#content {
position: relative;
- width: 100%;
height: 250px;
overflow: hidden;
}
-.waiting #formWrap, .checkregistration #formWrap {
- width: 325px;
- margin: 0 auto;
- text-align: center;
+section {
+ display: block;
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ z-index: 0;
}
-.waiting #formWrap > form, .checkregistration #formWrap > form {
- height: 250px;
- width: 325px;
+section > .contents {
display: table-cell;
vertical-align: middle;
+ height: 250px;
}
-#signIn,
-#favicon {
- display: inline-block;
- float: left;
+#wait, #error {
+ text-align: center;
+ background-image: url("/i/bg.png");
+}
+
+#wait {
+ z-index: 1;
+}
+
+#error {
+ display: none;
+ z-index: 2;
+}
+
+#wait strong, #error strong {
+ color: #222;
+ font-weight: bold;
+}
+
+#error {
+ z-index: 2;
+}
+
+#formWrap {
+ background-color: #fff;
+ background-image: none;
+ display: block;
}
#signIn {
@@ -166,6 +183,7 @@ h2 {
#favicon {
position: absolute;
left: 450px;
+ top: 0;
z-index: 10;
}
@@ -355,21 +373,12 @@ footer .learn a {
text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.5);
}
-.checkregistration p:first-child {
- margin-bottom: 20px;
- font-size: 150%;
-}
-
-.checkregistration strong {
- color: #222;
- font-weight: bold;
-}
-
+ /*
#favicon {
text-align: center;
max-width: 940px;
}
-
+*/
header {
padding: 20px;
font-weight: bold;
@@ -435,11 +444,11 @@ header,
footer {
display: block;
width: 100%;
+ border-top: 1px solid rgba(0, 0, 0, 0.05);
}
footer {
padding: 20px;
- border-top: 1px solid rgba(0,0,0,0.05);
color: #aaa;
}
View
21 browserid/static/dialog/qunit.html
@@ -2,9 +2,6 @@
<head>
<link rel="stylesheet" type="text/css" href="/funcunit/qunit/qunit.css" />
<title>dialog QUnit Test</title>
- <script type='text/javascript'>
- steal = {ignoreControllers: true}
- </script>
<script type='text/javascript' src='/vepbundle'></script>
<script type='text/javascript' src='/steal/steal.js?/dialog/test/qunit'></script>
</head>
@@ -14,7 +11,23 @@ <h1 id="qunit-header">dialog Test Suite</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
- <div id="test-content"></div>
+ <div id="test-content">
+ <div id="page_controller">
+
+ <div id="formWrap">
+ <div class="contents"></div>
+ </div>
+
+ <div id="wait">
+ <div class="contents"></div>
+ </div>
+
+ <div id="error">
+ <div class="contents"></div>
+ </div>
+
+ </div>
+ </div>
<ol id="qunit-tests"></ol>
<div id="qunit-test-area"></div>
</body>
View
28 browserid/static/dialog/resources/error-messages.js
@@ -38,44 +38,44 @@ BrowserID.Errors = (function(){
var Errors = {
authentication: {
type: "serverError",
- message: "Error Authenticating",
- description: "There was a technical problem while trying to log you in. Yucky!"
+ title: "Error Authenticating",
+ message: "There was a technical problem while trying to log you in. Yucky!"
},
addEmail: {
type: "serverError",
- message: "Error Adding Address",
- description: "There was a technical problem while trying to add this email to your account. Yucky!"
+ title: "Error Adding Address",
+ message: "There was a technical problem while trying to add this email to your account. Yucky!"
},
checkAuthentication: {
type: "serverError",
- message: "Error Checking Authentication",
- description: "There was a technical problem while trying to log you in. Yucky!"
+ title: "Error Checking Authentication",
+ message: "There was a technical problem while trying to log you in. Yucky!"
},
createAccount: {
type: "serverError",
- message: "Error Creating Account",
- description: "There was a technical problem while trying to create your account. Yucky!"
+ title: "Error Creating Account",
+ message: "There was a technical problem while trying to create your account. Yucky!"
},
registration: {
type: "serverError",
- message: "Registration Failed",
- description: "An error was encountered and the signup cannot be completed. Yucky!"
+ title: "Registration Failed",
+ message: "An error was encountered and the signup cannot be completed. Yucky!"
},
signIn: {
type: "serverError",
- message: "Signin Failed",
- description: "There was an error signing in. Yucky!"
+ title: "Signin Failed",
+ message: "There was an error signing in. Yucky!"
},
syncAddress: {
type: "serverError",
- message: "Error Syncing Address",
- description: "There was a technical problem while trying to synchronize your account. Yucky!"
+ title: "Error Syncing Address",
+ message: "There was a technical problem while trying to synchronize your account. Yucky!"
}
};
View
49 browserid/static/dialog/resources/user.js
@@ -98,10 +98,6 @@ BrowserID.User = (function() {
}
}
- function filterOrigin(origin) {
- return origin.replace(/^.*:\/\//, "");
- }
-
function registrationPoll(checkFunc, email, onSuccess, onFailure) {
function poll() {
checkFunc(email, function(status) {
@@ -201,8 +197,8 @@ BrowserID.User = (function() {
* @method setOrigin
* @param {string} origin
*/
- setOrigin: function(unfilteredOrigin) {
- origin = filterOrigin(unfilteredOrigin);
+ setOrigin: function(originArg) {
+ origin = originArg;
},
/**
@@ -215,6 +211,15 @@ BrowserID.User = (function() {
},
/**
+ * Get the hostname for the set origin
+ * @method getHostname
+ * @returns {string}
+ */
+ getHostname: function() {
+ return origin.replace(/^.*:\/\//, "").replace(/:\d*$/, "");
+ },
+
+ /**
* Create a user account - this creates an user account that must be verified.
* @method createUser
* @param {string} email - Email address.
@@ -403,33 +408,18 @@ BrowserID.User = (function() {
},
/**
- * Authenticate the user with the given email and password, if
- * authentication successful, sync addresses with server.
- * @method authenticateAndSync
+ * Authenticate the user with the given email and password.
+ * @method authenticate
* @param {string} email - Email address to authenticate.
* @param {string} password - Password.
- * @param {function} [onSuccess] - Called whenever authentication succeeds
- * but before sync starts. Useful for displaying status messages about the
- * sync taking a moment.
* @param {function} [onComplete] - Called on sync completion.
* @param {function} [onFailure] - Called on failure.
*/
- authenticateAndSync: function(email, password, onSuccess, onComplete, onFailure) {
+ authenticate: function(email, password, onComplete, onFailure) {
var self=this;
network.authenticate(email, password, function(authenticated) {
setAuthenticationStatus(authenticated);
- if (authenticated) {
- if (onSuccess) {
- onSuccess(authenticated);
- }
-
- self.syncEmails(function() {
- if (onComplete) {
- onComplete(authenticated);
- }
- }, onFailure);
- } else if (onComplete) {
- // If not authenticated, we have to complete still.
+ if (onComplete) {
onComplete(authenticated);
}
}, onFailure);
@@ -535,7 +525,10 @@ BrowserID.User = (function() {
function createAssertion(idInfo) {
network.serverTime(function(serverTime) {
var sk = jwk.SecretKey.fromSimpleObject(idInfo.priv);
- var tok = new jwt.JWT(null, serverTime, origin);
+ // assertions are valid for 2 minutes
+ var expirationMS = serverTime.getTime() + (2 * 60 * 1000);
+ var expirationDate = new Date(expirationMS);
+ var tok = new jwt.JWT(null, expirationDate, origin);
assertion = vep.bundleCertsAndAssertion([idInfo.cert], tok.sign(sk));
if (onSuccess) {
onSuccess(assertion);
@@ -587,9 +580,7 @@ BrowserID.User = (function() {
*/
clearStoredEmailKeypairs: function() {
storage.clear();
- },
-
-
+ }
};
User.setOrigin(document.location.host);
View
21 browserid/static/dialog/resources/wait-messages.js
@@ -37,24 +37,15 @@ BrowserID.Wait = (function(){
var Wait = {
authentication: {
- message: "Finishing Sign In...",
- description: "In just a moment you'll be signed into BrowserID."
+ title: "Finishing Sign In...",
+ message: "In just a moment you'll be signed into BrowserID."
},
- addEmail: {
- message: "One Moment Please...",
- description: "We're adding this email to your account, this should only take a couple of seconds."
- },
-
- checkAuth: {
- message: "Communicating with server",
- description: "Just a moment while we talk with the server."
- },
-
- createAccount: {
- message: "One Moment Please...",
- description: "We're creating your account, this should only take a couple of seconds."
+ generateKey: {
+ title: "Finishing Sign In...",
+ message: "Please wait just a moment while your account is authenticated."
}
+
};
View
146 browserid/static/dialog/test/qunit/controllers/page_controller_unit_test.js
@@ -0,0 +1,146 @@
+/*jshint browsers:true, forin: true, laxbreak: true */
+/*global steal: true, test: true, start: true, stop: true, module: true, ok: true, equal: true, BrowserID:true */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla BrowserID.
+ *
+ * The Initial Developer of the Original Code is Mozilla.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+steal.plugins("jquery").then("/dialog/controllers/page_controller", function() {
+ "use strict";
+
+ var controller, el,
+ bodyTemplate = "testBodyTemplate.ejs",
+ waitTemplate = "wait.ejs";
+
+ module("PageController", {
+ setup: function() {
+ el = $("#page_controller");
+ },
+
+ teardown: function() {
+ el.find("#formWrap .contents").html("");
+ el.find("#wait .contents").html("");
+ el.find("#error .contents").html("");
+ controller.destroy();
+ }
+ });
+
+ test("page controller with no template causes no side effects", function() {
+ controller = el.page().controller();
+
+ var html = el.find("#formWrap .contents").html();
+ equal(html, "", "with no template specified, no text is loaded");
+
+ html = el.find("#wait .contents").html();
+ equal(html, "", "with no template specified, no text is loaded");
+ });
+
+ test("page controller with body template renders in #formWrap .contents", function() {
+ controller = el.page({
+ bodyTemplate: bodyTemplate,
+ bodyVars: {
+ title: "Test title",
+ message: "Test message"
+ }
+ }).controller();
+
+ var html = el.find("#formWrap .contents").html();
+ ok(html.length, "with template specified, form text is loaded");
+
+
+ var input = el.find("input").eq(0);
+ ok(input.is(":focus"), "make sure the first input is focused");
+
+ html = el.find("#wait .contents").html();
+ equal(html, "", "with body template specified, wait text is not loaded");
+ });
+
+ test("page controller with wait template renders in #wait .contents", function() {
+ controller = el.page({
+ waitTemplate: waitTemplate,
+ waitVars: {
+ title: "Test title",
+ message: "Test message"
+ }
+ }).controller();
+
+ var html = el.find("#formWrap .contents").html();
+ equal(html, "", "with wait template specified, form is ignored");
+
+ html = el.find("#wait .contents").html();
+ ok(html.length, "with wait template specified, wait text is loaded");
+ });
+
+ test("renderError renders an error message", function() {
+ controller = el.page({
+ waitTemplate: waitTemplate,
+ waitVars: {
+ title: "Test title",
+ message: "Test message"
+ }
+ }).controller();
+
+ controller.renderError({
+ title: "error title",
+ message: "error message"
+ });
+
+ var html = el.find("#error .contents").html();
+ // XXX underpowered test, we don't actually check the contents.
+ ok(html.length, "with error template specified, error text is loaded");
+ });
+
+ test("getErrorDialog gets a function that can be used to render an error message", function() {
+ controller = el.page({
+ waitTemplate: waitTemplate,
+ waitVars: {
+ title: "Test title",
+ message: "Test message"
+ }
+ }).controller();
+
+ var func = controller.getErrorDialog({
+ title: "error title",
+ message: "error message"
+ });
+
+ equal(typeof func, "function", "a function was returned from getErrorDialog");
+ func();
+
+ var html = el.find("#error .contents").html();
+ // XXX underpowered test, we don't actually check the contents.
+ ok(html.length, "when function is run, error text is loaded");
+
+ });
+
+});
+
View
19 browserid/static/dialog/test/qunit/qunit.js
@@ -3,9 +3,18 @@ steal("/dialog/resources/browserid.js",
"/dialog/resources/tooltip.js",
"/dialog/resources/validation.js",
"/dialog/resources/underscore-min.js")
- .plugins("funcunit/qunit")
+ .plugins(
+ "jquery",
+ "jquery/controller",
+ "jquery/controller/subscribe",
+ "jquery/controller/view",
+ "jquery/view/ejs",
+ "funcunit/qunit")
+ .views('testBodyTemplate.ejs')
+ .views('wait.ejs')
.then("browserid_unit_test")
- .then("validation_unit_test")
- .then("storage_unit_test")
- .then("network_unit_test")
- .then("user_unit_test")
+ .then("controllers/page_controller_unit_test")
+ .then("resources/validation_unit_test")
+ .then("resources/storage_unit_test")
+ .then("resources/network_unit_test")
+ .then("resources/user_unit_test")
View
0 ...ic/dialog/test/qunit/network_unit_test.js → ...test/qunit/resources/network_unit_test.js
File renamed without changes.
View
0 ...ic/dialog/test/qunit/storage_unit_test.js → ...test/qunit/resources/storage_unit_test.js
File renamed without changes.
View
137 ...tatic/dialog/test/qunit/user_unit_test.js → ...og/test/qunit/resources/user_unit_test.js
@@ -35,12 +35,14 @@
*
* ***** END LICENSE BLOCK ***** */
var jwk = require("./jwk");
+var jwt = require("./jwt");
var jwcert = require("./jwcert");
steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", function() {
var lib = BrowserID.User,
network = BrowserID.Network,
- storage = BrowserID.Storage;
+ storage = BrowserID.Storage,
+ testOrigin = "testOrigin";
// I generated these locally, they are used nowhere else.
var pubkey = {"algorithm":"RS","n":"56063028070432982322087418176876748072035482898334811368408525596198252519267108132604198004792849077868951906170812540713982954653810539949384712773390200791949565903439521424909576832418890819204354729217207360105906039023299561374098942789996780102073071760852841068989860403431737480182725853899733706069","e":"65537"};
@@ -158,6 +160,42 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", functio
}
};
+
+ function testAssertion(assertion) {
+ equal(typeof assertion, "string", "An assertion was correctly generated");
+
+ // Decode the assertion to a bundle.
+ var bundle = JSON.parse(window.atob(assertion));
+
+ // Make sure both parts of the bundle exist
+ ok(bundle.certificates && bundle.certificates.length, "we have an array like object for the certificates");
+ equal(typeof bundle.assertion, "string");
+
+ // Decode the assertion itself
+ var tok = new jwt.JWT();
+ tok.parse(bundle.assertion);
+
+
+ // Check for parts of the assertion
+ equal(tok.audience, testOrigin, "correct audience");
+ var expires = tok.expires.getTime();
+ ok(typeof expires === "number" && !isNaN(expires), "expiration date is valid");
+
+ var nowPlus2Mins = new Date().getTime() + (2 * 60 * 1000);
+ // expiration date must be within 5 seconds of 2 minutes from now - see
+ // issue 433 (https://github.com/mozilla/browserid/issues/433)
+ ok(((nowPlus2Mins - 5000) < expires) && (expires < (nowPlus2Mins + 5000)), "expiration date must be within 5 seconds of 2 minutes from now");
+
+ equal(typeof tok.cryptoSegment, "string", "cryptoSegment exists");
+ equal(typeof tok.headerSegment, "string", "headerSegment exists");
+ equal(typeof tok.payloadSegment, "string", "payloadSegment exists");
+ /*
+ // What are these supposed to be?
+ ok(tok.issuer, "issuer?");
+ ok(tok.payload, "payload?");
+ */
+ }
+
module("user", {
setup: function() {
lib.setNetwork(netStub);
@@ -179,8 +217,16 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", functio
}
test("setOrigin, getOrigin", function() {
- lib.setOrigin("someorigin");
- equal(lib.getOrigin(), "someorigin");
+ lib.setOrigin(testOrigin);
+ equal(lib.getOrigin(), testOrigin);
+ });
+
+ test("setOrigin, getHostname", function() {
+ var origin = "http://testorigin.com:10001";
+ lib.setOrigin(origin);
+
+ var hostname = lib.getHostname();
+ equal(hostname, "testorigin.com", "getHostname returns only the hostname");
});
test("getStoredEmailKeypairs", function() {
@@ -291,9 +337,8 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", functio
});
- test("authenticateAndSync with valid credentials", function() {
- lib.authenticateAndSync("testuser@testuser.com", "testuser", function() {
- }, function(authenticated) {
+ test("authenticate with valid credentials", function() {
+ lib.authenticate("testuser@testuser.com", "testuser", function(authenticated) {
equal(true, authenticated, "we are authenticated!");
start();
}, failure("Authentication failure"));
@@ -304,11 +349,9 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", functio
- test("authenticateAndSync with invalid credentials", function() {
+ test("authenticate with invalid credentials", function() {
credentialsValid = false;
- lib.authenticateAndSync("testuser@testuser.com", "testuser", function onSuccess(authenticated) {
- ok(false, "This should not be called on authentication failure");
- }, function onComplete(authenticated) {
+ lib.authenticate("testuser@testuser.com", "testuser", function onComplete(authenticated) {
equal(false, authenticated, "invalid authentication.");
start();
}, failure("Authentication failure"));
@@ -368,40 +411,6 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", functio
stop();
});
-
- test("authenticateAndSync with valid authentication", function() {
- credentialsValid = true;
- keyRefresh = ["testuser@testuser.com"];
-
- lib.authenticateAndSync("testuser@testuser.com", "testuser", function() {
- }, function(authenticated) {
- var identities = lib.getStoredEmailKeypairs();
- ok("testuser@testuser.com" in identities, "authenticateAndSync syncs email addresses");
- ok(authenticated, "we are authenticated")
- start();
- });
-
- stop();
- });
-
-
-
- test("authenticateAndSync with invalid authentication", function() {
- credentialsValid = false;
- keyRefresh = ["testuser@testuser.com"];
-
- lib.authenticateAndSync("testuser@testuser.com", "testuser", function() {
- }, function(authenticated) {
- var identities = lib.getStoredEmailKeypairs();
- equal("testuser@testuser.com" in identities, false, "authenticateAndSync does not sync if authentication is invalid");
- equal(authenticated, false, "not authenticated");
- start();
- });
-
- stop();
- });
-
-
test("isEmailRegistered with registered email", function() {
lib.isEmailRegistered("registered", function(registered) {
ok(registered);
@@ -614,11 +623,24 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", functio
stop();
});
+ test("syncEmails with one to refresh", function() {
+ storage.addEmail("testuser@testuser.com", {pub: pubkey, cert: random_cert});
+ keyRefresh = ["testuser@testuser.com"];
+
+ lib.syncEmails(function onSuccess() {
+ var identities = lib.getStoredEmailKeypairs();
+ ok("testuser@testuser.com" in identities, "refreshed key is synced");
+ start();
+ }, failure("identity sync failure"));
+
+ stop();
+ });
+
test("getAssertion with known email that has key", function() {
lib.syncEmailKeypair("testuser@testuser.com", function() {
lib.getAssertion("testuser@testuser.com", function onSuccess(assertion) {
- equal("string", typeof assertion, "we have an assertion!");
+ testAssertion(assertion);
start();
}, failure("getAssertion failure"));
}, failure("syncEmailKeypair failure"));
@@ -630,7 +652,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", functio
test("getAssertion with known email that does not have a key", function() {
storage.addEmail("testuser@testuser.com", {});
lib.getAssertion("testuser@testuser.com", function onSuccess(assertion) {
- equal("string", typeof assertion, "we have an assertion!");
+ testAssertion(assertion);
start();
}, failure("getAssertion failure"));
@@ -653,19 +675,20 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/user", functio
credentialsValid = true;
keyRefresh = ["testuser@testuser.com"];
- lib.authenticateAndSync("testuser@testuser.com", "testuser", function() {
- }, function(authenticated) {
- var storedIdentities = storage.getEmails();
- equal(_.size(storedIdentities), 1, "one identity");
+ lib.authenticate("testuser@testuser.com", "testuser", function(authenticated) {
+ lib.syncEmails(function() {
+ var storedIdentities = storage.getEmails();
+ equal(_.size(storedIdentities), 1, "one identity");
- lib.logoutUser(function() {
- storedIdentities = storage.getEmails();
- equal(_.size(storedIdentities), 0, "All items have been removed on logout");
+ lib.logoutUser(function() {
+ storedIdentities = storage.getEmails();
+ equal(_.size(storedIdentities), 0, "All items have been removed on logout");
- equal(credentialsValid, false, "credentials were invalidated in logout");
- start();
- });
- });
+ equal(credentialsValid, false, "credentials were invalidated in logout");
+ start();
+ }, failure("logoutUser failure"));
+ }, failure("syncEmails failure"));
+ }, failure("authenticate failure"));
stop();
});
View
0 ...dialog/test/qunit/validation_unit_test.js → ...t/qunit/resources/validation_unit_test.js
File renamed without changes.
View
106 browserid/static/dialog/views/authenticate.ejs
@@ -1,78 +1,54 @@
- <!-- This takes care of the core user creation/authentication. When the user
- enters an email address and hits "next", the address will be checked
- against the currently registered addresses. If the address exists,
- the password container is shown, if the address does not exist, the
- option to create a new account is given.
- -->
+ <strong>Sign in using</strong>
+ <ul class="inputs">
- <div id="favicon">
- <div class="table">
- <div class="vertical">
- <strong><%= sitename %></strong>
- </div>
- </div>
- </div>
+ <li>
+ <label for="email" class="serif">Email</label>
+ <input id="email" class="sans" type="email" autocapitalize="off" autocorrect="off" value="<%= email %>" />
- <div id="signIn">
- <div class="arrow"></div>
- <div class="table">
- <div class="vertical">
- <strong>Sign in using</strong>
- <ul class="inputs">
+ <div id="email_format" class="tooltip" for="email">
+ This field must be an email address.
+ </div>
- <li>
- <label for="email" class="serif">Email</label>
- <input id="email" class="sans" type="email" autocapitalize="off" autocorrect="off" value="<%= email %>" />
+ <div id="email_required" class="tooltip" for="email">
+ The email field is required.
+ </div>
+ </li>
- <div id="email_format" class="tooltip" for="email">
- This field must be an email address.
- </div>
+ <li id="hint_section" class="start">
+ <p>Enter your email address to sign in to <strong><%= sitename %></strong></p>
+ </li>
- <div id="email_required" class="tooltip" for="email">
- The email field is required.
- </div>
- </li>
+ <li id="create_text_section" class="newuser">
+ <p><strong>Welcome to BrowserID!</strong></p>
+ <p>This email looks new, so let's get you set up.</p>
+ </li>
- <li id="hint_section" class="start">
- <p>Enter your email address to sign in to <strong><%= sitename %></strong></p>
- </li>
+ <li id="password_section" class="returning">
- <li id="create_text_section" class="newuser">
- <p><strong>Welcome to BrowserID!</strong></p>
- <p>This email looks new, so let's get you set up.</p>
- </li>
+ <label for="password" class="half serif">Password</label>
+ <div class="half right">
+ <a id="forgotPassword" href="#">forgot your password?</a>
+ </div>
+ <input id="password" class="sans" type="password" maxlength="80">
- <li id="password_section" class="returning">
- <label for="password" class="half serif">Password</label>
- <div class="half right">
- <a id="forgotPassword" href="#">forgot your password?</a>
- </div>
- <input id="password" class="sans" type="password" maxlength="80">
+ <div id="password_required" class="tooltip" for="password">
+ The password field is required.
+ </div>
+ <div id="cannot_authenticate" class="tooltip" for="password">
+ The account cannot be logged in with this username and password.
+ </div>
+ </li>
+
+ </ul>
- <div id="password_required" class="tooltip" for="password">
- The password field is required.
- </div>
-
- <div id="cannot_authenticate" class="tooltip" for="password">
- The account cannot be logged in with this username and password.
- </div>
- </li>
-
- </ul>
-
- <div class="submit cf">
- <button class="start">next</button>
- <button class="newuser">Verify Email</button>
-
- <button class="returning">sign in</button>
-
- <button class="forgot">Reset Password</button>
- <button id="cancel_forgot_password" class="forgot">Cancel</button>
- </div>
- </div>
- </div>
- </div>
+ <div class="submit cf">
+ <button class="start">next</button>
+ <button class="newuser">Verify Email</button>
+ <button class="returning">sign in</button>
+ <button class="forgot">Reset Password</button>
+ <button id="cancel_forgot_password" class="forgot">Cancel</button>
+ </div>
View
2 browserid/static/dialog/views/confirmemail.ejs
@@ -1,4 +1,4 @@
- <p><strong>Check your email!</strong></p>
+ <h2>Check your email!</h2>
<p>We sent a confirmation email to <strong><%= email %></strong></p>
<p>To finish signing in just click the verify link we sent to your email address.</p><br />
<p>If this is a mistake, <a href="#" id="back">cancel this email</a>.</p>
View
99 browserid/static/dialog/views/pickemail.ejs
@@ -1,72 +1,55 @@
- <div id="favicon">
- <div class="table">
- <div class="vertical">
- <strong><%= sitename %></strong>
- </div>
+ <strong>Sign in using</strong>
+
+ <div id="selectEmail" class="form_section">
+ <ul class="inputs">
+ <% _.each(identities, function(email_obj, email_address) { %>
+ <li>
+ <label for="<%= email_address %>" class="serif">
+ <input type="radio" name="email" id="<%= email_address %>" value="<%= email_address %>" />
+ <%= email_address %>
+ </label>
+ </li>
+ <% }); %>
+ </ul>
+
+ <div class="submit add cf">
+ <button id="signInButton">sign in</button>
+ <p>
+ <a id="thisIsNotMe" href="#">This is not me</a>
+ <a id="useNewEmail" href="#">Use a different email</a>
+ </p>
</div>
</div>
+ <div id="addEmail" class="cf form_section">
- <div id="signIn">
- <div class="arrow"></div>
- <div class="table">
- <div class="vertical">
- <strong>Sign in using</strong>
+ <ul class="inputs">
+ <li>
+ <label for="newEmail" class="serif">New email address</label>
+ <input id="newEmail" name="newEmail" type="email" class="sans" autocapitalize="off" autocorrect="off" />
- <div id="selectEmail" class="form_section">
- <ul class="inputs">
- <% _.each(identities, function(email_obj, email_address) { %>
- <li>
- <label for="<%= email_address %>" class="serif">
- <input type="radio" name="email" id="<%= email_address %>" value="<%= email_address %>" />
- <%= email_address %>
- </label>
- </li>
- <% }); %>
- </ul>
-
- <div class="submit add cf">
- <button id="signInButton">sign in</button>
- <p>
- <a id="thisIsNotMe" href="#">This is not me</a>
- <a id="useNewEmail" href="#">Use a different email</a>
- </p>
- </div>
+ <div id="email_format" class="tooltip" for="newEmail">
+ This field must be an email address.
</div>
- <div id="addEmail" class="cf form_section">
-
- <ul class="inputs">
- <li>
- <label for="newEmail">New email address</label>
- <input id="newEmail" name="newEmail" type="email" />
-
- <div id="email_format" class="tooltip" for="newEmail">
- This field must be an email address.
- </div>
-
- <div id="email_required" class="tooltip" for="newEmail">
- The email field is required.
- </div>
-
- <div id="could_not_add" class="tooltip" for="newEmail">
- This email address could not be added.
- </div>
-
- <div id="already_taken" class="tooltip" for="newEmail">
- This email address is already taken.
- </div>
- </li>
- </ul>
+ <div id="email_required" class="tooltip" for="newEmail">
+ The email field is required.
+ </div>
- <div class="submit cf">
- <button id="addNewEmail">add</button>
- <button id="cancelNewEmail">cancel</button>
- </div>
+ <div id="could_not_add" class="tooltip" for="newEmail">
+ This email address could not be added.
+ </div>
+ <div id="already_taken" class="tooltip" for="newEmail">
+ This email address is already taken.
</div>
+ </li>
+ </ul>
- </div>
+ <div class="submit cf">
+ <button id="addNewEmail">add</button>
+ <button id="cancelNewEmail">cancel</button>
</div>
+
</div>
View
2 browserid/static/dialog/views/testBodyTemplate.ejs
@@ -0,0 +1,2 @@
+<input type="text" value="" />
+
View
20 browserid/static/include.js
@@ -1,4 +1,7 @@
-/* ***** BEGIN LICENSE BLOCK *****
+/**
+ * Uncompressed source can be found at https://browserid.org/include.orig.js
+ *
+ * ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
@@ -33,9 +36,9 @@
*
* ***** END LICENSE BLOCK ***** */
-// this is the file that the RP includes to shim in the
-// navigator.id.getVerifiedEmail() function
(function() {
+ // this is the file that the RP includes to shim in the
+ // navigator.id.getVerifiedEmail() function
"use strict";
// local embedded copy of jschannel: http://github.com/mozilla/jschannel
@@ -595,17 +598,6 @@
var postMessage = !!window.postMessage;
var json = true;
- if(window.JSON) {
- // If there is no native JSON support, we use Crockford's JSON2 library.
- try {
- // Android < 3 blows up on this.
- JSON.parse(null);
- }
- catch(e) {
- json = false;
- }
- }
-
var explicitNo = explicitNosupport()
if(!explicitNo && !(localStorage && postMessage && json)) {
View
2 browserid/static/js/pages/manage_account.js
@@ -147,7 +147,7 @@
}
}
else {
- if (confirm('Removing the last address will cancel your BrowserID account,\nare you sure you want to continute?')) {
+ if (confirm('Removing the last address will cancel your BrowserID account.\nAre you sure you want to continue?')) {
User.cancelUser(function() {
document.location="/";
});
View
17 browserid/static/js/pages/signin.js
@@ -38,12 +38,25 @@
"use strict";
var bid = BrowserID,
- network = bid.Network,
+ user = bid.User,
validation = bid.Validation;
+ function prefillEmail() {
+ // If the user tried to sign in on the sign up page with an existing email,
+ // place that email in the email field, then focus the password.
+ var email = window.localStorage.signInEmail;
+ if (email) {
+ $("#email").val(email);
+ window.localStorage.removeItem('signInEmail');
+ $("#password").focus();
+ }
+ }
+
bid.signIn = function () {
$("form input[autofocus]").focus();
+ prefillEmail();
+
$("#signUpForm").bind("submit", function(event) {
event.preventDefault();
@@ -53,7 +66,7 @@
var valid = validation.emailAndPassword(email, password);
if (valid) {
- network.authenticate(email, password, function onSuccess(authenticated) {
+ user.authenticate(email, password, function onSuccess(authenticated) {
if (authenticated) {
document.location = "/";
}
View
1 browserid/static/js/pages/signup.js
@@ -87,6 +87,7 @@
else {
$('#registeredEmail').html(email);
showNotice(".alreadyRegistered");
+ window.localStorage.signInEmail = email;
}
}, onFailure);
});
View
6 browserid/views/about.ejs
@@ -10,7 +10,7 @@
<div class="row one cf">
<img src="/i/tutorial_1.png">
<div>
- <p><div class="steps"></div> On your favorite website that supports BrowserID, Click the &lsquo;Sign In&rsquo; button</p>
+ <div class="steps"></div> On your favorite website that supports BrowserID, Click the &lsquo;Sign In&rsquo; button
</div>
</div>
@@ -19,7 +19,7 @@
<div class="row two cf">
<img src="/i/tutorial_2.png">
<div>
- <p><div class="steps"></div> Select your preferred Email</p>
+ <div class="steps"></div> Select your preferred Email
</div>
</div>
@@ -29,7 +29,7 @@
<div class="row three cf">
<img src="/i/tutorial_3.png">
<div>
- <p><div class="steps"></div> No passwords necessary, you're done!</p>
+ <div class="steps"></div> No passwords necessary, you're done!
</div>
</div>
</div>
View
42 browserid/views/dialog.ejs
@@ -23,20 +23,42 @@
</ul>
</header>
- <div id="formWrap" class="cf">
- <form novalidate class="cf">
+ <div id="content">
+ <section id="formWrap">
+ <form novalidate>
+ <div id="favicon">
+ <div class="vertical">
+ <strong id="sitename"></strong>
+ </div>
+ </div>
- <!-- All of this is going to be replaced on startup, but we need some temporary placeholder -->
- <h2>Communicating with server</h2>
- <p>Just a moment while we talk with the server.</p>
+ <div id="signIn">
+ <div class="arrow"></div>
+ <div class="table">
+ <div class="vertical contents">
+ </div>
+ </div>
+ </div>
+ </form>
+ </section>
- </form>
- <div id="error_dialog">
- <h3 class="title"></h3>
- <p class="content"></p>
- </div>
+ <section id="wait">
+ <div class="table">
+ <div class="vertical contents">
+ <h2>Communicating with server</h2>
+ <p>Just a moment while we talk with the server.</p>
+ </div>
+ </div>
+ </section>
+
+ <section id="error">
+ <div class="table">
+ <div class="vertical contents">
+ </div>
+ </div>
+ </section>
</div>
<footer>
View
2 browserid/views/index.ejs
@@ -2,7 +2,7 @@
<div id="manage">
<h1 class="serif">Account Manager</h1>
<div class="edit cf">
- <strong>Your Accounts</strong>
+ <strong>Your Email Addresses</strong>
<a id="manageAccounts" href="#">edit</a>
<a id="cancelManage" href="#">done</a>
View
2 browserid/views/privacy.ejs
@@ -57,7 +57,7 @@
<p>Mozilla is an open organization that believes in sharing as much information as possible about its products, its operations, and its associations with its wider community. As such, BrowserID Service users should expect that Mozilla will make all Usage Statistics publicly available at some point. However, any publicly available Usage Statistics will only be reported on an aggregate, anonymous basis. No Personal Information or Potentially Personal Information will be available in any of these public reports.</p>
<h5>How to Disable or Opt-Out of BrowserID</h5>
- <p>If at any time, you decide you no longer want to use the BrowserID Service, you may cancel your BrowserID Account by visiting <tt>https://browserid.org/manage</tt> and clicking the "cancel your account" link.</p>
+ <p>If at any time, you decide you no longer want to use the BrowserID Service, you may cancel your BrowserID Account by visiting <tt>https://browserid.org/</tt>, signing in using any of your email addresses and your password, clicking the "edit" button, and clicking "remove" next to each of your email addresses.</p>
<h5>Other Disclosures</h5>
<p>In certain other limited situations, Mozilla may disclose your Personal Information, such as when necessary to protect our websites and operations (e.g., against attacks); to protect the rights, privacy, safety, or property of Mozilla or its users; to enforce our terms of service; and to pursue available legal remedies. Additionally, Mozilla may need to transfer Personal Information to an affiliate or successor in the event of a change of our corporate structure or status, such as in the event of a restructuring, sale, or bankruptcy.</p>
View
2 browserid/views/verifyuser.ejs
@@ -49,7 +49,7 @@
</form>
<div id="congrats">
- <p class="serif">Thank you signing up with <strong>BrowserID</strong>. You can now use your <strong>BrowserID</strong> account to <em>Sign In</em> or <em>Sign Up</em> to websites all across the web!</p>
+ <p class="serif">Thank you for signing up with <strong>BrowserID</strong>. You can now use your <strong>BrowserID</strong> account to <em>Sign In</em> or <em>Sign Up</em> to websites all across the web!</p>
</div>
</div>
View
2 rp/index.html
@@ -94,7 +94,7 @@
var url = "https://browserid.org/verify"
var data = {
assertion: assertion,
- audience: window.location.host
+ audience: window.location.protocol + "//" + window.location.host
};
$("#oVerificationRequest").empty().text("POST " + url + "\n" + JSON.stringify(data));
View
8 verifier/app.js
@@ -68,25 +68,25 @@ function doVerify(req, resp, next) {
certassertion.verify(
assertion, audience,
- function(email, audience, expires, issuer) {
+ function(email, audienceFromAssertion, expires, issuer) {
resp.json({
status : "okay",
email : email,
- audience : audience,
+ audience : audience, // NOTE: we return the audience formatted as the RP provided it, not normalized in any way.
expires : expires.valueOf(),
issuer: issuer
});
metrics.report('verify', {
result: 'success',
- rp: audience
+ rp: audienceFromAssertion
});
},
function(error) {
resp.json({"status":"failure", reason: (error ? error.toString() : "unknown")});
metrics.report('verify', {
result: 'failure',
- rp: audience
+ rp: audienceFromAssertion
});
});
View
53 verifier/lib/certassertion.js
@@ -118,15 +118,49 @@ function retrieveHostPublicKey(host, successCB, errorCB) {
// cache it
publicKeys[host] = pk;
-
+
return successCB(pk);
});
});
-
+
parser.parseString(hostmeta);
}, errorCB);
}
+// compare two audiences:
+// *want* is what was extracted from the assertion (it's trusted, we
+// generated it!
+// *got* is what was provided by the RP, so depending on their implementation
+// it might be strangely formed.
+function compareAudiences(want, got) {
+ try {
+ // issue #82 - for a limited time, let's allow got to be sloppy and omit scheme
+ // in which case we guess a scheme based on port
+ if (!/^https?:\/\//.test(got)) {
+ var x = got.split(':');
+ var scheme = "http";
+ if (x.length === 2 && x[1] === '443') scheme = "https";
+ got = scheme + "://" + got;
+ }
+
+ // now parse and compare
+ function normalizeParsedURL(u) {
+ if (!u.port) u.port = u.protocol === 'https:' ? 443 : 80;
+ return u;
+ }
+
+ want = normalizeParsedURL(url.parse(want));
+
+ got = normalizeParsedURL(url.parse(got));
+
+ return (want.protocol === got.protocol &&
+ want.hostname === got.hostname &&
+ want.port === got.port);
+ } catch(e) {
+ return false;
+ }
+}
+
// verify the tuple certList, assertion, audience
//
// assertion is a bundle of the underlying assertion and the cert list
@@ -156,22 +190,25 @@ function verify(assertion, audience, successCB, errorCB, pkRetriever) {
if (!principal.email.match("@" + theIssuer + "$"))
return errorCB();
}
-
+
var tok = new jwt.JWT();
tok.parse(bundle.assertion);
-
+
// audience must match!
- if (tok.audience != audience)
- return errorCB();
-
+ if (!compareAudiences(tok.audience, audience)) {
+ logger.debug("verification failure, audience mismatch: '"
+ + tok.audience + "' != '" + audience + "'");
+ return errorCB("audience mismatch");
+ }
+
if (tok.verify(pk)) {
successCB(principal.email, tok.audience, tok.expires, theIssuer);
} else {
errorCB();
}
}, errorCB);
}
-
+