diff --git a/resources/static/dialog/controllers/dialog.js b/resources/static/dialog/controllers/dialog.js index 8f670b8b238bc3d0fa85a1cb0d3f6ad3d3691ef6..ec3a1d365735b200f93d315c0d916df4ed936f18 100644 --- a/resources/static/dialog/controllers/dialog.js +++ b/resources/static/dialog/controllers/dialog.js @@ -184,16 +184,19 @@ BrowserID.Modules.Dialog = (function() { params.siteName = _.escape(paramsFromRP.siteName); } - var originHREF = paramsFromRP.originHREF; - - // Native implementations will be behind on the originHREF feature. Until - // they are ready, set the originHREF to be the origin_url; - if (!originHREF) originHREF = origin_url; - - if(originHREF.indexOf(origin_url) !== 0) { - throw "originHREF/origin mismatch"; + // 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); } - user.setOriginHREF(fixupURL(origin_url, originHREF)); + 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; + } + + 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 053b1b58fcad9bffcb9cab7ca7a2eac3c386027b..b4da74e0cd095e51a08e0c5831b9c88d40d07e3f 100644 --- a/resources/static/include_js/include.js +++ b/resources/static/include_js/include.js @@ -1069,8 +1069,8 @@ // don't do duplicative work if (commChan) commChan.notify({ method: 'dialog_running' }); - // originHREF is used for post-email-verification redirect - options.originHREF = document.location.href; + // returnTo is used for post-email-verification redirect + if (!options.returnTo) options.returnTo = document.location.href; w = WinChan.open({ url: ipServer + '/sign_in', diff --git a/resources/static/shared/storage.js b/resources/static/shared/storage.js index 53ff05adc9496b39cec096984e72897a96492924..7f055c069e5df2c03c27361ad853604ba82b5db0 100644 --- a/resources/static/shared/storage.js +++ b/resources/static/shared/storage.js @@ -146,18 +146,18 @@ BrowserID.Storage = (function() { } } - function setStagedOnBehalfOf(origin) { - storage.stagedOnBehalfOf = JSON.stringify({ + function setReturnTo(origin) { + storage.returnTo = JSON.stringify({ at: new Date().toString(), origin: origin }); } - function getStagedOnBehalfOf() { + function getReturnTo() { var origin; try { - var staged = JSON.parse(storage.stagedOnBehalfOf); + var staged = JSON.parse(storage.returnTo || storage.stagedOnBehalfOf); if (staged) { if ((new Date() - new Date(staged.at)) > (5 * 60 * 1000)) throw "stale"; @@ -165,6 +165,7 @@ BrowserID.Storage = (function() { origin = staged.origin; } } catch (x) { + storage.removeItem("returnTo"); storage.removeItem("stagedOnBehalfOf"); } @@ -569,7 +570,7 @@ BrowserID.Storage = (function() { clear: clear, storeTemporaryKeypair: storeTemporaryKeypair, retrieveTemporaryKeypair: retrieveTemporaryKeypair, - setStagedOnBehalfOf: setStagedOnBehalfOf, - getStagedOnBehalfOf: getStagedOnBehalfOf + setReturnTo: setReturnTo, + getReturnTo: getReturnTo }; }()); diff --git a/resources/static/shared/user.js b/resources/static/shared/user.js index b0445716346abe5a2deba5f80930dac55e42272e..2d293c7e0d80790903ad6d6b1778a5e17e31706e 100644 --- a/resources/static/shared/user.js +++ b/resources/static/shared/user.js @@ -113,7 +113,7 @@ BrowserID.User = (function() { // As soon as the registration comes back as complete, we should // ensure that the stagedOnBehalfOf is cleared so there is no stale // data. - storage.setStagedOnBehalfOf(""); + storage.setReturnTo(""); // To avoid too many address_info requests, returns from each // address_info request are cached. If the user is doing @@ -273,12 +273,12 @@ BrowserID.User = (function() { return origin.replace(/^.*:\/\//, "").replace(/:\d*$/, ""); }, - setOriginHREF: function(originHREF) { - this.originHREF = originHREF; + setReturnTo: function(returnTo) { + this.returnTo = returnTo; }, - getOriginHREF: function() { - return this.originHREF; + getReturnTo: function() { + return this.returnTo; }, /** @@ -292,8 +292,8 @@ BrowserID.User = (function() { network.createUser(email, password, origin, function(created) { // Used on the main site when the user verifies - once verification // is complete, the user is redirected back to the RP and logged in. - var site = User.getOriginHREF(); - if (created && site) storage.setStagedOnBehalfOf(site); + var site = User.getReturnTo(); + if (created && site) storage.setReturnTo(site); complete(onComplete, created); }, onFailure); }, @@ -486,7 +486,7 @@ BrowserID.User = (function() { tokenInfo: function(token, onComplete, onFailure) { network.emailForVerificationToken(token, function (info) { if(info) { - info = _.extend(info, { origin: storage.getStagedOnBehalfOf() }); + info = _.extend(info, { origin: storage.getReturnTo() }); } onComplete && onComplete(info); @@ -512,8 +512,8 @@ BrowserID.User = (function() { var result = invalidInfo; if(valid) { - result = _.extend({ valid: valid, origin: storage.getStagedOnBehalfOf() }, info); - storage.setStagedOnBehalfOf(""); + result = _.extend({ valid: valid, origin: storage.getReturnTo() }, info); + storage.setReturnTo(""); } complete(onComplete, result); @@ -582,8 +582,8 @@ BrowserID.User = (function() { // Used on the main site when the user verifies - once // verification is complete, the user is redirected back to the // RP and logged in. - var site = User.getOriginHREF(); - if (reset && site) storage.setStagedOnBehalfOf(site); + var site = User.getReturnTo(); + if (reset && site) storage.setReturnTo(site); complete(onComplete, status); }, onFailure); @@ -816,8 +816,8 @@ BrowserID.User = (function() { network.addSecondaryEmail(email, password, origin, function(added) { // Used on the main site when the user verifies - once verification // is complete, the user is redirected back to the RP and logged in. - var originHREF = User.getOriginHREF(); - if (added && originHREF) storage.setStagedOnBehalfOf(originHREF); + var returnTo = User.getReturnTo(); + if (added && returnTo) storage.setReturnTo(returnTo); // we no longer send the keypair, since we will certify it later. complete(onComplete, added); @@ -882,8 +882,8 @@ BrowserID.User = (function() { var result = invalidInfo; if(valid) { - result = _.extend({ valid: valid, origin: storage.getStagedOnBehalfOf() }, info); - storage.setStagedOnBehalfOf(""); + result = _.extend({ valid: valid, origin: storage.getReturnTo() }, info); + storage.setReturnTo(""); } complete(onComplete, result); diff --git a/resources/static/test/cases/controllers/dialog.js b/resources/static/test/cases/controllers/dialog.js index ebd95b2b12932d258356ab4c34afdab514a4b8c5..aedd54d97475b720ed0affd26f640161948dfd04 100644 --- a/resources/static/test/cases/controllers/dialog.js +++ b/resources/static/test/cases/controllers/dialog.js @@ -18,7 +18,6 @@ user = bid.User, HTTP_TEST_DOMAIN = "http://testdomain.org", HTTPS_TEST_DOMAIN = "https://testdomain.org", - HTTPS_TEST_URL = "https://testdomain.org/some/page", TESTEMAIL = "testuser@testuser.com", controller, el, @@ -585,33 +584,36 @@ }); - asyncTest("originHREF specified, different domain as origin - error thrown", function() { + asyncTest("get with returnTo with https - not allowed", function() { createController({ ready: function() { + var URL = HTTP_TEST_DOMAIN + "/path"; + mediator.subscribe("start", function(msg, info) { ok(false, "unexpected start"); }); var retval = controller.get(HTTP_TEST_DOMAIN, { - originHREF: "http://different.domain" + returnTo: URL }); - equal(retval, "originHREF/origin mismatch", "expected error"); + + equal(retval, "must be an absolute path: (" + URL + ")", "expected error"); testErrorVisible(); start(); } }); }); - asyncTest("originHREF specified, same domain as origin - originHREF correctly set", function() { + asyncTest("get with absolute path returnTo - allowed", function() { createController({ ready: function() { mediator.subscribe("start", function(msg, info) { - equal(user.getOriginHREF(), HTTPS_TEST_URL, "Origin HREF correctly set"); + equal(user.getReturnTo(), HTTPS_TEST_DOMAIN + "/path", "returnTo correctly set"); start(); }); var retval = controller.get(HTTPS_TEST_DOMAIN, { - originHREF: HTTPS_TEST_URL + returnTo: "/path" }); } }); diff --git a/resources/static/test/cases/pages/verify_secondary_address.js b/resources/static/test/cases/pages/verify_secondary_address.js index 79bfcd6ab6ce324199b0c453365859d1c86c0ac0..99d2917af1a4c93aa1429069c449ae532f30d055 100644 --- a/resources/static/test/cases/pages/verify_secondary_address.js +++ b/resources/static/test/cases/pages/verify_secondary_address.js @@ -71,7 +71,7 @@ }); asyncTest("no password: start with good token and site", function() { - storage.setStagedOnBehalfOf("browserid.org"); + storage.setReturnTo("browserid.org"); createController(config, function() { testEmail(); diff --git a/resources/static/test/cases/shared/storage.js b/resources/static/test/cases/shared/storage.js index c9406c84a21ef077b4a150f956a3666a0eccc505..5a7048c00799dfa54ebd351407cf45199264aec9 100644 --- a/resources/static/test/cases/shared/storage.js +++ b/resources/static/test/cases/shared/storage.js @@ -164,12 +164,9 @@ // XXX needs a test }); - test("setStagedOnBehalfOf", function() { - // XXX needs a test - }); - - test("getStagedOnBehalfOf", function() { - // XXX needs a test + test("setReturnTo", function() { + storage.setReturnTo("http://some.domain/path"); + equal(storage.getReturnTo(), "http://some.domain/path", "setReturnTo/getReturnTo working as expected"); }); test("signInEmail.set/.get/.remove - set, get, and remove the signInEmail", function() { diff --git a/resources/static/test/cases/shared/user.js b/resources/static/test/cases/shared/user.js index 0a3a8cdb2e08197d120d48a16211c71ee2deab2d..9da4f8474665ec7be3f35a6e1e5f8e7be80c5dbd 100644 --- a/resources/static/test/cases/shared/user.js +++ b/resources/static/test/cases/shared/user.js @@ -85,10 +85,10 @@ var jwcrypto = require("./lib/jwcrypto"); equal(hostname, "browserid.org", "getHostname returns only the hostname"); }); - test("setOriginHREF, getOriginHREF", function() { - var originHREF = "http://samplerp.org"; - lib.setOriginHREF(originHREF); - equal(lib.getOriginHREF(), originHREF, "get/setOriginHREF work as expected"); + test("setReturnTo, getReturnTo", function() { + var returnTo = "http://samplerp.org"; + lib.setReturnTo(returnTo); + equal(lib.getReturnTo(), returnTo, "get/setReturnTo work as expected"); }); test("getStoredEmailKeypairs without key - return all identities", function() { @@ -298,27 +298,27 @@ var jwcrypto = require("./lib/jwcrypto"); }); asyncTest("waitForUserValidation with `complete` response", function() { - storage.setStagedOnBehalfOf(testOrigin); + storage.setReturnTo(testOrigin); xhr.useResult("complete"); lib.waitForUserValidation("registered@testuser.com", function(status) { equal(status, "complete", "complete response expected"); - ok(!storage.getStagedOnBehalfOf(), "staged on behalf of is cleared when validation completes"); + ok(!storage.getReturnTo(), "staged on behalf of is cleared when validation completes"); start(); }, testHelpers.unexpectedXHRFailure); }); asyncTest("waitForUserValidation with `mustAuth` response", function() { - storage.setStagedOnBehalfOf(testOrigin); + storage.setReturnTo(testOrigin); xhr.useResult("mustAuth"); lib.waitForUserValidation("registered@testuser.com", function(status) { equal(status, "mustAuth", "mustAuth response expected"); - ok(!storage.getStagedOnBehalfOf(), "staged on behalf of is cleared when validation completes"); + ok(!storage.getReturnTo(), "staged on behalf of is cleared when validation completes"); start(); }, testHelpers.unexpectedXHRFailure); }); @@ -326,12 +326,12 @@ var jwcrypto = require("./lib/jwcrypto"); asyncTest("waitForUserValidation with `noRegistration` response", function() { xhr.useResult("noRegistration"); - storage.setStagedOnBehalfOf(testOrigin); + storage.setReturnTo(testOrigin); lib.waitForUserValidation( "registered@testuser.com", testHelpers.unexpectedSuccess, function(status) { - ok(storage.getStagedOnBehalfOf(), "staged on behalf of is not cleared for noRegistration response"); + ok(storage.getReturnTo(), "staged on behalf of is not cleared for noRegistration response"); ok(status, "noRegistration", "noRegistration response causes failure"); start(); } @@ -340,12 +340,12 @@ var jwcrypto = require("./lib/jwcrypto"); asyncTest("waitForUserValidation with XHR failure", function() { - storage.setStagedOnBehalfOf(testOrigin); + storage.setReturnTo(testOrigin); lib.waitForUserValidation( "registered@testuser.com", testHelpers.unexpectedSuccess, function() { - ok(storage.getStagedOnBehalfOf(), "staged on behalf of is not cleared on XHR failure"); + ok(storage.getReturnTo(), "staged on behalf of is not cleared on XHR failure"); ok(true, "xhr failure should always be a failure"); start(); } @@ -355,7 +355,7 @@ var jwcrypto = require("./lib/jwcrypto"); asyncTest("cancelUserValidation: ~1 second", function() { xhr.useResult("pending"); - storage.setStagedOnBehalfOf(testOrigin); + storage.setReturnTo(testOrigin); // yes, we are neither expected succes nor failure because we are // cancelling the wait. lib.waitForUserValidation( @@ -366,13 +366,13 @@ var jwcrypto = require("./lib/jwcrypto"); setTimeout(function() { lib.cancelUserValidation(); - ok(storage.getStagedOnBehalfOf(), "staged on behalf of is not cleared when validation cancelled"); + ok(storage.getReturnTo(), "staged on behalf of is not cleared when validation cancelled"); start(); }, 500); }); asyncTest("tokenInfo with a good token and origin info, expect origin in results", function() { - storage.setStagedOnBehalfOf(testOrigin); + storage.setReturnTo(testOrigin); lib.tokenInfo("token", function(info) { equal(info.email, TEST_EMAIL, "correct email"); @@ -394,14 +394,14 @@ var jwcrypto = require("./lib/jwcrypto"); }); asyncTest("verifyUser with a good token", function() { - storage.setStagedOnBehalfOf(testOrigin); + storage.setReturnTo(testOrigin); lib.verifyUser("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.getStagedOnBehalfOf(), "", "initiating origin was removed"); + equal(storage.getReturnTo(), "", "initiating origin was removed"); start(); }, testHelpers.unexpectedXHRFailure); @@ -467,12 +467,12 @@ var jwcrypto = require("./lib/jwcrypto"); }); asyncTest("requestPasswordReset with known email - true status", function() { - var originHREF = "http://samplerp.org"; - lib.setOriginHREF(originHREF); + var returnTo = "http://samplerp.org"; + lib.setReturnTo(returnTo); lib.requestPasswordReset("registered@testuser.com", "password", function(status) { equal(status.success, true, "password reset for known user"); - equal(storage.getStagedOnBehalfOf(), originHREF, "RP URL is stored for verification"); + equal(storage.getReturnTo(), returnTo, "RP URL is stored for verification"); start(); }, testHelpers.unexpectedXHRFailure); @@ -653,8 +653,8 @@ var jwcrypto = require("./lib/jwcrypto"); }); asyncTest("addEmail", function() { - var originHREF = "http://samplerp.org"; - lib.setOriginHREF(originHREF); + var returnTo = "http://samplerp.org"; + lib.setReturnTo(returnTo); lib.addEmail("testemail@testemail.com", "password", function(added) { ok(added, "user was added"); @@ -662,7 +662,7 @@ var jwcrypto = require("./lib/jwcrypto"); var identities = lib.getStoredEmailKeypairs(); equal("testemail@testemail.com" in identities, false, "new email is not added until confirmation."); - equal(storage.getStagedOnBehalfOf(), originHREF, "RP URL is stored for verification"); + equal(storage.getReturnTo(), returnTo, "RP URL is stored for verification"); start(); }, testHelpers.unexpectedXHRFailure); @@ -677,7 +677,7 @@ var jwcrypto = require("./lib/jwcrypto"); var identities = lib.getStoredEmailKeypairs(); equal(false, "testemail@testemail.com" in identities, "Our new email is not added until confirmation."); - equal(typeof storage.getStagedOnBehalfOf(), "undefined", "initiatingOrigin is not stored"); + equal(typeof storage.getReturnTo(), "undefined", "initiatingOrigin is not stored"); start(); }, testHelpers.unexpectedXHRFailure); @@ -689,36 +689,36 @@ var jwcrypto = require("./lib/jwcrypto"); asyncTest("waitForEmailValidation `complete` response", function() { - storage.setStagedOnBehalfOf(testOrigin); + storage.setReturnTo(testOrigin); xhr.useResult("complete"); lib.waitForEmailValidation("registered@testuser.com", function(status) { - ok(!storage.getStagedOnBehalfOf(), "staged on behalf of is cleared when validation completes"); + ok(!storage.getReturnTo(), "staged on behalf of is cleared when validation completes"); equal(status, "complete", "complete response expected"); start(); }, testHelpers.unexpectedXHRFailure); }); asyncTest("waitForEmailValidation `mustAuth` response", function() { - storage.setStagedOnBehalfOf(testOrigin); + storage.setReturnTo(testOrigin); xhr.useResult("mustAuth"); lib.waitForEmailValidation("registered@testuser.com", function(status) { - ok(!storage.getStagedOnBehalfOf(), "staged on behalf of is cleared when validation completes"); + ok(!storage.getReturnTo(), "staged on behalf of is cleared when validation completes"); equal(status, "mustAuth", "mustAuth response expected"); start(); }, testHelpers.unexpectedXHRFailure); }); asyncTest("waitForEmailValidation with `noRegistration` response", function() { - storage.setStagedOnBehalfOf(testOrigin); + storage.setReturnTo(testOrigin); xhr.useResult("noRegistration"); lib.waitForEmailValidation( "registered@testuser.com", testHelpers.unexpectedSuccess, function(status) { - ok(storage.getStagedOnBehalfOf(), "staged on behalf of is cleared when validation completes"); + ok(storage.getReturnTo(), "staged on behalf of is cleared when validation completes"); ok(status, "noRegistration", "noRegistration response causes failure"); start(); }); @@ -726,7 +726,7 @@ var jwcrypto = require("./lib/jwcrypto"); asyncTest("waitForEmailValidation XHR failure", function() { - storage.setStagedOnBehalfOf(testOrigin); + storage.setReturnTo(testOrigin); xhr.useResult("ajaxError"); lib.waitForEmailValidation( @@ -740,7 +740,7 @@ var jwcrypto = require("./lib/jwcrypto"); asyncTest("cancelEmailValidation: ~1 second", function() { xhr.useResult("pending"); - storage.setStagedOnBehalfOf(testOrigin); + storage.setReturnTo(testOrigin); lib.waitForEmailValidation( "registered@testuser.com", testHelpers.unexpectedSuccess, @@ -749,19 +749,19 @@ var jwcrypto = require("./lib/jwcrypto"); setTimeout(function() { lib.cancelUserValidation(); - ok(storage.getStagedOnBehalfOf(), "staged on behalf of is not cleared when validation cancelled"); + ok(storage.getReturnTo(), "staged on behalf of is not cleared when validation cancelled"); start(); }, 500); }); asyncTest("verifyEmail with a good token - callback with email, origin, valid", function() { - storage.setStagedOnBehalfOf(testOrigin); + 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.getStagedOnBehalfOf(), "", "initiating origin was removed"); + equal(storage.getReturnTo(), "", "initiating origin was removed"); start(); }, testHelpers.unexpectedXHRFailure); @@ -1213,7 +1213,7 @@ var jwcrypto = require("./lib/jwcrypto"); asyncTest("shouldAskIfUsersComputer with user who has not been asked and has verified email in this dialog session - call onSuccess with false", function() { lib.authenticate(TEST_EMAIL, "password", function() { - storage.setStagedOnBehalfOf(testOrigin); + storage.setReturnTo(testOrigin); xhr.useResult("complete"); lib.waitForEmailValidation(TEST_EMAIL, function() {