1464 lines
54 KiB
JavaScript
1464 lines
54 KiB
JavaScript
//////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// This is a generated file. You can view the original //
|
|
// source in your browser if your browser supports source maps. //
|
|
// Source maps are supported by all recent versions of Chrome, Safari, //
|
|
// and Firefox, and by Internet Explorer 11. //
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
(function () {
|
|
|
|
/* Imports */
|
|
var Meteor = Package.meteor.Meteor;
|
|
var global = Package.meteor.global;
|
|
var meteorEnv = Package.meteor.meteorEnv;
|
|
var _ = Package.underscore._;
|
|
var Tracker = Package.tracker.Tracker;
|
|
var Deps = Package.tracker.Deps;
|
|
var Random = Package.random.Random;
|
|
var Hook = Package['callback-hook'].Hook;
|
|
var ReactiveVar = Package['reactive-var'].ReactiveVar;
|
|
var DDP = Package['ddp-client'].DDP;
|
|
var Mongo = Package.mongo.Mongo;
|
|
var meteorInstall = Package.modules.meteorInstall;
|
|
var meteorBabelHelpers = Package['babel-runtime'].meteorBabelHelpers;
|
|
var Promise = Package.promise.Promise;
|
|
var Symbol = Package['ecmascript-runtime-client'].Symbol;
|
|
var Map = Package['ecmascript-runtime-client'].Map;
|
|
var Set = Package['ecmascript-runtime-client'].Set;
|
|
|
|
/* Package-scope variables */
|
|
var Accounts, EXPIRE_TOKENS_INTERVAL_MS, CONNECTION_CLOSE_DELAY_MS;
|
|
|
|
var require = meteorInstall({"node_modules":{"meteor":{"accounts-base":{"client_main.js":function(require,exports,module){
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// packages/accounts-base/client_main.js //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
var module1 = module;
|
|
module1.export({
|
|
AccountsClient: function () {
|
|
return AccountsClient;
|
|
},
|
|
AccountsTest: function () {
|
|
return AccountsTest;
|
|
}
|
|
});
|
|
var AccountsClient;
|
|
module1.watch(require("./accounts_client.js"), {
|
|
AccountsClient: function (v) {
|
|
AccountsClient = v;
|
|
}
|
|
}, 0);
|
|
var AccountsTest;
|
|
module1.watch(require("./url_client.js"), {
|
|
AccountsTest: function (v) {
|
|
AccountsTest = v;
|
|
}
|
|
}, 1);
|
|
module1.watch(require("./localstorage_token.js"));
|
|
|
|
/**
|
|
* @namespace Accounts
|
|
* @summary The namespace for all client-side accounts-related methods.
|
|
*/
|
|
Accounts = new AccountsClient();
|
|
/**
|
|
* @summary A [Mongo.Collection](#collections) containing user documents.
|
|
* @locus Anywhere
|
|
* @type {Mongo.Collection}
|
|
* @importFromPackage meteor
|
|
*/
|
|
|
|
Meteor.users = Accounts.users;
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
},"accounts_client.js":function(require,exports,module){
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// packages/accounts-base/accounts_client.js //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
|
|
var _inheritsLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/inheritsLoose"));
|
|
|
|
module.export({
|
|
AccountsClient: function () {
|
|
return AccountsClient;
|
|
}
|
|
});
|
|
var AccountsCommon;
|
|
module.watch(require("./accounts_common.js"), {
|
|
AccountsCommon: function (v) {
|
|
AccountsCommon = v;
|
|
}
|
|
}, 0);
|
|
|
|
var AccountsClient =
|
|
/*#__PURE__*/
|
|
function (_AccountsCommon) {
|
|
(0, _inheritsLoose2.default)(AccountsClient, _AccountsCommon);
|
|
|
|
function AccountsClient(options) {
|
|
var _this;
|
|
|
|
_this = _AccountsCommon.call(this, options) || this;
|
|
_this._loggingIn = new ReactiveVar(false);
|
|
_this._loggingOut = new ReactiveVar(false);
|
|
_this._loginServicesHandle = _this.connection.subscribe("meteor.loginServiceConfiguration");
|
|
_this._pageLoadLoginCallbacks = [];
|
|
_this._pageLoadLoginAttemptInfo = null; // Defined in url_client.js.
|
|
|
|
_this._initUrlMatching(); // Defined in localstorage_token.js.
|
|
|
|
|
|
_this._initLocalStorage(); // This is for .registerClientLoginFunction & .callLoginFunction.
|
|
|
|
|
|
_this._loginFuncs = {};
|
|
return _this;
|
|
} ///
|
|
/// CURRENT USER
|
|
///
|
|
// @override
|
|
|
|
|
|
var _proto = AccountsClient.prototype;
|
|
|
|
_proto.userId = function () {
|
|
function userId() {
|
|
return this.connection.userId();
|
|
}
|
|
|
|
return userId;
|
|
}(); // This is mostly just called within this file, but Meteor.loginWithPassword
|
|
// also uses it to make loggingIn() be true during the beginPasswordExchange
|
|
// method call too.
|
|
|
|
|
|
_proto._setLoggingIn = function () {
|
|
function _setLoggingIn(x) {
|
|
this._loggingIn.set(x);
|
|
}
|
|
|
|
return _setLoggingIn;
|
|
}();
|
|
/**
|
|
* @summary True if a login method (such as `Meteor.loginWithPassword`, `Meteor.loginWithFacebook`, or `Accounts.createUser`) is currently in progress. A reactive data source.
|
|
* @locus Client
|
|
*/
|
|
|
|
|
|
_proto.loggingIn = function () {
|
|
function loggingIn() {
|
|
return this._loggingIn.get();
|
|
}
|
|
|
|
return loggingIn;
|
|
}();
|
|
/**
|
|
* @summary True if a logout method (such as `Meteor.logout`) is currently in progress. A reactive data source.
|
|
* @locus Client
|
|
*/
|
|
|
|
|
|
_proto.loggingOut = function () {
|
|
function loggingOut() {
|
|
return this._loggingOut.get();
|
|
}
|
|
|
|
return loggingOut;
|
|
}();
|
|
/**
|
|
* @summary Register a new login function on the client. Intended for OAuth package authors. You can call the login function by using
|
|
`Accounts.callLoginFunction` or `Accounts.callLoginFunction`.
|
|
* @locus Client
|
|
* @param {String} funcName The name of your login function. Used by `Accounts.callLoginFunction` and `Accounts.applyLoginFunction`.
|
|
Should be the OAuth provider name accordingly.
|
|
* @param {Function} func The actual function you want to call. Just write it in the manner of `loginWithFoo`.
|
|
*/
|
|
|
|
|
|
_proto.registerClientLoginFunction = function () {
|
|
function registerClientLoginFunction(funcName, func) {
|
|
if (this._loginFuncs[funcName]) {
|
|
throw new Error(funcName + " has been defined already");
|
|
}
|
|
|
|
this._loginFuncs[funcName] = func;
|
|
}
|
|
|
|
return registerClientLoginFunction;
|
|
}();
|
|
/**
|
|
* @summary Call a login function defined using `Accounts.registerClientLoginFunction`. Excluding the first argument, all remaining
|
|
arguments are passed to the login function accordingly. Use `applyLoginFunction` if you want to pass in an arguments array that contains
|
|
all arguments for the login function.
|
|
* @locus Client
|
|
* @param {String} funcName The name of the login function you wanted to call.
|
|
*/
|
|
|
|
|
|
_proto.callLoginFunction = function () {
|
|
function callLoginFunction(funcName) {
|
|
if (!this._loginFuncs[funcName]) {
|
|
throw new Error(funcName + " was not defined");
|
|
}
|
|
|
|
for (var _len = arguments.length, funcArgs = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
funcArgs[_key - 1] = arguments[_key];
|
|
}
|
|
|
|
return this._loginFuncs[funcName].apply(this, funcArgs);
|
|
}
|
|
|
|
return callLoginFunction;
|
|
}();
|
|
/**
|
|
* @summary Same as ``callLoginFunction` but accept an `arguments` which contains all arguments for the login
|
|
function.
|
|
* @locus Client
|
|
* @param {String} funcName The name of the login function you wanted to call.
|
|
* @param {Array} funcArgs The `arguments` for the login function.
|
|
*/
|
|
|
|
|
|
_proto.applyLoginFunction = function () {
|
|
function applyLoginFunction(funcName, funcArgs) {
|
|
if (!this._loginFuncs[funcName]) {
|
|
throw new Error(funcName + " was not defined");
|
|
}
|
|
|
|
return this._loginFuncs[funcName].apply(this, funcArgs);
|
|
}
|
|
|
|
return applyLoginFunction;
|
|
}();
|
|
/**
|
|
* @summary Log the user out.
|
|
* @locus Client
|
|
* @param {Function} [callback] Optional callback. Called with no arguments on success, or with a single `Error` argument on failure.
|
|
*/
|
|
|
|
|
|
_proto.logout = function () {
|
|
function logout(callback) {
|
|
var self = this;
|
|
|
|
self._loggingOut.set(true);
|
|
|
|
self.connection.apply('logout', [], {
|
|
wait: true
|
|
}, function (error, result) {
|
|
self._loggingOut.set(false);
|
|
|
|
if (error) {
|
|
callback && callback(error);
|
|
} else {
|
|
self.makeClientLoggedOut();
|
|
callback && callback();
|
|
}
|
|
});
|
|
}
|
|
|
|
return logout;
|
|
}();
|
|
/**
|
|
* @summary Log out other clients logged in as the current user, but does not log out the client that calls this function.
|
|
* @locus Client
|
|
* @param {Function} [callback] Optional callback. Called with no arguments on success, or with a single `Error` argument on failure.
|
|
*/
|
|
|
|
|
|
_proto.logoutOtherClients = function () {
|
|
function logoutOtherClients(callback) {
|
|
var self = this; // We need to make two method calls: one to replace our current token,
|
|
// and another to remove all tokens except the current one. We want to
|
|
// call these two methods one after the other, without any other
|
|
// methods running between them. For example, we don't want `logout`
|
|
// to be called in between our two method calls (otherwise the second
|
|
// method call would return an error). Another example: we don't want
|
|
// logout to be called before the callback for `getNewToken`;
|
|
// otherwise we would momentarily log the user out and then write a
|
|
// new token to localStorage.
|
|
//
|
|
// To accomplish this, we make both calls as wait methods, and queue
|
|
// them one after the other, without spinning off the event loop in
|
|
// between. Even though we queue `removeOtherTokens` before
|
|
// `getNewToken`, we won't actually send the `removeOtherTokens` call
|
|
// until the `getNewToken` callback has finished running, because they
|
|
// are both wait methods.
|
|
|
|
self.connection.apply('getNewToken', [], {
|
|
wait: true
|
|
}, function (err, result) {
|
|
if (!err) {
|
|
self._storeLoginToken(self.userId(), result.token, result.tokenExpires);
|
|
}
|
|
});
|
|
self.connection.apply('removeOtherTokens', [], {
|
|
wait: true
|
|
}, function (err) {
|
|
callback && callback(err);
|
|
});
|
|
}
|
|
|
|
return logoutOtherClients;
|
|
}();
|
|
|
|
return AccountsClient;
|
|
}(AccountsCommon);
|
|
|
|
;
|
|
var Ap = AccountsClient.prototype;
|
|
/**
|
|
* @summary True if a login method (such as `Meteor.loginWithPassword`, `Meteor.loginWithFacebook`, or `Accounts.createUser`) is currently in progress. A reactive data source.
|
|
* @locus Client
|
|
* @importFromPackage meteor
|
|
*/
|
|
|
|
Meteor.loggingIn = function () {
|
|
return Accounts.loggingIn();
|
|
};
|
|
/**
|
|
* @summary True if a logout method (such as `Meteor.logout`) is currently in progress. A reactive data source.
|
|
* @locus Client
|
|
* @importFromPackage meteor
|
|
*/
|
|
|
|
|
|
Meteor.loggingOut = function () {
|
|
return Accounts.loggingOut();
|
|
}; ///
|
|
/// LOGIN METHODS
|
|
///
|
|
// Call a login method on the server.
|
|
//
|
|
// A login method is a method which on success calls `this.setUserId(id)` and
|
|
// `Accounts._setLoginToken` on the server and returns an object with fields
|
|
// 'id' (containing the user id), 'token' (containing a resume token), and
|
|
// optionally `tokenExpires`.
|
|
//
|
|
// This function takes care of:
|
|
// - Updating the Meteor.loggingIn() reactive data source
|
|
// - Calling the method in 'wait' mode
|
|
// - On success, saving the resume token to localStorage
|
|
// - On success, calling Accounts.connection.setUserId()
|
|
// - Setting up an onReconnect handler which logs in with
|
|
// the resume token
|
|
//
|
|
// Options:
|
|
// - methodName: The method to call (default 'login')
|
|
// - methodArguments: The arguments for the method
|
|
// - validateResult: If provided, will be called with the result of the
|
|
// method. If it throws, the client will not be logged in (and
|
|
// its error will be passed to the callback).
|
|
// - userCallback: Will be called with no arguments once the user is fully
|
|
// logged in, or with the error on error.
|
|
//
|
|
|
|
|
|
Ap.callLoginMethod = function (options) {
|
|
var self = this;
|
|
options = _.extend({
|
|
methodName: 'login',
|
|
methodArguments: [{}],
|
|
_suppressLoggingIn: false
|
|
}, options); // Set defaults for callback arguments to no-op functions; make sure we
|
|
// override falsey values too.
|
|
|
|
_.each(['validateResult', 'userCallback'], function (f) {
|
|
if (!options[f]) options[f] = function () {};
|
|
}); // Prepare callbacks: user provided and onLogin/onLoginFailure hooks.
|
|
|
|
|
|
var loginCallbacks = _.once(function (_ref) {
|
|
var error = _ref.error,
|
|
loginDetails = _ref.loginDetails;
|
|
|
|
if (!error) {
|
|
self._onLoginHook.each(function (callback) {
|
|
callback(loginDetails);
|
|
return true;
|
|
});
|
|
} else {
|
|
self._onLoginFailureHook.each(function (callback) {
|
|
callback({
|
|
error: error
|
|
});
|
|
return true;
|
|
});
|
|
}
|
|
|
|
options.userCallback(error, loginDetails);
|
|
});
|
|
|
|
var reconnected = false; // We want to set up onReconnect as soon as we get a result token back from
|
|
// the server, without having to wait for subscriptions to rerun. This is
|
|
// because if we disconnect and reconnect between getting the result and
|
|
// getting the results of subscription rerun, we WILL NOT re-send this
|
|
// method (because we never re-send methods whose results we've received)
|
|
// but we WILL call loggedInAndDataReadyCallback at "reconnect quiesce"
|
|
// time. This will lead to makeClientLoggedIn(result.id) even though we
|
|
// haven't actually sent a login method!
|
|
//
|
|
// But by making sure that we send this "resume" login in that case (and
|
|
// calling makeClientLoggedOut if it fails), we'll end up with an accurate
|
|
// client-side userId. (It's important that livedata_connection guarantees
|
|
// that the "reconnect quiesce"-time call to loggedInAndDataReadyCallback
|
|
// will occur before the callback from the resume login call.)
|
|
|
|
var onResultReceived = function (err, result) {
|
|
if (err || !result || !result.token) {// Leave onReconnect alone if there was an error, so that if the user was
|
|
// already logged in they will still get logged in on reconnect.
|
|
// See issue #4970.
|
|
} else {
|
|
// First clear out any previously set Acccounts login onReconnect
|
|
// callback (to make sure we don't keep piling up duplicate callbacks,
|
|
// which would then all be triggered when reconnecting).
|
|
if (self._reconnectStopper) {
|
|
self._reconnectStopper.stop();
|
|
}
|
|
|
|
self._reconnectStopper = DDP.onReconnect(function (conn) {
|
|
if (conn != self.connection) {
|
|
return;
|
|
}
|
|
|
|
reconnected = true; // If our token was updated in storage, use the latest one.
|
|
|
|
var storedToken = self._storedLoginToken();
|
|
|
|
if (storedToken) {
|
|
result = {
|
|
token: storedToken,
|
|
tokenExpires: self._storedLoginTokenExpires()
|
|
};
|
|
}
|
|
|
|
if (!result.tokenExpires) result.tokenExpires = self._tokenExpiration(new Date());
|
|
|
|
if (self._tokenExpiresSoon(result.tokenExpires)) {
|
|
self.makeClientLoggedOut();
|
|
} else {
|
|
self.callLoginMethod({
|
|
methodArguments: [{
|
|
resume: result.token
|
|
}],
|
|
// Reconnect quiescence ensures that the user doesn't see an
|
|
// intermediate state before the login method finishes. So we don't
|
|
// need to show a logging-in animation.
|
|
_suppressLoggingIn: true,
|
|
userCallback: function (error, loginDetails) {
|
|
var storedTokenNow = self._storedLoginToken();
|
|
|
|
if (error) {
|
|
// If we had a login error AND the current stored token is the
|
|
// one that we tried to log in with, then declare ourselves
|
|
// logged out. If there's a token in storage but it's not the
|
|
// token that we tried to log in with, we don't know anything
|
|
// about whether that token is valid or not, so do nothing. The
|
|
// periodic localStorage poll will decide if we are logged in or
|
|
// out with this token, if it hasn't already. Of course, even
|
|
// with this check, another tab could insert a new valid token
|
|
// immediately before we clear localStorage here, which would
|
|
// lead to both tabs being logged out, but by checking the token
|
|
// in storage right now we hope to make that unlikely to happen.
|
|
//
|
|
// If there is no token in storage right now, we don't have to
|
|
// do anything; whatever code removed the token from storage was
|
|
// responsible for calling `makeClientLoggedOut()`, or the
|
|
// periodic localStorage poll will call `makeClientLoggedOut`
|
|
// eventually if another tab wiped the token from storage.
|
|
if (storedTokenNow && storedTokenNow === result.token) {
|
|
self.makeClientLoggedOut();
|
|
}
|
|
} // Possibly a weird callback to call, but better than nothing if
|
|
// there is a reconnect between "login result received" and "data
|
|
// ready".
|
|
|
|
|
|
loginCallbacks({
|
|
error: error,
|
|
loginDetails: loginDetails
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}; // This callback is called once the local cache of the current-user
|
|
// subscription (and all subscriptions, in fact) are guaranteed to be up to
|
|
// date.
|
|
|
|
|
|
var loggedInAndDataReadyCallback = function (error, result) {
|
|
// If the login method returns its result but the connection is lost
|
|
// before the data is in the local cache, it'll set an onReconnect (see
|
|
// above). The onReconnect will try to log in using the token, and *it*
|
|
// will call userCallback via its own version of this
|
|
// loggedInAndDataReadyCallback. So we don't have to do anything here.
|
|
if (reconnected) return; // Note that we need to call this even if _suppressLoggingIn is true,
|
|
// because it could be matching a _setLoggingIn(true) from a
|
|
// half-completed pre-reconnect login method.
|
|
|
|
self._setLoggingIn(false);
|
|
|
|
if (error || !result) {
|
|
error = error || new Error("No result from call to " + options.methodName);
|
|
loginCallbacks({
|
|
error: error
|
|
});
|
|
return;
|
|
}
|
|
|
|
try {
|
|
options.validateResult(result);
|
|
} catch (e) {
|
|
loginCallbacks({
|
|
error: e
|
|
});
|
|
return;
|
|
} // Make the client logged in. (The user data should already be loaded!)
|
|
|
|
|
|
self.makeClientLoggedIn(result.id, result.token, result.tokenExpires);
|
|
loginCallbacks({
|
|
loginDetails: {
|
|
type: result.type
|
|
}
|
|
});
|
|
};
|
|
|
|
if (!options._suppressLoggingIn) self._setLoggingIn(true);
|
|
self.connection.apply(options.methodName, options.methodArguments, {
|
|
wait: true,
|
|
onResultReceived: onResultReceived
|
|
}, loggedInAndDataReadyCallback);
|
|
};
|
|
|
|
Ap.makeClientLoggedOut = function () {
|
|
// Ensure client was successfully logged in before running logout hooks.
|
|
if (this.connection._userId) {
|
|
this._onLogoutHook.each(function (callback) {
|
|
callback();
|
|
return true;
|
|
});
|
|
}
|
|
|
|
this._unstoreLoginToken();
|
|
|
|
this.connection.setUserId(null);
|
|
this._reconnectStopper && this._reconnectStopper.stop();
|
|
};
|
|
|
|
Ap.makeClientLoggedIn = function (userId, token, tokenExpires) {
|
|
this._storeLoginToken(userId, token, tokenExpires);
|
|
|
|
this.connection.setUserId(userId);
|
|
};
|
|
/**
|
|
* @summary Log the user out.
|
|
* @locus Client
|
|
* @param {Function} [callback] Optional callback. Called with no arguments on success, or with a single `Error` argument on failure.
|
|
* @importFromPackage meteor
|
|
*/
|
|
|
|
|
|
Meteor.logout = function (callback) {
|
|
return Accounts.logout(callback);
|
|
};
|
|
/**
|
|
* @summary Log out other clients logged in as the current user, but does not log out the client that calls this function.
|
|
* @locus Client
|
|
* @param {Function} [callback] Optional callback. Called with no arguments on success, or with a single `Error` argument on failure.
|
|
* @importFromPackage meteor
|
|
*/
|
|
|
|
|
|
Meteor.logoutOtherClients = function (callback) {
|
|
return Accounts.logoutOtherClients(callback);
|
|
}; ///
|
|
/// LOGIN SERVICES
|
|
///
|
|
// A reactive function returning whether the loginServiceConfiguration
|
|
// subscription is ready. Used by accounts-ui to hide the login button
|
|
// until we have all the configuration loaded
|
|
//
|
|
|
|
|
|
Ap.loginServicesConfigured = function () {
|
|
return this._loginServicesHandle.ready();
|
|
}; // Some login services such as the redirect login flow or the resume
|
|
// login handler can log the user in at page load time. The
|
|
// Meteor.loginWithX functions have a callback argument, but the
|
|
// callback function instance won't be in memory any longer if the
|
|
// page was reloaded. The `onPageLoadLogin` function allows a
|
|
// callback to be registered for the case where the login was
|
|
// initiated in a previous VM, and we now have the result of the login
|
|
// attempt in a new VM.
|
|
// Register a callback to be called if we have information about a
|
|
// login attempt at page load time. Call the callback immediately if
|
|
// we already have the page load login attempt info, otherwise stash
|
|
// the callback to be called if and when we do get the attempt info.
|
|
//
|
|
|
|
|
|
Ap.onPageLoadLogin = function (f) {
|
|
if (this._pageLoadLoginAttemptInfo) {
|
|
f(this._pageLoadLoginAttemptInfo);
|
|
} else {
|
|
this._pageLoadLoginCallbacks.push(f);
|
|
}
|
|
}; // Receive the information about the login attempt at page load time.
|
|
// Call registered callbacks, and also record the info in case
|
|
// someone's callback hasn't been registered yet.
|
|
//
|
|
|
|
|
|
Ap._pageLoadLogin = function (attemptInfo) {
|
|
if (this._pageLoadLoginAttemptInfo) {
|
|
Meteor._debug("Ignoring unexpected duplicate page load login attempt info");
|
|
|
|
return;
|
|
}
|
|
|
|
_.each(this._pageLoadLoginCallbacks, function (callback) {
|
|
callback(attemptInfo);
|
|
});
|
|
|
|
this._pageLoadLoginCallbacks = [];
|
|
this._pageLoadLoginAttemptInfo = attemptInfo;
|
|
}; ///
|
|
/// HANDLEBARS HELPERS
|
|
///
|
|
// If our app has a Blaze, register the {{currentUser}} and {{loggingIn}}
|
|
// global helpers.
|
|
|
|
|
|
if (Package.blaze) {
|
|
/**
|
|
* @global
|
|
* @name currentUser
|
|
* @isHelper true
|
|
* @summary Calls [Meteor.user()](#meteor_user). Use `{{#if currentUser}}` to check whether the user is logged in.
|
|
*/
|
|
Package.blaze.Blaze.Template.registerHelper('currentUser', function () {
|
|
return Meteor.user();
|
|
});
|
|
/**
|
|
* @global
|
|
* @name loggingIn
|
|
* @isHelper true
|
|
* @summary Calls [Meteor.loggingIn()](#meteor_loggingin).
|
|
*/
|
|
|
|
Package.blaze.Blaze.Template.registerHelper('loggingIn', function () {
|
|
return Meteor.loggingIn();
|
|
});
|
|
/**
|
|
* @global
|
|
* @name loggingOut
|
|
* @isHelper true
|
|
* @summary Calls [Meteor.loggingOut()](#meteor_loggingout).
|
|
*/
|
|
|
|
Package.blaze.Blaze.Template.registerHelper('loggingOut', function () {
|
|
return Meteor.loggingOut();
|
|
});
|
|
/**
|
|
* @global
|
|
* @name loggingInOrOut
|
|
* @isHelper true
|
|
* @summary Calls [Meteor.loggingIn()](#meteor_loggingin) or [Meteor.loggingOut()](#meteor_loggingout).
|
|
*/
|
|
|
|
Package.blaze.Blaze.Template.registerHelper('loggingInOrOut', function () {
|
|
return Meteor.loggingIn() || Meteor.loggingOut();
|
|
});
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
},"accounts_common.js":function(require,exports,module){
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// packages/accounts-base/accounts_common.js //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
module.export({
|
|
AccountsCommon: function () {
|
|
return AccountsCommon;
|
|
}
|
|
});
|
|
|
|
var AccountsCommon =
|
|
/*#__PURE__*/
|
|
function () {
|
|
function AccountsCommon(options) {
|
|
// Currently this is read directly by packages like accounts-password
|
|
// and accounts-ui-unstyled.
|
|
this._options = {}; // Note that setting this.connection = null causes this.users to be a
|
|
// LocalCollection, which is not what we want.
|
|
|
|
this.connection = undefined;
|
|
|
|
this._initConnection(options || {}); // There is an allow call in accounts_server.js that restricts writes to
|
|
// this collection.
|
|
|
|
|
|
this.users = new Mongo.Collection("users", {
|
|
_preventAutopublish: true,
|
|
connection: this.connection
|
|
}); // Callback exceptions are printed with Meteor._debug and ignored.
|
|
|
|
this._onLoginHook = new Hook({
|
|
bindEnvironment: false,
|
|
debugPrintExceptions: "onLogin callback"
|
|
});
|
|
this._onLoginFailureHook = new Hook({
|
|
bindEnvironment: false,
|
|
debugPrintExceptions: "onLoginFailure callback"
|
|
});
|
|
this._onLogoutHook = new Hook({
|
|
bindEnvironment: false,
|
|
debugPrintExceptions: "onLogout callback"
|
|
});
|
|
}
|
|
/**
|
|
* @summary Get the current user id, or `null` if no user is logged in. A reactive data source.
|
|
* @locus Anywhere
|
|
*/
|
|
|
|
|
|
var _proto = AccountsCommon.prototype;
|
|
|
|
_proto.userId = function () {
|
|
function userId() {
|
|
throw new Error("userId method not implemented");
|
|
}
|
|
|
|
return userId;
|
|
}();
|
|
/**
|
|
* @summary Get the current user record, or `null` if no user is logged in. A reactive data source.
|
|
* @locus Anywhere
|
|
*/
|
|
|
|
|
|
_proto.user = function () {
|
|
function user() {
|
|
var userId = this.userId();
|
|
return userId ? this.users.findOne(userId) : null;
|
|
}
|
|
|
|
return user;
|
|
}(); // Set up config for the accounts system. Call this on both the client
|
|
// and the server.
|
|
//
|
|
// Note that this method gets overridden on AccountsServer.prototype, but
|
|
// the overriding method calls the overridden method.
|
|
//
|
|
// XXX we should add some enforcement that this is called on both the
|
|
// client and the server. Otherwise, a user can
|
|
// 'forbidClientAccountCreation' only on the client and while it looks
|
|
// like their app is secure, the server will still accept createUser
|
|
// calls. https://github.com/meteor/meteor/issues/828
|
|
//
|
|
// @param options {Object} an object with fields:
|
|
// - sendVerificationEmail {Boolean}
|
|
// Send email address verification emails to new users created from
|
|
// client signups.
|
|
// - forbidClientAccountCreation {Boolean}
|
|
// Do not allow clients to create accounts directly.
|
|
// - restrictCreationByEmailDomain {Function or String}
|
|
// Require created users to have an email matching the function or
|
|
// having the string as domain.
|
|
// - loginExpirationInDays {Number}
|
|
// Number of days since login until a user is logged out (login token
|
|
// expires).
|
|
// - passwordResetTokenExpirationInDays {Number}
|
|
// Number of days since password reset token creation until the
|
|
// token cannt be used any longer (password reset token expires).
|
|
// - ambiguousErrorMessages {Boolean}
|
|
// Return ambiguous error messages from login failures to prevent
|
|
// user enumeration.
|
|
// - bcryptRounds {Number}
|
|
// Allows override of number of bcrypt rounds (aka work factor) used
|
|
// to store passwords.
|
|
|
|
/**
|
|
* @summary Set global accounts options.
|
|
* @locus Anywhere
|
|
* @param {Object} options
|
|
* @param {Boolean} options.sendVerificationEmail New users with an email address will receive an address verification email.
|
|
* @param {Boolean} options.forbidClientAccountCreation Calls to [`createUser`](#accounts_createuser) from the client will be rejected. In addition, if you are using [accounts-ui](#accountsui), the "Create account" link will not be available.
|
|
* @param {String | Function} options.restrictCreationByEmailDomain If set to a string, only allows new users if the domain part of their email address matches the string. If set to a function, only allows new users if the function returns true. The function is passed the full email address of the proposed new user. Works with password-based sign-in and external services that expose email addresses (Google, Facebook, GitHub). All existing users still can log in after enabling this option. Example: `Accounts.config({ restrictCreationByEmailDomain: 'school.edu' })`.
|
|
* @param {Number} options.loginExpirationInDays The number of days from when a user logs in until their token expires and they are logged out. Defaults to 90. Set to `null` to disable login expiration.
|
|
* @param {String} options.oauthSecretKey When using the `oauth-encryption` package, the 16 byte key using to encrypt sensitive account credentials in the database, encoded in base64. This option may only be specifed on the server. See packages/oauth-encryption/README.md for details.
|
|
* @param {Number} options.passwordResetTokenExpirationInDays The number of days from when a link to reset password is sent until token expires and user can't reset password with the link anymore. Defaults to 3.
|
|
* @param {Number} options.passwordEnrollTokenExpirationInDays The number of days from when a link to set inital password is sent until token expires and user can't set password with the link anymore. Defaults to 30.
|
|
* @param {Boolean} options.ambiguousErrorMessages Return ambiguous error messages from login failures to prevent user enumeration. Defaults to false.
|
|
*/
|
|
|
|
|
|
_proto.config = function () {
|
|
function config(options) {
|
|
var self = this; // We don't want users to accidentally only call Accounts.config on the
|
|
// client, where some of the options will have partial effects (eg removing
|
|
// the "create account" button from accounts-ui if forbidClientAccountCreation
|
|
// is set, or redirecting Google login to a specific-domain page) without
|
|
// having their full effects.
|
|
|
|
if (Meteor.isServer) {
|
|
__meteor_runtime_config__.accountsConfigCalled = true;
|
|
} else if (!__meteor_runtime_config__.accountsConfigCalled) {
|
|
// XXX would be nice to "crash" the client and replace the UI with an error
|
|
// message, but there's no trivial way to do this.
|
|
Meteor._debug("Accounts.config was called on the client but not on the " + "server; some configuration options may not take effect.");
|
|
} // We need to validate the oauthSecretKey option at the time
|
|
// Accounts.config is called. We also deliberately don't store the
|
|
// oauthSecretKey in Accounts._options.
|
|
|
|
|
|
if (_.has(options, "oauthSecretKey")) {
|
|
if (Meteor.isClient) throw new Error("The oauthSecretKey option may only be specified on the server");
|
|
if (!Package["oauth-encryption"]) throw new Error("The oauth-encryption package must be loaded to set oauthSecretKey");
|
|
Package["oauth-encryption"].OAuthEncryption.loadKey(options.oauthSecretKey);
|
|
options = _.omit(options, "oauthSecretKey");
|
|
} // validate option keys
|
|
|
|
|
|
var VALID_KEYS = ["sendVerificationEmail", "forbidClientAccountCreation", "passwordEnrollTokenExpirationInDays", "restrictCreationByEmailDomain", "loginExpirationInDays", "passwordResetTokenExpirationInDays", "ambiguousErrorMessages", "bcryptRounds"];
|
|
|
|
_.each(_.keys(options), function (key) {
|
|
if (!_.contains(VALID_KEYS, key)) {
|
|
throw new Error("Accounts.config: Invalid key: " + key);
|
|
}
|
|
}); // set values in Accounts._options
|
|
|
|
|
|
_.each(VALID_KEYS, function (key) {
|
|
if (key in options) {
|
|
if (key in self._options) {
|
|
throw new Error("Can't set `" + key + "` more than once");
|
|
}
|
|
|
|
self._options[key] = options[key];
|
|
}
|
|
});
|
|
}
|
|
|
|
return config;
|
|
}();
|
|
/**
|
|
* @summary Register a callback to be called after a login attempt succeeds.
|
|
* @locus Anywhere
|
|
* @param {Function} func The callback to be called when login is successful.
|
|
* The callback receives a single object that
|
|
* holds login details. This object contains the login
|
|
* result type (password, resume, etc.) on both the
|
|
* client and server. `onLogin` callbacks registered
|
|
* on the server also receive extra data, such
|
|
* as user details, connection information, etc.
|
|
*/
|
|
|
|
|
|
_proto.onLogin = function () {
|
|
function onLogin(func) {
|
|
return this._onLoginHook.register(func);
|
|
}
|
|
|
|
return onLogin;
|
|
}();
|
|
/**
|
|
* @summary Register a callback to be called after a login attempt fails.
|
|
* @locus Anywhere
|
|
* @param {Function} func The callback to be called after the login has failed.
|
|
*/
|
|
|
|
|
|
_proto.onLoginFailure = function () {
|
|
function onLoginFailure(func) {
|
|
return this._onLoginFailureHook.register(func);
|
|
}
|
|
|
|
return onLoginFailure;
|
|
}();
|
|
/**
|
|
* @summary Register a callback to be called after a logout attempt succeeds.
|
|
* @locus Anywhere
|
|
* @param {Function} func The callback to be called when logout is successful.
|
|
*/
|
|
|
|
|
|
_proto.onLogout = function () {
|
|
function onLogout(func) {
|
|
return this._onLogoutHook.register(func);
|
|
}
|
|
|
|
return onLogout;
|
|
}();
|
|
|
|
_proto._initConnection = function () {
|
|
function _initConnection(options) {
|
|
if (!Meteor.isClient) {
|
|
return;
|
|
} // The connection used by the Accounts system. This is the connection
|
|
// that will get logged in by Meteor.login(), and this is the
|
|
// connection whose login state will be reflected by Meteor.userId().
|
|
//
|
|
// It would be much preferable for this to be in accounts_client.js,
|
|
// but it has to be here because it's needed to create the
|
|
// Meteor.users collection.
|
|
|
|
|
|
if (options.connection) {
|
|
this.connection = options.connection;
|
|
} else if (options.ddpUrl) {
|
|
this.connection = DDP.connect(options.ddpUrl);
|
|
} else if (typeof __meteor_runtime_config__ !== "undefined" && __meteor_runtime_config__.ACCOUNTS_CONNECTION_URL) {
|
|
// Temporary, internal hook to allow the server to point the client
|
|
// to a different authentication server. This is for a very
|
|
// particular use case that comes up when implementing a oauth
|
|
// server. Unsupported and may go away at any point in time.
|
|
//
|
|
// We will eventually provide a general way to use account-base
|
|
// against any DDP connection, not just one special one.
|
|
this.connection = DDP.connect(__meteor_runtime_config__.ACCOUNTS_CONNECTION_URL);
|
|
} else {
|
|
this.connection = Meteor.connection;
|
|
}
|
|
}
|
|
|
|
return _initConnection;
|
|
}();
|
|
|
|
_proto._getTokenLifetimeMs = function () {
|
|
function _getTokenLifetimeMs() {
|
|
// When loginExpirationInDays is set to null, we'll use a really high
|
|
// number of days (LOGIN_UNEXPIRABLE_TOKEN_DAYS) to simulate an
|
|
// unexpiring token.
|
|
var loginExpirationInDays = this._options.loginExpirationInDays === null ? LOGIN_UNEXPIRING_TOKEN_DAYS : this._options.loginExpirationInDays;
|
|
return (loginExpirationInDays || DEFAULT_LOGIN_EXPIRATION_DAYS) * 24 * 60 * 60 * 1000;
|
|
}
|
|
|
|
return _getTokenLifetimeMs;
|
|
}();
|
|
|
|
_proto._getPasswordResetTokenLifetimeMs = function () {
|
|
function _getPasswordResetTokenLifetimeMs() {
|
|
return (this._options.passwordResetTokenExpirationInDays || DEFAULT_PASSWORD_RESET_TOKEN_EXPIRATION_DAYS) * 24 * 60 * 60 * 1000;
|
|
}
|
|
|
|
return _getPasswordResetTokenLifetimeMs;
|
|
}();
|
|
|
|
_proto._getPasswordEnrollTokenLifetimeMs = function () {
|
|
function _getPasswordEnrollTokenLifetimeMs() {
|
|
return (this._options.passwordEnrollTokenExpirationInDays || DEFAULT_PASSWORD_ENROLL_TOKEN_EXPIRATION_DAYS) * 24 * 60 * 60 * 1000;
|
|
}
|
|
|
|
return _getPasswordEnrollTokenLifetimeMs;
|
|
}();
|
|
|
|
_proto._tokenExpiration = function () {
|
|
function _tokenExpiration(when) {
|
|
// We pass when through the Date constructor for backwards compatibility;
|
|
// `when` used to be a number.
|
|
return new Date(new Date(when).getTime() + this._getTokenLifetimeMs());
|
|
}
|
|
|
|
return _tokenExpiration;
|
|
}();
|
|
|
|
_proto._tokenExpiresSoon = function () {
|
|
function _tokenExpiresSoon(when) {
|
|
var minLifetimeMs = .1 * this._getTokenLifetimeMs();
|
|
|
|
var minLifetimeCapMs = MIN_TOKEN_LIFETIME_CAP_SECS * 1000;
|
|
if (minLifetimeMs > minLifetimeCapMs) minLifetimeMs = minLifetimeCapMs;
|
|
return new Date() > new Date(when) - minLifetimeMs;
|
|
}
|
|
|
|
return _tokenExpiresSoon;
|
|
}();
|
|
|
|
return AccountsCommon;
|
|
}();
|
|
|
|
var Ap = AccountsCommon.prototype; // Note that Accounts is defined separately in accounts_client.js and
|
|
// accounts_server.js.
|
|
|
|
/**
|
|
* @summary Get the current user id, or `null` if no user is logged in. A reactive data source.
|
|
* @locus Anywhere but publish functions
|
|
* @importFromPackage meteor
|
|
*/
|
|
|
|
Meteor.userId = function () {
|
|
return Accounts.userId();
|
|
};
|
|
/**
|
|
* @summary Get the current user record, or `null` if no user is logged in. A reactive data source.
|
|
* @locus Anywhere but publish functions
|
|
* @importFromPackage meteor
|
|
*/
|
|
|
|
|
|
Meteor.user = function () {
|
|
return Accounts.user();
|
|
}; // how long (in days) until a login token expires
|
|
|
|
|
|
var DEFAULT_LOGIN_EXPIRATION_DAYS = 90; // Expose for testing.
|
|
|
|
Ap.DEFAULT_LOGIN_EXPIRATION_DAYS = DEFAULT_LOGIN_EXPIRATION_DAYS; // how long (in days) until reset password token expires
|
|
|
|
var DEFAULT_PASSWORD_RESET_TOKEN_EXPIRATION_DAYS = 3; // how long (in days) until enrol password token expires
|
|
|
|
var DEFAULT_PASSWORD_ENROLL_TOKEN_EXPIRATION_DAYS = 30; // Clients don't try to auto-login with a token that is going to expire within
|
|
// .1 * DEFAULT_LOGIN_EXPIRATION_DAYS, capped at MIN_TOKEN_LIFETIME_CAP_SECS.
|
|
// Tries to avoid abrupt disconnects from expiring tokens.
|
|
|
|
var MIN_TOKEN_LIFETIME_CAP_SECS = 3600; // one hour
|
|
// how often (in milliseconds) we check for expired tokens
|
|
|
|
EXPIRE_TOKENS_INTERVAL_MS = 600 * 1000; // 10 minutes
|
|
// how long we wait before logging out clients when Meteor.logoutOtherClients is
|
|
// called
|
|
|
|
CONNECTION_CLOSE_DELAY_MS = 10 * 1000; // A large number of expiration days (approximately 100 years worth) that is
|
|
// used when creating unexpiring tokens.
|
|
|
|
var LOGIN_UNEXPIRING_TOKEN_DAYS = 365 * 100; // Expose for testing.
|
|
|
|
Ap.LOGIN_UNEXPIRING_TOKEN_DAYS = LOGIN_UNEXPIRING_TOKEN_DAYS; // loginServiceConfiguration and ConfigError are maintained for backwards compatibility
|
|
|
|
Meteor.startup(function () {
|
|
var ServiceConfiguration = Package['service-configuration'].ServiceConfiguration;
|
|
Ap.loginServiceConfiguration = ServiceConfiguration.configurations;
|
|
Ap.ConfigError = ServiceConfiguration.ConfigError;
|
|
}); // Thrown when the user cancels the login process (eg, closes an oauth
|
|
// popup, declines retina scan, etc)
|
|
|
|
var lceName = 'Accounts.LoginCancelledError';
|
|
Ap.LoginCancelledError = Meteor.makeErrorType(lceName, function (description) {
|
|
this.message = description;
|
|
});
|
|
Ap.LoginCancelledError.prototype.name = lceName; // This is used to transmit specific subclass errors over the wire. We should
|
|
// come up with a more generic way to do this (eg, with some sort of symbolic
|
|
// error code rather than a number).
|
|
|
|
Ap.LoginCancelledError.numericError = 0x8acdc2f;
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
},"localstorage_token.js":function(require,exports,module){
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// packages/accounts-base/localstorage_token.js //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
var AccountsClient;
|
|
module.watch(require("./accounts_client.js"), {
|
|
AccountsClient: function (v) {
|
|
AccountsClient = v;
|
|
}
|
|
}, 0);
|
|
var Ap = AccountsClient.prototype; // This file deals with storing a login token and user id in the
|
|
// browser's localStorage facility. It polls local storage every few
|
|
// seconds to synchronize login state between multiple tabs in the same
|
|
// browser.
|
|
// Login with a Meteor access token. This is the only public function
|
|
// here.
|
|
|
|
Meteor.loginWithToken = function (token, callback) {
|
|
return Accounts.loginWithToken(token, callback);
|
|
};
|
|
|
|
Ap.loginWithToken = function (token, callback) {
|
|
this.callLoginMethod({
|
|
methodArguments: [{
|
|
resume: token
|
|
}],
|
|
userCallback: callback
|
|
});
|
|
}; // Semi-internal API. Call this function to re-enable auto login after
|
|
// if it was disabled at startup.
|
|
|
|
|
|
Ap._enableAutoLogin = function () {
|
|
this._autoLoginEnabled = true;
|
|
|
|
this._pollStoredLoginToken();
|
|
}; ///
|
|
/// STORING
|
|
///
|
|
// Call this from the top level of the test file for any test that does
|
|
// logging in and out, to protect multiple tabs running the same tests
|
|
// simultaneously from interfering with each others' localStorage.
|
|
|
|
|
|
Ap._isolateLoginTokenForTest = function () {
|
|
this.LOGIN_TOKEN_KEY = this.LOGIN_TOKEN_KEY + Random.id();
|
|
this.USER_ID_KEY = this.USER_ID_KEY + Random.id();
|
|
};
|
|
|
|
Ap._storeLoginToken = function (userId, token, tokenExpires) {
|
|
Meteor._localStorage.setItem(this.USER_ID_KEY, userId);
|
|
|
|
Meteor._localStorage.setItem(this.LOGIN_TOKEN_KEY, token);
|
|
|
|
if (!tokenExpires) tokenExpires = this._tokenExpiration(new Date());
|
|
|
|
Meteor._localStorage.setItem(this.LOGIN_TOKEN_EXPIRES_KEY, tokenExpires); // to ensure that the localstorage poller doesn't end up trying to
|
|
// connect a second time
|
|
|
|
|
|
this._lastLoginTokenWhenPolled = token;
|
|
};
|
|
|
|
Ap._unstoreLoginToken = function () {
|
|
Meteor._localStorage.removeItem(this.USER_ID_KEY);
|
|
|
|
Meteor._localStorage.removeItem(this.LOGIN_TOKEN_KEY);
|
|
|
|
Meteor._localStorage.removeItem(this.LOGIN_TOKEN_EXPIRES_KEY); // to ensure that the localstorage poller doesn't end up trying to
|
|
// connect a second time
|
|
|
|
|
|
this._lastLoginTokenWhenPolled = null;
|
|
}; // This is private, but it is exported for now because it is used by a
|
|
// test in accounts-password.
|
|
//
|
|
|
|
|
|
Ap._storedLoginToken = function () {
|
|
return Meteor._localStorage.getItem(this.LOGIN_TOKEN_KEY);
|
|
};
|
|
|
|
Ap._storedLoginTokenExpires = function () {
|
|
return Meteor._localStorage.getItem(this.LOGIN_TOKEN_EXPIRES_KEY);
|
|
};
|
|
|
|
Ap._storedUserId = function () {
|
|
return Meteor._localStorage.getItem(this.USER_ID_KEY);
|
|
};
|
|
|
|
Ap._unstoreLoginTokenIfExpiresSoon = function () {
|
|
var tokenExpires = this._storedLoginTokenExpires();
|
|
|
|
if (tokenExpires && this._tokenExpiresSoon(new Date(tokenExpires))) {
|
|
this._unstoreLoginToken();
|
|
}
|
|
}; ///
|
|
/// AUTO-LOGIN
|
|
///
|
|
|
|
|
|
Ap._initLocalStorage = function () {
|
|
var self = this; // Key names to use in localStorage
|
|
|
|
self.LOGIN_TOKEN_KEY = "Meteor.loginToken";
|
|
self.LOGIN_TOKEN_EXPIRES_KEY = "Meteor.loginTokenExpires";
|
|
self.USER_ID_KEY = "Meteor.userId";
|
|
var rootUrlPathPrefix = __meteor_runtime_config__.ROOT_URL_PATH_PREFIX;
|
|
|
|
if (rootUrlPathPrefix || this.connection !== Meteor.connection) {
|
|
// We want to keep using the same keys for existing apps that do not
|
|
// set a custom ROOT_URL_PATH_PREFIX, so that most users will not have
|
|
// to log in again after an app updates to a version of Meteor that
|
|
// contains this code, but it's generally preferable to namespace the
|
|
// keys so that connections from distinct apps to distinct DDP URLs
|
|
// will be distinct in Meteor._localStorage.
|
|
var namespace = ":" + this.connection._stream.rawUrl;
|
|
|
|
if (rootUrlPathPrefix) {
|
|
namespace += ":" + rootUrlPathPrefix;
|
|
}
|
|
|
|
self.LOGIN_TOKEN_KEY += namespace;
|
|
self.LOGIN_TOKEN_EXPIRES_KEY += namespace;
|
|
self.USER_ID_KEY += namespace;
|
|
}
|
|
|
|
if (self._autoLoginEnabled) {
|
|
// Immediately try to log in via local storage, so that any DDP
|
|
// messages are sent after we have established our user account
|
|
self._unstoreLoginTokenIfExpiresSoon();
|
|
|
|
var token = self._storedLoginToken();
|
|
|
|
if (token) {
|
|
// On startup, optimistically present us as logged in while the
|
|
// request is in flight. This reduces page flicker on startup.
|
|
var userId = self._storedUserId();
|
|
|
|
userId && self.connection.setUserId(userId);
|
|
self.loginWithToken(token, function (err) {
|
|
if (err) {
|
|
Meteor._debug("Error logging in with token: " + err);
|
|
|
|
self.makeClientLoggedOut();
|
|
}
|
|
|
|
self._pageLoadLogin({
|
|
type: "resume",
|
|
allowed: !err,
|
|
error: err,
|
|
methodName: "login",
|
|
// XXX This is duplicate code with loginWithToken, but
|
|
// loginWithToken can also be called at other times besides
|
|
// page load.
|
|
methodArguments: [{
|
|
resume: token
|
|
}]
|
|
});
|
|
});
|
|
}
|
|
} // Poll local storage every 3 seconds to login if someone logged in in
|
|
// another tab
|
|
|
|
|
|
self._lastLoginTokenWhenPolled = token;
|
|
|
|
if (self._pollIntervalTimer) {
|
|
// Unlikely that _initLocalStorage will be called more than once for
|
|
// the same AccountsClient instance, but just in case...
|
|
clearInterval(self._pollIntervalTimer);
|
|
}
|
|
|
|
self._pollIntervalTimer = setInterval(function () {
|
|
self._pollStoredLoginToken();
|
|
}, 3000);
|
|
};
|
|
|
|
Ap._pollStoredLoginToken = function () {
|
|
var self = this;
|
|
|
|
if (!self._autoLoginEnabled) {
|
|
return;
|
|
}
|
|
|
|
var currentLoginToken = self._storedLoginToken(); // != instead of !== just to make sure undefined and null are treated the same
|
|
|
|
|
|
if (self._lastLoginTokenWhenPolled != currentLoginToken) {
|
|
if (currentLoginToken) {
|
|
self.loginWithToken(currentLoginToken, function (err) {
|
|
if (err) {
|
|
self.makeClientLoggedOut();
|
|
}
|
|
});
|
|
} else {
|
|
self.logout();
|
|
}
|
|
}
|
|
|
|
self._lastLoginTokenWhenPolled = currentLoginToken;
|
|
};
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
},"url_client.js":function(require,exports,module){
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// packages/accounts-base/url_client.js //
|
|
// //
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
module.export({
|
|
AccountsTest: function () {
|
|
return AccountsTest;
|
|
}
|
|
});
|
|
var AccountsClient;
|
|
module.watch(require("./accounts_client.js"), {
|
|
AccountsClient: function (v) {
|
|
AccountsClient = v;
|
|
}
|
|
}, 0);
|
|
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
|
|
|
|
|
|
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;
|
|
var tokenRegex = new RegExp("^\\#\\/" + urlPart + "\\/(.*)$");
|
|
var match = hash.match(tokenRegex);
|
|
|
|
if (match) {
|
|
token = match[1]; // XXX COMPAT WITH 0.9.3
|
|
|
|
if (urlPart === "reset-password") {
|
|
accounts._resetPasswordToken = token;
|
|
} else if (urlPart === "verify-email") {
|
|
accounts._verifyEmailToken = token;
|
|
} else if (urlPart === "enroll-account") {
|
|
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.call(accounts, token, urlPart);
|
|
});
|
|
}
|
|
|
|
function defaultSuccessHandler(token, urlPart) {
|
|
var self = this; // put login in a suspended state to wait for the interaction to finish
|
|
|
|
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 (self._accountsCallbacks[urlPart]) {
|
|
self._accountsCallbacks[urlPart](token, function () {
|
|
self._enableAutoLogin();
|
|
});
|
|
}
|
|
});
|
|
} // Export for testing
|
|
|
|
|
|
var AccountsTest = {
|
|
attemptToMatchHash: function (hash, success) {
|
|
return attemptToMatchHash(Accounts, hash, success);
|
|
}
|
|
};
|
|
|
|
// XXX these should be moved to accounts-password eventually. Right now
|
|
// this is prevented by the need to set autoLoginEnabled=false, but in
|
|
// some bright future we won't need to do that anymore.
|
|
|
|
/**
|
|
* @summary Register a function to call when a reset password link is clicked
|
|
* in an email sent by
|
|
* [`Accounts.sendResetPasswordEmail`](#accounts_sendresetpasswordemail).
|
|
* This function should be called in top-level code, not inside
|
|
* `Meteor.startup()`.
|
|
* @memberof! Accounts
|
|
* @name onResetPasswordLink
|
|
* @param {Function} callback The function to call. It is given two arguments:
|
|
*
|
|
* 1. `token`: A password reset token that can be passed to
|
|
* [`Accounts.resetPassword`](#accounts_resetpassword).
|
|
* 2. `done`: A function to call when the password reset UI flow is complete. The normal
|
|
* login process is suspended until this function is called, so that the
|
|
* password for user A can be reset even if user B was logged in.
|
|
* @locus Client
|
|
*/
|
|
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.");
|
|
}
|
|
|
|
this._accountsCallbacks["reset-password"] = callback;
|
|
};
|
|
/**
|
|
* @summary Register a function to call when an email verification link is
|
|
* clicked in an email sent by
|
|
* [`Accounts.sendVerificationEmail`](#accounts_sendverificationemail).
|
|
* This function should be called in top-level code, not inside
|
|
* `Meteor.startup()`.
|
|
* @memberof! Accounts
|
|
* @name onEmailVerificationLink
|
|
* @param {Function} callback The function to call. It is given two arguments:
|
|
*
|
|
* 1. `token`: An email verification token that can be passed to
|
|
* [`Accounts.verifyEmail`](#accounts_verifyemail).
|
|
* 2. `done`: A function to call when the email verification UI flow is complete.
|
|
* The normal login process is suspended until this function is called, so
|
|
* that the user can be notified that they are verifying their email before
|
|
* being logged in.
|
|
* @locus Client
|
|
*/
|
|
|
|
|
|
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.");
|
|
}
|
|
|
|
this._accountsCallbacks["verify-email"] = callback;
|
|
};
|
|
/**
|
|
* @summary Register a function to call when an account enrollment link is
|
|
* clicked in an email sent by
|
|
* [`Accounts.sendEnrollmentEmail`](#accounts_sendenrollmentemail).
|
|
* This function should be called in top-level code, not inside
|
|
* `Meteor.startup()`.
|
|
* @memberof! Accounts
|
|
* @name onEnrollmentLink
|
|
* @param {Function} callback The function to call. It is given two arguments:
|
|
*
|
|
* 1. `token`: A password reset token that can be passed to
|
|
* [`Accounts.resetPassword`](#accounts_resetpassword) to give the newly
|
|
* enrolled account a password.
|
|
* 2. `done`: A function to call when the enrollment UI flow is complete.
|
|
* The normal login process is suspended until this function is called, so that
|
|
* user A can be enrolled even if user B was logged in.
|
|
* @locus Client
|
|
*/
|
|
|
|
|
|
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.");
|
|
}
|
|
|
|
this._accountsCallbacks["enroll-account"] = callback;
|
|
};
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
}}}}},{
|
|
"extensions": [
|
|
".js",
|
|
".json"
|
|
]
|
|
});
|
|
var exports = require("/node_modules/meteor/accounts-base/client_main.js");
|
|
|
|
/* Exports */
|
|
Package._define("accounts-base", exports, {
|
|
Accounts: Accounts
|
|
});
|
|
|
|
})();
|