Files
2025-02-26 14:49:25 +07:00

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
});
})();