diff --git a/packages/accounts-base/accounts_client.js b/packages/accounts-base/accounts_client.js index f0d3ac423c..4786d011e9 100644 --- a/packages/accounts-base/accounts_client.js +++ b/packages/accounts-base/accounts_client.js @@ -16,6 +16,9 @@ AccountsClient = function AccountsClient(options) { this._pageLoadLoginCallbacks = []; this._pageLoadLoginAttemptInfo = null; + + // Defined in url_client.js. + this._initUrlMatching(); }; var Ap = AccountsClient.prototype = diff --git a/packages/accounts-base/package.js b/packages/accounts-base/package.js index dceee430db..b497996d5d 100644 --- a/packages/accounts-base/package.js +++ b/packages/accounts-base/package.js @@ -38,7 +38,6 @@ Package.onUse(function (api) { api.addFiles('accounts_common.js', ['client', 'server']); api.addFiles('accounts_server.js', 'server'); - api.addFiles('url_client.js', 'client'); api.addFiles('url_server.js', 'server'); // accounts_client must be before localstorage_token, because @@ -46,6 +45,7 @@ Package.onUse(function (api) { // Accounts.callLoginMethod) on startup. And localstorage_token must be after // url_client, which sets autoLoginEnabled. api.addFiles('accounts_client.js', 'client'); + api.addFiles('url_client.js', 'client'); api.addFiles('localstorage_token.js', 'client'); }); diff --git a/packages/accounts-base/url_client.js b/packages/accounts-base/url_client.js index 202784f15a..725b717bf2 100644 --- a/packages/accounts-base/url_client.js +++ b/packages/accounts-base/url_client.js @@ -1,11 +1,30 @@ -// By default, allow the autologin process to happen -autoLoginEnabled = true; +var Ap = AccountsClient.prototype; // All of the special hash URLs we support for accounts interactions var accountsPaths = ["reset-password", "verify-email", "enroll-account"]; +var savedHash = window.location.hash; + +Ap._initUrlMatching = function () { + // By default, allow the autologin process to happen. + this._autoLoginEnabled = true; + + // We only support one callback per URL. + this._accountsCallbacks = {}; + + // Try to match the saved value of window.location.hash. + this._attemptToMatchHash(); +}; + // Separate out this functionality for testing -var attemptToMatchHash = function (hash, success) { + +Ap._attemptToMatchHash = function () { + attemptToMatchHash(this, savedHash, defaultSuccessHandler); +}; + +// Note that both arguments are optional and are currently only passed by +// accounts_url_tests.js. +function attemptToMatchHash(accounts, hash, success) { _.each(accountsPaths, function (urlPart) { var token; @@ -17,50 +36,50 @@ var attemptToMatchHash = function (hash, success) { // XXX COMPAT WITH 0.9.3 if (urlPart === "reset-password") { - Accounts._resetPasswordToken = token; + accounts._resetPasswordToken = token; } else if (urlPart === "verify-email") { - Accounts._verifyEmailToken = token; + accounts._verifyEmailToken = token; } else if (urlPart === "enroll-account") { - Accounts._enrollAccountToken = token; + accounts._enrollAccountToken = token; } } else { return; } + // If no handlers match the hash, then maybe it's meant to be consumed + // by some entirely different code, so we only clear it the first time + // a handler successfully matches. Note that later handlers reuse the + // savedHash, so clearing window.location.hash here will not interfere + // with their needs. + window.location.hash = ""; + // Do some stuff with the token we matched - success(token, urlPart); + success.call(accounts, token, urlPart); }); -}; +} -// We only support one callback per URL -var accountsCallbacks = {}; +function defaultSuccessHandler(token, urlPart) { + var self = this; -// The UI flow will call this when done to log in the existing person -var enableAutoLogin = function () { - Accounts._enableAutoLogin(); -}; - -// Actually call the function, has to happen in the top level so that we can -// mess with autoLoginEnabled. -attemptToMatchHash(window.location.hash, function (token, urlPart) { // put login in a suspended state to wait for the interaction to finish - autoLoginEnabled = false; - - // reset the URL - window.location.hash = ""; + self._autoLoginEnabled = false; // wait for other packages to register callbacks Meteor.startup(function () { // if a callback has been registered for this kind of token, call it - if (accountsCallbacks[urlPart]) { - accountsCallbacks[urlPart](token, enableAutoLogin); + if (self._accountsCallbacks[urlPart]) { + self._accountsCallbacks[urlPart](token, function () { + self._enableAutoLogin(); + }); } }); -}); +} // Export for testing AccountsTest = { - attemptToMatchHash: attemptToMatchHash + attemptToMatchHash: function (hash, success) { + return attemptToMatchHash(Accounts, hash, success); + } }; // XXX these should be moved to accounts-password eventually. Right now @@ -82,13 +101,13 @@ AccountsTest = { * password for user A can be reset even if user B was logged in. * @locus Client */ -Accounts.onResetPasswordLink = function (callback) { - if (accountsCallbacks["reset-password"]) { +Ap.onResetPasswordLink = function (callback) { + if (this._accountsCallbacks["reset-password"]) { Meteor._debug("Accounts.onResetPasswordLink was called more than once. " + "Only one callback added will be executed."); } - accountsCallbacks["reset-password"] = callback; + this._accountsCallbacks["reset-password"] = callback; }; /** @@ -107,13 +126,13 @@ Accounts.onResetPasswordLink = function (callback) { * being logged in. * @locus Client */ -Accounts.onEmailVerificationLink = function (callback) { - if (accountsCallbacks["verify-email"]) { +Ap.onEmailVerificationLink = function (callback) { + if (this._accountsCallbacks["verify-email"]) { Meteor._debug("Accounts.onEmailVerificationLink was called more than once. " + "Only one callback added will be executed."); } - accountsCallbacks["verify-email"] = callback; + this._accountsCallbacks["verify-email"] = callback; }; /** @@ -132,11 +151,11 @@ Accounts.onEmailVerificationLink = function (callback) { * user A can be enrolled even if user B was logged in. * @locus Client */ -Accounts.onEnrollmentLink = function (callback) { - if (accountsCallbacks["enroll-account"]) { +Ap.onEnrollmentLink = function (callback) { + if (this._accountsCallbacks["enroll-account"]) { Meteor._debug("Accounts.onEnrollmentLink was called more than once. " + "Only one callback added will be executed."); } - accountsCallbacks["enroll-account"] = callback; + this._accountsCallbacks["enroll-account"] = callback; }; diff --git a/packages/accounts-base/url_server.js b/packages/accounts-base/url_server.js index 2aac2cc4fc..da7bde1254 100644 --- a/packages/accounts-base/url_server.js +++ b/packages/accounts-base/url_server.js @@ -1,15 +1,15 @@ // XXX These should probably not actually be public? -Accounts.urls = {}; +AccountsServer.prototype.urls = { + resetPassword: function (token) { + return Meteor.absoluteUrl('#/reset-password/' + token); + }, -Accounts.urls.resetPassword = function (token) { - return Meteor.absoluteUrl('#/reset-password/' + token); -}; + verifyEmail: function (token) { + return Meteor.absoluteUrl('#/verify-email/' + token); + }, -Accounts.urls.verifyEmail = function (token) { - return Meteor.absoluteUrl('#/verify-email/' + token); -}; - -Accounts.urls.enrollAccount = function (token) { - return Meteor.absoluteUrl('#/enroll-account/' + token); + enrollAccount: function (token) { + return Meteor.absoluteUrl('#/enroll-account/' + token); + } };