diff --git a/example/rp/index.html b/example/rp/index.html index fa60c2b2e8c2a55fa1c568a443364417ba54ffc5..3d4740668c1de345c3fae0d8b97abe2a24f122f0 100644 --- a/example/rp/index.html +++ b/example/rp/index.html @@ -73,6 +73,10 @@ pre { <input type="checkbox" id="siteLogo"> <label for="siteLogo">Supply Site Logo</label><br /> </li> + </li><li> + <input type="checkbox" id="returnTo"> + <label for="returnTo">Supply returnTo</label><br /> + </li> </li><li> <input type="text" id="requiredEmail" width="80"> <label for="requiredEmail">Require a specific email</label><br /> @@ -193,6 +197,7 @@ $(document).ready(function() { termsOfService: $('#termsOfService').attr('checked') ? "/TOS.html" : undefined, siteName: $('#siteName').attr('checked') ? "Persona Test Relying Party" : undefined, siteLogo: $('#siteLogo').attr('checked') ? "/i/logo.png" : undefined, + returnTo: $('#returnTo').attr('checked') ? "/postVerificationReturn.html" : undefined, requiredEmail: requiredEmail, oncancel: function() { loggit("oncancel"); diff --git a/example/rp/postVerificationReturn.html b/example/rp/postVerificationReturn.html new file mode 100644 index 0000000000000000000000000000000000000000..518c7abbcd7e3073215336abff5adfb523c97815 --- /dev/null +++ b/example/rp/postVerificationReturn.html @@ -0,0 +1,142 @@ +<!DOCTYPE html> +<!-- 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/. --> + +<html> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="initial-scale=1.0; maximum-scale=1.0; width=device-width;"> +<title> +Persona Relying Party Post Verification Return +</title> +<style type="text/css"> + +body { margin: auto; font: 13px/1.5 Helvetica, Arial, 'Liberation Sans', FreeSans, sans-serif; } +a:link, a:visited { font-style: italic; text-decoration: none; color: #008; } +a:hover { border-bottom: 2px solid black ; } +.title { font-size: 2em; font-weight: bold; text-align: center; margin: 1.5em auto 1.5em auto; } +.intro { font-size: 1.2em; } +.specify, .session { font-size: 1.1em; padding-top: 2em; } +body div { width: 600px; margin: auto; } + +pre { + font-family: 'lucida console', monaco, 'andale mono', 'bitstream vera sans mono', consolas, monospace; + border: 3px solid #666; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + padding: .5em; + margin: .5em; + color: #ccc; + background-color: #333; +/* white-space: pre;*/ + font-size: .9em; + word-wrap: break-word; +} + +.specify ul { padding-left: 0px; } +.specify li { list-style: none; } + +@media screen and (max-width: 640px) { + .intro, .output, .step { + width: 90%; + } +} + +</style> +</head> +<body> +<div class="title"> + Persona Relying Party Post Verification Return +</div> + +<div class="intro"> + This is part of a RP for testing, it is the returnTo for post-verification redirect. + <p> + <a href="/">Return to RP Test Page</a> + </p> +</div> + +<div class="loginEvents"> + <h2>logins</h2> + <pre> ... </pre> +</div> + + +<div class="readiness"> + <h2>readiness</h2> + <pre> ... </pre> +</div> + +</body> + +<script src="jquery-min.js"></script> +<script src="https://browserid.org/include.js"></script> +<script> + +try { + var storage = localStorage; +} +catch(e) { + // Fx with cookies disabled with blow up when trying to access localStorage. + storage = {}; +} + + +function loggit() { + try { + console.log.apply(console, arguments); + } catch(e) {} +} + +var serial = 1; + +// a function to check an assertion against the server +function checkAssertion(assertion) { + $.ajax({ + url: "/process_assertion", + type: "post", + dataType: "json", + data: { + assertion: assertion, + audience: window.location.protocol + "//" + window.location.host + }, + success: function(data, textStatus, jqXHR) { + var old = $(".loginEvents > pre").text() + "\n"; + $(".loginEvents > pre").text(old + JSON.stringify(data, null, 4)); + }, + error: function(jqXHR, textStatus, errorThrown) { + var resp = jqXHR.responseText ? JSON.parse(jqXHR.responseText) : errorThrown; + $(".loginEvents > pre").text(resp); + } + }); +}; + +navigator.id.watch({ + loggedInEmail: (storage.loggedInUser === 'null') ? null : storage.loggedInUser, + onready: function () { + loggit("onready"); + var txt = serial++ + ' navigator.id ready at ' + (new Date).toString(); + $(".readiness > pre").text(txt); + + }, + onlogin: function (assertion) { + loggit("onlogin"); + var txt = serial++ + ' got assertion at ' + (new Date).toString(); + $(".loginEvents > pre").text(txt); + + checkAssertion(assertion); + + $(".specify button.assertion").removeAttr('disabled'); + }, + onlogout: function () { + loggit("onlogout"); + var txt = serial++ + ' logout callback invoked at ' + (new Date).toString(); + $(".logoutEvents > pre").text(txt); + } +}); + +</script> + +</html> diff --git a/resources/static/dialog/controllers/dialog.js b/resources/static/dialog/controllers/dialog.js index ec3a1d365735b200f93d315c0d916df4ed936f18..36e66dca60e3ccc6310246344cb029f22d637376 100644 --- a/resources/static/dialog/controllers/dialog.js +++ b/resources/static/dialog/controllers/dialog.js @@ -186,17 +186,11 @@ BrowserID.Modules.Dialog = (function() { // returnTo is used for post verification redirection. Redirect back // to the path specified by the RP. - var returnTo; if (paramsFromRP.returnTo) { - returnTo = fixupAbsolutePath(origin_url, paramsFromRP.returnTo); - } - else { - // Native implementations will be behind on the returnTo feature. Until - // they are ready, set the returnTo to be the origin_url; - returnTo = origin_url; + var returnTo = fixupAbsolutePath(origin_url, paramsFromRP.returnTo); + user.setReturnTo(returnTo); } - user.setReturnTo(returnTo); if (hash.indexOf("#CREATE_EMAIL=") === 0) { var email = hash.replace(/#CREATE_EMAIL=/, ""); diff --git a/resources/static/include_js/include.js b/resources/static/include_js/include.js index b4da74e0cd095e51a08e0c5831b9c88d40d07e3f..f935a32684b746fbb490d6d05e493bbcc107410d 100644 --- a/resources/static/include_js/include.js +++ b/resources/static/include_js/include.js @@ -1070,7 +1070,7 @@ if (commChan) commChan.notify({ method: 'dialog_running' }); // returnTo is used for post-email-verification redirect - if (!options.returnTo) options.returnTo = document.location.href; + if (!options.returnTo) options.returnTo = document.location.pathname; w = WinChan.open({ url: ipServer + '/sign_in', diff --git a/resources/static/pages/verify_secondary_address.js b/resources/static/pages/verify_secondary_address.js index 881a9304133d004c29446446b82b483a662d7382..47ce8d191a3679971729c5a5988580d69c763e96 100644 --- a/resources/static/pages/verify_secondary_address.js +++ b/resources/static/pages/verify_secondary_address.js @@ -34,8 +34,8 @@ BrowserID.verifySecondaryAddress = (function() { function showRegistrationInfo(info) { dom.setInner("#email", info.email); - if (info.origin) { - dom.setInner(".website", info.origin); + if (info.returnTo) { + dom.setInner(".website", info.returnTo); dom.show(".siteinfo"); } } @@ -84,7 +84,7 @@ BrowserID.verifySecondaryAddress = (function() { function startVerification(oncomplete) { user.tokenInfo(token, function(info) { if (info) { - redirectTo = info.origin; + redirectTo = info.returnTo; showRegistrationInfo(info); needsPassword = info.needs_password; diff --git a/resources/static/shared/storage.js b/resources/static/shared/storage.js index 7f055c069e5df2c03c27361ad853604ba82b5db0..7b4e768ac2d2ab3d37e36365fed4b919fd3c039b 100644 --- a/resources/static/shared/storage.js +++ b/resources/static/shared/storage.js @@ -146,30 +146,50 @@ BrowserID.Storage = (function() { } } - function setReturnTo(origin) { + function setReturnTo(returnToURL) { storage.returnTo = JSON.stringify({ at: new Date().toString(), - origin: origin + url: returnToURL }); } function getReturnTo() { - var origin; + var returnToURL; + // XXX - The transitional code is to make sure any emails that were staged using + // the old setStagedOnBehalfOf still work with the new API. This should be + // able to be removed by mid-July 2012. try { - var staged = JSON.parse(storage.returnTo || storage.stagedOnBehalfOf); + // BEGIN TRANSITIONAL CODE + if (storage.returnTo) { + // END TRANSITIONAL CODE + var staged = JSON.parse(storage.returnTo); + + if (staged) { + if ((new Date() - new Date(staged.at)) > (5 * 60 * 1000)) throw "stale"; + if (typeof(staged.url) !== 'string') throw "malformed"; + returnToURL = staged.url; + } + // BEGIN TRANSITIONAL CODE + } + else if(storage.stagedOnBehalfOf) { + var staged = JSON.parse(storage.stagedOnBehalfOf); - if (staged) { - if ((new Date() - new Date(staged.at)) > (5 * 60 * 1000)) throw "stale"; - if (typeof(staged.origin) !== 'string') throw "malformed"; - origin = staged.origin; + if (staged) { + if ((new Date() - new Date(staged.at)) > (5 * 60 * 1000)) throw "stale"; + if (typeof(staged.origin) !== 'string') throw "malformed"; + returnToURL = staged.origin; + } } + // END TRANSITIONAL CODE } catch (x) { storage.removeItem("returnTo"); + // BEGIN TRANSITIONAL CODE storage.removeItem("stagedOnBehalfOf"); + // END TRANSITIONAL CODE } - return origin; + return returnToURL; } function siteSet(site, key, value) { diff --git a/resources/static/shared/user.js b/resources/static/shared/user.js index 2d293c7e0d80790903ad6d6b1778a5e17e31706e..0eda75118ec733971c52d64458e5195ed90d3740 100644 --- a/resources/static/shared/user.js +++ b/resources/static/shared/user.js @@ -282,7 +282,8 @@ BrowserID.User = (function() { }, /** - * Create a user account - this creates an user account that must be verified. * @method createSecondaryUser + * Create a user account - this creates an user account that must be verified. + * @method createSecondaryUser * @param {string} email * @param {string} password * @param {function} [onComplete] - Called on completion. @@ -486,10 +487,10 @@ BrowserID.User = (function() { tokenInfo: function(token, onComplete, onFailure) { network.emailForVerificationToken(token, function (info) { if(info) { - info = _.extend(info, { origin: storage.getReturnTo() }); + info = _.extend(info, { returnTo: storage.getReturnTo() }); } - onComplete && onComplete(info); + complete(onComplete, info); }, onFailure); }, @@ -512,7 +513,7 @@ BrowserID.User = (function() { var result = invalidInfo; if(valid) { - result = _.extend({ valid: valid, origin: storage.getReturnTo() }, info); + result = _.extend({ valid: valid, returnTo: storage.getReturnTo() }, info); storage.setReturnTo(""); } @@ -882,7 +883,7 @@ BrowserID.User = (function() { var result = invalidInfo; if(valid) { - result = _.extend({ valid: valid, origin: storage.getReturnTo() }, info); + result = _.extend({ valid: valid, returnTo: storage.getReturnTo() }, info); storage.setReturnTo(""); } diff --git a/resources/static/test/cases/shared/user.js b/resources/static/test/cases/shared/user.js index 9da4f8474665ec7be3f35a6e1e5f8e7be80c5dbd..5fdc5529ea9be7bac3e0eb767a0970a335034e67 100644 --- a/resources/static/test/cases/shared/user.js +++ b/resources/static/test/cases/shared/user.js @@ -371,20 +371,20 @@ var jwcrypto = require("./lib/jwcrypto"); }, 500); }); - asyncTest("tokenInfo with a good token and origin info, expect origin in results", function() { + asyncTest("tokenInfo with a good token and returnTo info, expect returnTo in results", function() { storage.setReturnTo(testOrigin); lib.tokenInfo("token", function(info) { equal(info.email, TEST_EMAIL, "correct email"); - equal(info.origin, testOrigin, "correct origin"); + equal(info.returnTo, testOrigin, "correct returnTo"); start(); }, testHelpers.unexpectedXHRFailure); }); - asyncTest("tokenInfo with a bad token without site info, no site in results", function() { + asyncTest("tokenInfo with a bad token without returnTo info, no returnTo in results", function() { lib.tokenInfo("token", function(info) { equal(info.email, TEST_EMAIL, "correct email"); - equal(typeof info.origin, "undefined", "origin is undefined"); + equal(typeof info.returnTo, "undefined", "returnTo is undefined"); start(); }, testHelpers.unexpectedXHRFailure); }); @@ -400,7 +400,7 @@ var jwcrypto = require("./lib/jwcrypto"); ok(info.valid, "token was valid"); equal(info.email, TEST_EMAIL, "email part of info"); - equal(info.origin, testOrigin, "origin in info"); + equal(info.returnTo, testOrigin, "returnTo in info"); equal(storage.getReturnTo(), "", "initiating origin was removed"); start(); @@ -754,14 +754,14 @@ var jwcrypto = require("./lib/jwcrypto"); }, 500); }); - asyncTest("verifyEmail with a good token - callback with email, origin, valid", function() { + asyncTest("verifyEmail with a good token - callback with email, returnTo, valid", function() { storage.setReturnTo(testOrigin); lib.verifyEmail("token", "password", function onSuccess(info) { ok(info.valid, "token was valid"); equal(info.email, TEST_EMAIL, "email part of info"); - equal(info.origin, testOrigin, "origin in info"); - equal(storage.getReturnTo(), "", "initiating origin was removed"); + equal(info.returnTo, testOrigin, "returnTo in info"); + equal(storage.getReturnTo(), "", "initiating returnTo was removed"); start(); }, testHelpers.unexpectedXHRFailure);