(function () {

/* Imports */
var Meteor = Package.meteor.Meteor;
var _ = Package.underscore._;
var ECMAScript = Package.ecmascript.ECMAScript;
var DDPRateLimiter = Package['ddp-rate-limiter'].DDPRateLimiter;
var check = Package.check.check;
var Match = Package.check.Match;
var Random = Package.random.Random;
var EJSON = Package.ejson.EJSON;
var Hook = Package['callback-hook'].Hook;
var DDP = Package['ddp-client'].DDP;
var DDPServer = Package['ddp-server'].DDPServer;
var MongoInternals = Package.mongo.MongoInternals;
var Mongo = Package.mongo.Mongo;
var babelHelpers = Package['babel-runtime'].babelHelpers;
var Symbol = Package['ecmascript-runtime'].Symbol;
var Map = Package['ecmascript-runtime'].Map;
var Set = Package['ecmascript-runtime'].Set;
var Promise = Package.promise.Promise;

/* Package-scope variables */
var AccountsCommon, EXPIRE_TOKENS_INTERVAL_MS, CONNECTION_CLOSE_DELAY_MS, AccountsServer, Accounts, AccountsTest;

(function(){

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                 //
// packages/accounts-base/accounts_common.js                                                                       //
//                                                                                                                 //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                                   //
/**                                                                                                                //
 * @summary Super-constructor for AccountsClient and AccountsServer.                                               //
 * @locus Anywhere                                                                                                 //
 * @class AccountsCommon                                                                                           //
 * @instancename accountsClientOrServer                                                                            //
 * @param options {Object} an object with fields:                                                                  //
 * - connection {Object} Optional DDP connection to reuse.                                                         //
 * - ddpUrl {String} Optional URL for creating a new DDP connection.                                               //
 */                                                                                                                //
AccountsCommon = (function () {                                                                                    // 10
  function AccountsCommon(options) {                                                                               // 11
    babelHelpers.classCallCheck(this, AccountsCommon);                                                             //
                                                                                                                   //
    // Currently this is read directly by packages like accounts-password                                          //
    // and accounts-ui-unstyled.                                                                                   //
    this._options = {};                                                                                            // 14
                                                                                                                   //
    // Note that setting this.connection = null causes this.users to be a                                          //
    // LocalCollection, which is not what we want.                                                                 //
    this.connection = undefined;                                                                                   // 18
    this._initConnection(options || {});                                                                           // 19
                                                                                                                   //
    // There is an allow call in accounts_server.js that restricts writes to                                       //
    // this collection.                                                                                            //
    this.users = new Mongo.Collection("users", {                                                                   // 23
      _preventAutopublish: true,                                                                                   // 24
      connection: this.connection                                                                                  // 25
    });                                                                                                            //
                                                                                                                   //
    // Callback exceptions are printed with Meteor._debug and ignored.                                             //
    this._onLoginHook = new Hook({                                                                                 // 29
      bindEnvironment: false,                                                                                      // 30
      debugPrintExceptions: "onLogin callback"                                                                     // 31
    });                                                                                                            //
                                                                                                                   //
    this._onLoginFailureHook = new Hook({                                                                          // 34
      bindEnvironment: false,                                                                                      // 35
      debugPrintExceptions: "onLoginFailure callback"                                                              // 36
    });                                                                                                            //
  }                                                                                                                //
                                                                                                                   //
  /**                                                                                                              //
   * @summary Get the current user id, or `null` if no user is logged in. A reactive data source.                  //
   * @locus Anywhere but publish functions                                                                         //
   */                                                                                                              //
                                                                                                                   //
  AccountsCommon.prototype.userId = (function () {                                                                 // 10
    function userId() {                                                                                            // 44
      throw new Error("userId method not implemented");                                                            // 45
    }                                                                                                              //
                                                                                                                   //
    return userId;                                                                                                 //
  })();                                                                                                            //
                                                                                                                   //
  /**                                                                                                              //
   * @summary Get the current user record, or `null` if no user is logged in. A reactive data source.              //
   * @locus Anywhere but publish functions                                                                         //
   */                                                                                                              //
                                                                                                                   //
  AccountsCommon.prototype.user = (function () {                                                                   // 10
    function user() {                                                                                              // 52
      var userId = this.userId();                                                                                  // 53
      return userId ? this.users.findOne(userId) : null;                                                           // 54
    }                                                                                                              //
                                                                                                                   //
    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).                                                                                                 //
                                                                                                                   //
  /**                                                                                                              //
   * @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.
   */                                                                                                              //
                                                                                                                   //
  AccountsCommon.prototype.config = (function () {                                                                 // 10
    function config(options) {                                                                                     // 92
      var self = this;                                                                                             // 93
                                                                                                                   //
      // 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) {                                                                                       // 100
        __meteor_runtime_config__.accountsConfigCalled = true;                                                     // 101
      } 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")) {                                                                      // 112
        if (Meteor.isClient) throw new Error("The oauthSecretKey option may only be specified on the server");     // 113
        if (!Package["oauth-encryption"]) throw new Error("The oauth-encryption package must be loaded to set oauthSecretKey");
        Package["oauth-encryption"].OAuthEncryption.loadKey(options.oauthSecretKey);                               // 117
        options = _.omit(options, "oauthSecretKey");                                                               // 118
      }                                                                                                            //
                                                                                                                   //
      // validate option keys                                                                                      //
      var VALID_KEYS = ["sendVerificationEmail", "forbidClientAccountCreation", "restrictCreationByEmailDomain", "loginExpirationInDays"];
      _.each(_.keys(options), function (key) {                                                                     // 124
        if (!_.contains(VALID_KEYS, key)) {                                                                        // 125
          throw new Error("Accounts.config: Invalid key: " + key);                                                 // 126
        }                                                                                                          //
      });                                                                                                          //
                                                                                                                   //
      // set values in Accounts._options                                                                           //
      _.each(VALID_KEYS, function (key) {                                                                          // 131
        if (key in options) {                                                                                      // 132
          if (key in self._options) {                                                                              // 133
            throw new Error("Can't set `" + key + "` more than once");                                             // 134
          }                                                                                                        //
          self._options[key] = options[key];                                                                       // 136
        }                                                                                                          //
      });                                                                                                          //
    }                                                                                                              //
                                                                                                                   //
    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.                                    //
   */                                                                                                              //
                                                                                                                   //
  AccountsCommon.prototype.onLogin = (function () {                                                                // 10
    function onLogin(func) {                                                                                       // 146
      return this._onLoginHook.register(func);                                                                     // 147
    }                                                                                                              //
                                                                                                                   //
    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.                                  //
   */                                                                                                              //
                                                                                                                   //
  AccountsCommon.prototype.onLoginFailure = (function () {                                                         // 10
    function onLoginFailure(func) {                                                                                // 155
      return this._onLoginFailureHook.register(func);                                                              // 156
    }                                                                                                              //
                                                                                                                   //
    return onLoginFailure;                                                                                         //
  })();                                                                                                            //
                                                                                                                   //
  AccountsCommon.prototype._initConnection = (function () {                                                        // 10
    function _initConnection(options) {                                                                            // 159
      if (!Meteor.isClient) {                                                                                      // 160
        return;                                                                                                    // 161
      }                                                                                                            //
                                                                                                                   //
      // 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) {                                                                                    // 172
        this.connection = options.connection;                                                                      // 173
      } else if (options.ddpUrl) {                                                                                 //
        this.connection = DDP.connect(options.ddpUrl);                                                             // 175
      } 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);                          // 185
      } else {                                                                                                     //
        this.connection = Meteor.connection;                                                                       // 188
      }                                                                                                            //
    }                                                                                                              //
                                                                                                                   //
    return _initConnection;                                                                                        //
  })();                                                                                                            //
                                                                                                                   //
  AccountsCommon.prototype._getTokenLifetimeMs = (function () {                                                    // 10
    function _getTokenLifetimeMs() {                                                                               // 192
      return (this._options.loginExpirationInDays || DEFAULT_LOGIN_EXPIRATION_DAYS) * 24 * 60 * 60 * 1000;         // 193
    }                                                                                                              //
                                                                                                                   //
    return _getTokenLifetimeMs;                                                                                    //
  })();                                                                                                            //
                                                                                                                   //
  AccountsCommon.prototype._tokenExpiration = (function () {                                                       // 10
    function _tokenExpiration(when) {                                                                              // 197
      // 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());                                      // 200
    }                                                                                                              //
                                                                                                                   //
    return _tokenExpiration;                                                                                       //
  })();                                                                                                            //
                                                                                                                   //
  AccountsCommon.prototype._tokenExpiresSoon = (function () {                                                      // 10
    function _tokenExpiresSoon(when) {                                                                             // 203
      var minLifetimeMs = .1 * this._getTokenLifetimeMs();                                                         // 204
      var minLifetimeCapMs = MIN_TOKEN_LIFETIME_CAP_SECS * 1000;                                                   // 205
      if (minLifetimeMs > minLifetimeCapMs) minLifetimeMs = minLifetimeCapMs;                                      // 206
      return new Date() > new Date(when) - minLifetimeMs;                                                          // 208
    }                                                                                                              //
                                                                                                                   //
    return _tokenExpiresSoon;                                                                                      //
  })();                                                                                                            //
                                                                                                                   //
  return AccountsCommon;                                                                                           //
})();                                                                                                              //
                                                                                                                   //
var Ap = AccountsCommon.prototype;                                                                                 // 212
                                                                                                                   //
// 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                                                                           //
 */                                                                                                                //
Meteor.userId = function () {                                                                                      // 221
  return Accounts.userId();                                                                                        // 222
};                                                                                                                 //
                                                                                                                   //
/**                                                                                                                //
 * @summary Get the current user record, or `null` if no user is logged in. A reactive data source.                //
 * @locus Anywhere but publish functions                                                                           //
 */                                                                                                                //
Meteor.user = function () {                                                                                        // 229
  return Accounts.user();                                                                                          // 230
};                                                                                                                 //
                                                                                                                   //
// how long (in days) until a login token expires                                                                  //
var DEFAULT_LOGIN_EXPIRATION_DAYS = 90;                                                                            // 234
// 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                                                                // 238
// how often (in milliseconds) we check for expired tokens                                                         //
EXPIRE_TOKENS_INTERVAL_MS = 600 * 1000; // 10 minutes                                                              // 240
// how long we wait before logging out clients when Meteor.logoutOtherClients is                                   //
// called                                                                                                          //
CONNECTION_CLOSE_DELAY_MS = 10 * 1000;                                                                             // 243
                                                                                                                   //
// loginServiceConfiguration and ConfigError are maintained for backwards compatibility                            //
Meteor.startup(function () {                                                                                       // 246
  var ServiceConfiguration = Package['service-configuration'].ServiceConfiguration;                                // 247
  Ap.loginServiceConfiguration = ServiceConfiguration.configurations;                                              // 249
  Ap.ConfigError = ServiceConfiguration.ConfigError;                                                               // 250
});                                                                                                                //
                                                                                                                   //
// Thrown when the user cancels the login process (eg, closes an oauth                                             //
// popup, declines retina scan, etc)                                                                               //
var lceName = 'Accounts.LoginCancelledError';                                                                      // 255
Ap.LoginCancelledError = Meteor.makeErrorType(lceName, function (description) {                                    // 256
  this.message = description;                                                                                      // 259
});                                                                                                                //
Ap.LoginCancelledError.prototype.name = lceName;                                                                   // 262
                                                                                                                   //
// 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;                                                                   // 267
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function(){

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                 //
// packages/accounts-base/accounts_server.js                                                                       //
//                                                                                                                 //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                                   //
var crypto = Npm.require('crypto');                                                                                // 1
                                                                                                                   //
/**                                                                                                                //
 * @summary Constructor for the `Accounts` namespace on the server.                                                //
 * @locus Server                                                                                                   //
 * @class                                                                                                          //
 * @extends AccountsCommon                                                                                         //
 * @instancename accountsServer                                                                                    //
 * @param {Object} server A server object such as `Meteor.server`.                                                 //
 */                                                                                                                //
AccountsServer = (function (_AccountsCommon) {                                                                     // 11
  babelHelpers.inherits(AccountsServer, _AccountsCommon);                                                          //
                                                                                                                   //
  // Note that this constructor is less likely to be instantiated multiple                                         //
  // times than the `AccountsClient` constructor, because a single server                                          //
  // can provide only one set of methods.                                                                          //
                                                                                                                   //
  function AccountsServer(server) {                                                                                // 15
    babelHelpers.classCallCheck(this, AccountsServer);                                                             //
                                                                                                                   //
    _AccountsCommon.call(this);                                                                                    // 16
                                                                                                                   //
    this._server = server || Meteor.server;                                                                        // 18
    // Set up the server's methods, as if by calling Meteor.methods.                                               //
    this._initServerMethods();                                                                                     // 20
                                                                                                                   //
    this._initAccountDataHooks();                                                                                  // 22
                                                                                                                   //
    // If autopublish is on, publish these user fields. Login service                                              //
    // packages (eg accounts-google) add to these by calling                                                       //
    // addAutopublishFields.  Notably, this isn't implemented with multiple                                        //
    // publishes since DDP only merges only across top-level fields, not                                           //
    // subfields (such as 'services.facebook.accessToken')                                                         //
    this._autopublishFields = {                                                                                    // 29
      loggedInUser: ['profile', 'username', 'emails'],                                                             // 30
      otherUsers: ['profile', 'username']                                                                          // 31
    };                                                                                                             //
    this._initServerPublications();                                                                                // 33
                                                                                                                   //
    // connectionId -> {connection, loginToken}                                                                    //
    this._accountData = {};                                                                                        // 36
                                                                                                                   //
    // connection id -> observe handle for the login token that this connection is                                 //
    // currently associated with, or a number. The number indicates that we are in                                 //
    // the process of setting up the observe (using a number instead of a single                                   //
    // sentinel allows multiple attempts to set up the observe to identify which                                   //
    // one was theirs).                                                                                            //
    this._userObservesForConnections = {};                                                                         // 43
    this._nextUserObserveNumber = 1; // for the number described above.                                            // 44
                                                                                                                   //
    // list of all registered handlers.                                                                            //
    this._loginHandlers = [];                                                                                      // 47
                                                                                                                   //
    setupUsersCollection(this.users);                                                                              // 49
    setupDefaultLoginHandlers(this);                                                                               // 50
    setExpireTokensInterval(this);                                                                                 // 51
                                                                                                                   //
    this._validateLoginHook = new Hook({ bindEnvironment: false });                                                // 53
    this._validateNewUserHooks = [defaultValidateNewUserHook.bind(this)];                                          // 54
                                                                                                                   //
    this._deleteSavedTokensForAllUsersOnStartup();                                                                 // 58
                                                                                                                   //
    this._skipCaseInsensitiveChecksForTest = {};                                                                   // 60
  }                                                                                                                //
                                                                                                                   //
  ///                                                                                                              //
  /// CURRENT USER                                                                                                 //
  ///                                                                                                              //
                                                                                                                   //
  // @override of "abstract" non-implementation in accounts_common.js                                              //
                                                                                                                   //
  AccountsServer.prototype.userId = (function () {                                                                 // 11
    function userId() {                                                                                            // 68
      // This function only works if called inside a method. In theory, it                                         //
      // could also be called from publish statements, since they also                                             //
      // have a userId associated with them. However, given that publish                                           //
      // functions aren't reactive, using any of the infomation from                                               //
      // Meteor.user() in a publish function will always use the value                                             //
      // from when the function first runs. This is likely not what the                                            //
      // user expects. The way to make this work in a publish is to do                                             //
      // Meteor.find(this.userId).observe and recompute when the user                                              //
      // record changes.                                                                                           //
      var currentInvocation = DDP._CurrentInvocation.get();                                                        // 78
      if (!currentInvocation) throw new Error("Meteor.userId can only be invoked in method calls. Use this.userId in publish functions.");
      return currentInvocation.userId;                                                                             // 81
    }                                                                                                              //
                                                                                                                   //
    return userId;                                                                                                 //
  })();                                                                                                            //
                                                                                                                   //
  ///                                                                                                              //
  /// LOGIN HOOKS                                                                                                  //
  ///                                                                                                              //
                                                                                                                   //
  /**                                                                                                              //
   * @summary Validate login attempts.                                                                             //
   * @locus Server                                                                                                 //
   * @param {Function} func Called whenever a login is attempted (either successful or unsuccessful).  A login can be aborted by returning a falsy value or throwing an exception.
   */                                                                                                              //
                                                                                                                   //
  AccountsServer.prototype.validateLoginAttempt = (function () {                                                   // 11
    function validateLoginAttempt(func) {                                                                          // 93
      // Exceptions inside the hook callback are passed up to us.                                                  //
      return this._validateLoginHook.register(func);                                                               // 95
    }                                                                                                              //
                                                                                                                   //
    return validateLoginAttempt;                                                                                   //
  })();                                                                                                            //
                                                                                                                   //
  /**                                                                                                              //
   * @summary Set restrictions on new user creation.                                                               //
   * @locus Server                                                                                                 //
   * @param {Function} func Called whenever a new user is created. Takes the new user object, and returns true to allow the creation or false to abort.
   */                                                                                                              //
                                                                                                                   //
  AccountsServer.prototype.validateNewUser = (function () {                                                        // 11
    function validateNewUser(func) {                                                                               // 103
      this._validateNewUserHooks.push(func);                                                                       // 104
    }                                                                                                              //
                                                                                                                   //
    return validateNewUser;                                                                                        //
  })();                                                                                                            //
                                                                                                                   //
  ///                                                                                                              //
  /// CREATE USER HOOKS                                                                                            //
  ///                                                                                                              //
                                                                                                                   //
  /**                                                                                                              //
   * @summary Customize new user creation.                                                                         //
   * @locus Server                                                                                                 //
   * @param {Function} func Called whenever a new user is created. Return the new user object, or throw an `Error` to abort the creation.
   */                                                                                                              //
                                                                                                                   //
  AccountsServer.prototype.onCreateUser = (function () {                                                           // 11
    function onCreateUser(func) {                                                                                  // 116
      if (this._onCreateUserHook) {                                                                                // 117
        throw new Error("Can only call onCreateUser once");                                                        // 118
      }                                                                                                            //
                                                                                                                   //
      this._onCreateUserHook = func;                                                                               // 121
    }                                                                                                              //
                                                                                                                   //
    return onCreateUser;                                                                                           //
  })();                                                                                                            //
                                                                                                                   //
  return AccountsServer;                                                                                           //
})(AccountsCommon);                                                                                                //
                                                                                                                   //
var Ap = AccountsServer.prototype;                                                                                 // 125
                                                                                                                   //
// Give each login hook callback a fresh cloned copy of the attempt                                                //
// object, but don't clone the connection.                                                                         //
//                                                                                                                 //
function cloneAttemptWithConnection(connection, attempt) {                                                         // 130
  var clonedAttempt = EJSON.clone(attempt);                                                                        // 131
  clonedAttempt.connection = connection;                                                                           // 132
  return clonedAttempt;                                                                                            // 133
}                                                                                                                  //
                                                                                                                   //
Ap._validateLogin = function (connection, attempt) {                                                               // 136
  this._validateLoginHook.each(function (callback) {                                                               // 137
    var ret;                                                                                                       // 138
    try {                                                                                                          // 139
      ret = callback(cloneAttemptWithConnection(connection, attempt));                                             // 140
    } catch (e) {                                                                                                  //
      attempt.allowed = false;                                                                                     // 143
      // XXX this means the last thrown error overrides previous error                                             //
      // messages. Maybe this is surprising to users and we should make                                            //
      // overriding errors more explicit. (see                                                                     //
      // https://github.com/meteor/meteor/issues/1960)                                                             //
      attempt.error = e;                                                                                           // 148
      return true;                                                                                                 // 149
    }                                                                                                              //
    if (!ret) {                                                                                                    // 151
      attempt.allowed = false;                                                                                     // 152
      // don't override a specific error provided by a previous                                                    //
      // validator or the initial attempt (eg "incorrect password").                                               //
      if (!attempt.error) attempt.error = new Meteor.Error(403, "Login forbidden");                                // 155
    }                                                                                                              //
    return true;                                                                                                   // 158
  });                                                                                                              //
};                                                                                                                 //
                                                                                                                   //
Ap._successfulLogin = function (connection, attempt) {                                                             // 163
  this._onLoginHook.each(function (callback) {                                                                     // 164
    callback(cloneAttemptWithConnection(connection, attempt));                                                     // 165
    return true;                                                                                                   // 166
  });                                                                                                              //
};                                                                                                                 //
                                                                                                                   //
Ap._failedLogin = function (connection, attempt) {                                                                 // 170
  this._onLoginFailureHook.each(function (callback) {                                                              // 171
    callback(cloneAttemptWithConnection(connection, attempt));                                                     // 172
    return true;                                                                                                   // 173
  });                                                                                                              //
};                                                                                                                 //
                                                                                                                   //
///                                                                                                                //
/// LOGIN METHODS                                                                                                  //
///                                                                                                                //
                                                                                                                   //
// Login methods return to the client an object containing these                                                   //
// fields when the user was logged in successfully:                                                                //
//                                                                                                                 //
//   id: userId                                                                                                    //
//   token: *                                                                                                      //
//   tokenExpires: *                                                                                               //
//                                                                                                                 //
// tokenExpires is optional and intends to provide a hint to the                                                   //
// client as to when the token will expire. If not provided, the                                                   //
// client will call Accounts._tokenExpiration, passing it the date                                                 //
// that it received the token.                                                                                     //
//                                                                                                                 //
// The login method will throw an error back to the client if the user                                             //
// failed to log in.                                                                                               //
//                                                                                                                 //
//                                                                                                                 //
// Login handlers and service specific login methods such as                                                       //
// `createUser` internally return a `result` object containing these                                               //
// fields:                                                                                                         //
//                                                                                                                 //
//   type:                                                                                                         //
//     optional string; the service name, overrides the handler                                                    //
//     default if present.                                                                                         //
//                                                                                                                 //
//   error:                                                                                                        //
//     exception; if the user is not allowed to login, the reason why.                                             //
//                                                                                                                 //
//   userId:                                                                                                       //
//     string; the user id of the user attempting to login (if                                                     //
//     known), required for an allowed login.                                                                      //
//                                                                                                                 //
//   options:                                                                                                      //
//     optional object merged into the result returned by the login                                                //
//     method; used by HAMK from SRP.                                                                              //
//                                                                                                                 //
//   stampedLoginToken:                                                                                            //
//     optional object with `token` and `when` indicating the login                                                //
//     token is already present in the database, returned by the                                                   //
//     "resume" login handler.                                                                                     //
//                                                                                                                 //
// For convenience, login methods can also throw an exception, which                                               //
// is converted into an {error} result.  However, if the id of the                                                 //
// user attempting the login is known, a {userId, error} result should                                             //
// be returned instead since the user id is not captured when an                                                   //
// exception is thrown.                                                                                            //
//                                                                                                                 //
// This internal `result` object is automatically converted into the                                               //
// public {id, token, tokenExpires} object returned to the client.                                                 //
                                                                                                                   //
// Try a login method, converting thrown exceptions into an {error}                                                //
// result.  The `type` argument is a default, inserted into the result                                             //
// object if not explicitly returned.                                                                              //
//                                                                                                                 //
var tryLoginMethod = function (type, fn) {                                                                         // 236
  var result;                                                                                                      // 237
  try {                                                                                                            // 238
    result = fn();                                                                                                 // 239
  } catch (e) {                                                                                                    //
    result = { error: e };                                                                                         // 242
  }                                                                                                                //
                                                                                                                   //
  if (result && !result.type && type) result.type = type;                                                          // 245
                                                                                                                   //
  return result;                                                                                                   // 248
};                                                                                                                 //
                                                                                                                   //
// Log in a user on a connection.                                                                                  //
//                                                                                                                 //
// We use the method invocation to set the user id on the connection,                                              //
// not the connection object directly. setUserId is tied to methods to                                             //
// enforce clear ordering of method application (using wait methods on                                             //
// the client, and a no setUserId after unblock restriction on the                                                 //
// server)                                                                                                         //
//                                                                                                                 //
// The `stampedLoginToken` parameter is optional.  When present, it                                                //
// indicates that the login token has already been inserted into the                                               //
// database and doesn't need to be inserted again.  (It's used by the                                              //
// "resume" login handler).                                                                                        //
Ap._loginUser = function (methodInvocation, userId, stampedLoginToken) {                                           // 264
  var self = this;                                                                                                 // 265
                                                                                                                   //
  if (!stampedLoginToken) {                                                                                        // 267
    stampedLoginToken = self._generateStampedLoginToken();                                                         // 268
    self._insertLoginToken(userId, stampedLoginToken);                                                             // 269
  }                                                                                                                //
                                                                                                                   //
  // This order (and the avoidance of yields) is important to make                                                 //
  // sure that when publish functions are rerun, they see a                                                        //
  // consistent view of the world: the userId is set and matches                                                   //
  // the login token on the connection (not that there is                                                          //
  // currently a public API for reading the login token on a                                                       //
  // connection).                                                                                                  //
  Meteor._noYieldsAllowed(function () {                                                                            // 278
    self._setLoginToken(userId, methodInvocation.connection, self._hashLoginToken(stampedLoginToken.token));       // 279
  });                                                                                                              //
                                                                                                                   //
  methodInvocation.setUserId(userId);                                                                              // 286
                                                                                                                   //
  return {                                                                                                         // 288
    id: userId,                                                                                                    // 289
    token: stampedLoginToken.token,                                                                                // 290
    tokenExpires: self._tokenExpiration(stampedLoginToken.when)                                                    // 291
  };                                                                                                               //
};                                                                                                                 //
                                                                                                                   //
// After a login method has completed, call the login hooks.  Note                                                 //
// that `attemptLogin` is called for *all* login attempts, even ones                                               //
// which aren't successful (such as an invalid password, etc).                                                     //
//                                                                                                                 //
// If the login is allowed and isn't aborted by a validate login hook                                              //
// callback, log in the user.                                                                                      //
//                                                                                                                 //
Ap._attemptLogin = function (methodInvocation, methodName, methodArgs, result) {                                   // 303
  if (!result) throw new Error("result is required");                                                              // 309
                                                                                                                   //
  // XXX A programming error in a login handler can lead to this occuring, and                                     //
  // then we don't call onLogin or onLoginFailure callbacks. Should                                                //
  // tryLoginMethod catch this case and turn it into an error?                                                     //
  if (!result.userId && !result.error) throw new Error("A login method must specify a userId or an error");        // 315
                                                                                                                   //
  var user;                                                                                                        // 318
  if (result.userId) user = this.users.findOne(result.userId);                                                     // 319
                                                                                                                   //
  var attempt = {                                                                                                  // 322
    type: result.type || "unknown",                                                                                // 323
    allowed: !!(result.userId && !result.error),                                                                   // 324
    methodName: methodName,                                                                                        // 325
    methodArguments: _.toArray(methodArgs)                                                                         // 326
  };                                                                                                               //
  if (result.error) attempt.error = result.error;                                                                  // 328
  if (user) attempt.user = user;                                                                                   // 330
                                                                                                                   //
  // _validateLogin may mutate `attempt` by adding an error and changing allowed                                   //
  // to false, but that's the only change it can make (and the user's callbacks                                    //
  // only get a clone of `attempt`).                                                                               //
  this._validateLogin(methodInvocation.connection, attempt);                                                       // 336
                                                                                                                   //
  if (attempt.allowed) {                                                                                           // 338
    var ret = _.extend(this._loginUser(methodInvocation, result.userId, result.stampedLoginToken), result.options || {});
    this._successfulLogin(methodInvocation.connection, attempt);                                                   // 347
    return ret;                                                                                                    // 348
  } else {                                                                                                         //
    this._failedLogin(methodInvocation.connection, attempt);                                                       // 351
    throw attempt.error;                                                                                           // 352
  }                                                                                                                //
};                                                                                                                 //
                                                                                                                   //
// All service specific login methods should go through this function.                                             //
// Ensure that thrown exceptions are caught and that login hook                                                    //
// callbacks are still called.                                                                                     //
//                                                                                                                 //
Ap._loginMethod = function (methodInvocation, methodName, methodArgs, type, fn) {                                  // 361
  return this._attemptLogin(methodInvocation, methodName, methodArgs, tryLoginMethod(type, fn));                   // 368
};                                                                                                                 //
                                                                                                                   //
// Report a login attempt failed outside the context of a normal login                                             //
// method. This is for use in the case where there is a multi-step login                                           //
// procedure (eg SRP based password login). If a method early in the                                               //
// chain fails, it should call this function to report a failure. There                                            //
// is no corresponding method for a successful login; methods that can                                             //
// succeed at logging a user in should always be actual login methods                                              //
// (using either Accounts._loginMethod or Accounts.registerLoginHandler).                                          //
Ap._reportLoginFailure = function (methodInvocation, methodName, methodArgs, result) {                             // 384
  var attempt = {                                                                                                  // 390
    type: result.type || "unknown",                                                                                // 391
    allowed: false,                                                                                                // 392
    error: result.error,                                                                                           // 393
    methodName: methodName,                                                                                        // 394
    methodArguments: _.toArray(methodArgs)                                                                         // 395
  };                                                                                                               //
                                                                                                                   //
  if (result.userId) {                                                                                             // 398
    attempt.user = this.users.findOne(result.userId);                                                              // 399
  }                                                                                                                //
                                                                                                                   //
  this._validateLogin(methodInvocation.connection, attempt);                                                       // 402
  this._failedLogin(methodInvocation.connection, attempt);                                                         // 403
                                                                                                                   //
  // _validateLogin may mutate attempt to set a new error message. Return                                          //
  // the modified version.                                                                                         //
  return attempt;                                                                                                  // 407
};                                                                                                                 //
                                                                                                                   //
///                                                                                                                //
/// LOGIN HANDLERS                                                                                                 //
///                                                                                                                //
                                                                                                                   //
// The main entry point for auth packages to hook in to login.                                                     //
//                                                                                                                 //
// A login handler is a login method which can return `undefined` to                                               //
// indicate that the login request is not handled by this handler.                                                 //
//                                                                                                                 //
// @param name {String} Optional.  The service name, used by default                                               //
// if a specific service name isn't returned in the result.                                                        //
//                                                                                                                 //
// @param handler {Function} A function that receives an options object                                            //
// (as passed as an argument to the `login` method) and returns one of:                                            //
// - `undefined`, meaning don't handle;                                                                            //
// - a login method result object                                                                                  //
                                                                                                                   //
Ap.registerLoginHandler = function (name, handler) {                                                               // 428
  if (!handler) {                                                                                                  // 429
    handler = name;                                                                                                // 430
    name = null;                                                                                                   // 431
  }                                                                                                                //
                                                                                                                   //
  this._loginHandlers.push({                                                                                       // 434
    name: name,                                                                                                    // 435
    handler: handler                                                                                               // 436
  });                                                                                                              //
};                                                                                                                 //
                                                                                                                   //
// Checks a user's credentials against all the registered login                                                    //
// handlers, and returns a login token if the credentials are valid. It                                            //
// is like the login method, except that it doesn't set the logged-in                                              //
// user on the connection. Throws a Meteor.Error if logging in fails,                                              //
// including the case where none of the login handlers handled the login                                           //
// request. Otherwise, returns {id: userId, token: *, tokenExpires: *}.                                            //
//                                                                                                                 //
// For example, if you want to login with a plaintext password, `options` could be                                 //
//   { user: { username: <username> }, password: <password> }, or                                                  //
//   { user: { email: <email> }, password: <password> }.                                                           //
                                                                                                                   //
// Try all of the registered login handlers until one of them doesn't                                              //
// return `undefined`, meaning it handled this call to `login`. Return                                             //
// that return value.                                                                                              //
Ap._runLoginHandlers = function (methodInvocation, options) {                                                      // 455
  for (var i = 0; i < this._loginHandlers.length; ++i) {                                                           // 456
    var handler = this._loginHandlers[i];                                                                          // 457
                                                                                                                   //
    var result = tryLoginMethod(handler.name, function () {                                                        // 459
      return handler.handler.call(methodInvocation, options);                                                      // 462
    });                                                                                                            //
                                                                                                                   //
    if (result) {                                                                                                  // 466
      return result;                                                                                               // 467
    }                                                                                                              //
                                                                                                                   //
    if (result !== undefined) {                                                                                    // 470
      throw new Meteor.Error(400, "A login handler should return a result or undefined");                          // 471
    }                                                                                                              //
  }                                                                                                                //
                                                                                                                   //
  return {                                                                                                         // 475
    type: null,                                                                                                    // 476
    error: new Meteor.Error(400, "Unrecognized options for login request")                                         // 477
  };                                                                                                               //
};                                                                                                                 //
                                                                                                                   //
// Deletes the given loginToken from the database.                                                                 //
//                                                                                                                 //
// For new-style hashed token, this will cause all connections                                                     //
// associated with the token to be closed.                                                                         //
//                                                                                                                 //
// Any connections associated with old-style unhashed tokens will be                                               //
// in the process of becoming associated with hashed tokens and then                                               //
// they'll get closed.                                                                                             //
Ap.destroyToken = function (userId, loginToken) {                                                                  // 489
  this.users.update(userId, {                                                                                      // 490
    $pull: {                                                                                                       // 491
      "services.resume.loginTokens": {                                                                             // 492
        $or: [{ hashedToken: loginToken }, { token: loginToken }]                                                  // 493
      }                                                                                                            //
    }                                                                                                              //
  });                                                                                                              //
};                                                                                                                 //
                                                                                                                   //
Ap._initServerMethods = function () {                                                                              // 502
  // The methods created in this function need to be created here so that                                          //
  // this variable is available in their scope.                                                                    //
  var accounts = this;                                                                                             // 505
                                                                                                                   //
  // This object will be populated with methods and then passed to                                                 //
  // accounts._server.methods further below.                                                                       //
  var methods = {};                                                                                                // 509
                                                                                                                   //
  // @returns {Object|null}                                                                                        //
  //   If successful, returns {token: reconnectToken, id: userId}                                                  //
  //   If unsuccessful (for example, if the user closed the oauth login popup),                                    //
  //     throws an error describing the reason                                                                     //
  methods.login = function (options) {                                                                             // 515
    var self = this;                                                                                               // 516
                                                                                                                   //
    // Login handlers should really also check whatever field they look at in                                      //
    // options, but we don't enforce it.                                                                           //
    check(options, Object);                                                                                        // 520
                                                                                                                   //
    var result = accounts._runLoginHandlers(self, options);                                                        // 522
                                                                                                                   //
    return accounts._attemptLogin(self, "login", arguments, result);                                               // 524
  };                                                                                                               //
                                                                                                                   //
  methods.logout = function () {                                                                                   // 527
    var token = accounts._getLoginToken(this.connection.id);                                                       // 528
    accounts._setLoginToken(this.userId, this.connection, null);                                                   // 529
    if (token && this.userId) accounts.destroyToken(this.userId, token);                                           // 530
    this.setUserId(null);                                                                                          // 532
  };                                                                                                               //
                                                                                                                   //
  // Delete all the current user's tokens and close all open connections logged                                    //
  // in as this user. Returns a fresh new login token that this client can                                         //
  // use. Tests set Accounts._noConnectionCloseDelayForTest to delete tokens                                       //
  // immediately instead of using a delay.                                                                         //
  //                                                                                                               //
  // XXX COMPAT WITH 0.7.2                                                                                         //
  // This single `logoutOtherClients` method has been replaced with two                                            //
  // methods, one that you call to get a new token, and another that you                                           //
  // call to remove all tokens except your own. The new design allows                                              //
  // clients to know when other clients have actually been logged                                                  //
  // out. (The `logoutOtherClients` method guarantees the caller that                                              //
  // the other clients will be logged out at some point, but makes no                                              //
  // guarantees about when.) This method is left in for backwards                                                  //
  // compatibility, especially since application code might be calling                                             //
  // this method directly.                                                                                         //
  //                                                                                                               //
  // @returns {Object} Object with token and tokenExpires keys.                                                    //
  methods.logoutOtherClients = function () {                                                                       // 552
    var self = this;                                                                                               // 553
    var user = accounts.users.findOne(self.userId, {                                                               // 554
      fields: {                                                                                                    // 555
        "services.resume.loginTokens": true                                                                        // 556
      }                                                                                                            //
    });                                                                                                            //
    if (user) {                                                                                                    // 559
      // Save the current tokens in the database to be deleted in                                                  //
      // CONNECTION_CLOSE_DELAY_MS ms. This gives other connections in the                                         //
      // caller's browser time to find the fresh token in localStorage. We save                                    //
      // the tokens in the database in case we crash before actually deleting                                      //
      // them.                                                                                                     //
      var tokens = user.services.resume.loginTokens;                                                               // 565
      var newToken = accounts._generateStampedLoginToken();                                                        // 566
      var userId = self.userId;                                                                                    // 567
      accounts.users.update(userId, {                                                                              // 568
        $set: {                                                                                                    // 569
          "services.resume.loginTokensToDelete": tokens,                                                           // 570
          "services.resume.haveLoginTokensToDelete": true                                                          // 571
        },                                                                                                         //
        $push: { "services.resume.loginTokens": accounts._hashStampedToken(newToken) }                             // 573
      });                                                                                                          //
      Meteor.setTimeout(function () {                                                                              // 575
        // The observe on Meteor.users will take care of closing the connections                                   //
        // associated with `tokens`.                                                                               //
        accounts._deleteSavedTokensForUser(userId, tokens);                                                        // 578
      }, accounts._noConnectionCloseDelayForTest ? 0 : CONNECTION_CLOSE_DELAY_MS);                                 //
      // We do not set the login token on this connection, but instead the                                         //
      // observe closes the connection and the client will reconnect with the                                      //
      // new token.                                                                                                //
      return {                                                                                                     // 584
        token: newToken.token,                                                                                     // 585
        tokenExpires: accounts._tokenExpiration(newToken.when)                                                     // 586
      };                                                                                                           //
    } else {                                                                                                       //
      throw new Meteor.Error("You are not logged in.");                                                            // 589
    }                                                                                                              //
  };                                                                                                               //
                                                                                                                   //
  // Generates a new login token with the same expiration as the                                                   //
  // connection's current token and saves it to the database. Associates                                           //
  // the connection with this new token and returns it. Throws an error                                            //
  // if called on a connection that isn't logged in.                                                               //
  //                                                                                                               //
  // @returns Object                                                                                               //
  //   If successful, returns { token: <new token>, id: <user id>,                                                 //
  //   tokenExpires: <expiration date> }.                                                                          //
  methods.getNewToken = function () {                                                                              // 601
    var self = this;                                                                                               // 602
    var user = accounts.users.findOne(self.userId, {                                                               // 603
      fields: { "services.resume.loginTokens": 1 }                                                                 // 604
    });                                                                                                            //
    if (!self.userId || !user) {                                                                                   // 606
      throw new Meteor.Error("You are not logged in.");                                                            // 607
    }                                                                                                              //
    // Be careful not to generate a new token that has a later                                                     //
    // expiration than the curren token. Otherwise, a bad guy with a                                               //
    // stolen token could use this method to stop his stolen token from                                            //
    // ever expiring.                                                                                              //
    var currentHashedToken = accounts._getLoginToken(self.connection.id);                                          // 613
    var currentStampedToken = _.find(user.services.resume.loginTokens, function (stampedToken) {                   // 614
      return stampedToken.hashedToken === currentHashedToken;                                                      // 617
    });                                                                                                            //
    if (!currentStampedToken) {                                                                                    // 620
      // safety belt: this should never happen                                                                     //
      throw new Meteor.Error("Invalid login token");                                                               // 621
    }                                                                                                              //
    var newStampedToken = accounts._generateStampedLoginToken();                                                   // 623
    newStampedToken.when = currentStampedToken.when;                                                               // 624
    accounts._insertLoginToken(self.userId, newStampedToken);                                                      // 625
    return accounts._loginUser(self, self.userId, newStampedToken);                                                // 626
  };                                                                                                               //
                                                                                                                   //
  // Removes all tokens except the token associated with the current                                               //
  // connection. Throws an error if the connection is not logged                                                   //
  // in. Returns nothing on success.                                                                               //
  methods.removeOtherTokens = function () {                                                                        // 632
    var self = this;                                                                                               // 633
    if (!self.userId) {                                                                                            // 634
      throw new Meteor.Error("You are not logged in.");                                                            // 635
    }                                                                                                              //
    var currentToken = accounts._getLoginToken(self.connection.id);                                                // 637
    accounts.users.update(self.userId, {                                                                           // 638
      $pull: {                                                                                                     // 639
        "services.resume.loginTokens": { hashedToken: { $ne: currentToken } }                                      // 640
      }                                                                                                            //
    });                                                                                                            //
  };                                                                                                               //
                                                                                                                   //
  // Allow a one-time configuration for a login service. Modifications                                             //
  // to this collection are also allowed in insecure mode.                                                         //
  methods.configureLoginService = function (options) {                                                             // 647
    check(options, Match.ObjectIncluding({ service: String }));                                                    // 648
    // Don't let random users configure a service we haven't added yet (so                                         //
    // that when we do later add it, it's set up with their configuration                                          //
    // instead of ours).                                                                                           //
    // XXX if service configuration is oauth-specific then this code should                                        //
    //     be in accounts-oauth; if it's not then the registry should be                                           //
    //     in this package                                                                                         //
    if (!(accounts.oauth && _.contains(accounts.oauth.serviceNames(), options.service))) {                         // 655
      throw new Meteor.Error(403, "Service unknown");                                                              // 657
    }                                                                                                              //
                                                                                                                   //
    var ServiceConfiguration = Package['service-configuration'].ServiceConfiguration;                              // 660
    if (ServiceConfiguration.configurations.findOne({ service: options.service })) throw new Meteor.Error(403, "Service " + options.service + " already configured");
                                                                                                                   //
    if (_.has(options, "secret") && usingOAuthEncryption()) options.secret = OAuthEncryption.seal(options.secret);
                                                                                                                   //
    ServiceConfiguration.configurations.insert(options);                                                           // 668
  };                                                                                                               //
                                                                                                                   //
  accounts._server.methods(methods);                                                                               // 671
};                                                                                                                 //
                                                                                                                   //
Ap._initAccountDataHooks = function () {                                                                           // 674
  var accounts = this;                                                                                             // 675
                                                                                                                   //
  accounts._server.onConnection(function (connection) {                                                            // 677
    accounts._accountData[connection.id] = {                                                                       // 678
      connection: connection                                                                                       // 679
    };                                                                                                             //
                                                                                                                   //
    connection.onClose(function () {                                                                               // 682
      accounts._removeTokenFromConnection(connection.id);                                                          // 683
      delete accounts._accountData[connection.id];                                                                 // 684
    });                                                                                                            //
  });                                                                                                              //
};                                                                                                                 //
                                                                                                                   //
Ap._initServerPublications = function () {                                                                         // 689
  var accounts = this;                                                                                             // 690
                                                                                                                   //
  // Publish all login service configuration fields other than secret.                                             //
  accounts._server.publish("meteor.loginServiceConfiguration", function () {                                       // 693
    var ServiceConfiguration = Package['service-configuration'].ServiceConfiguration;                              // 694
    return ServiceConfiguration.configurations.find({}, { fields: { secret: 0 } });                                // 696
  }, { is_auto: true }); // not techincally autopublish, but stops the warning.                                    //
                                                                                                                   //
  // Publish the current user's record to the client.                                                              //
  accounts._server.publish(null, function () {                                                                     // 700
    if (this.userId) {                                                                                             // 701
      return accounts.users.find({                                                                                 // 702
        _id: this.userId                                                                                           // 703
      }, {                                                                                                         //
        fields: {                                                                                                  // 705
          profile: 1,                                                                                              // 706
          username: 1,                                                                                             // 707
          emails: 1                                                                                                // 708
        }                                                                                                          //
      });                                                                                                          //
    } else {                                                                                                       //
      return null;                                                                                                 // 712
    }                                                                                                              //
  }, /*suppress autopublish warning*/{ is_auto: true });                                                           //
                                                                                                                   //
  // Use Meteor.startup to give other packages a chance to call                                                    //
  // addAutopublishFields.                                                                                         //
  Package.autopublish && Meteor.startup(function () {                                                              // 718
    // ['profile', 'username'] -> {profile: 1, username: 1}                                                        //
    var toFieldSelector = function (fields) {                                                                      // 720
      return _.object(_.map(fields, function (field) {                                                             // 721
        return [field, 1];                                                                                         // 722
      }));                                                                                                         //
    };                                                                                                             //
                                                                                                                   //
    accounts._server.publish(null, function () {                                                                   // 726
      if (this.userId) {                                                                                           // 727
        return accounts.users.find({                                                                               // 728
          _id: this.userId                                                                                         // 729
        }, {                                                                                                       //
          fields: toFieldSelector(accounts._autopublishFields.loggedInUser)                                        // 731
        });                                                                                                        //
      } else {                                                                                                     //
        return null;                                                                                               // 734
      }                                                                                                            //
    }, /*suppress autopublish warning*/{ is_auto: true });                                                         //
                                                                                                                   //
    // XXX this publish is neither dedup-able nor is it optimized by our special                                   //
    // treatment of queries on a specific _id. Therefore this will have O(n^2)                                     //
    // run-time performance every time a user document is changed (eg someone                                      //
    // logging in). If this is a problem, we can instead write a manual publish                                    //
    // function which filters out fields based on 'this.userId'.                                                   //
    accounts._server.publish(null, function () {                                                                   // 743
      var selector = this.userId ? {                                                                               // 744
        _id: { $ne: this.userId }                                                                                  // 745
      } : {};                                                                                                      //
                                                                                                                   //
      return accounts.users.find(selector, {                                                                       // 748
        fields: toFieldSelector(accounts._autopublishFields.otherUsers)                                            // 749
      });                                                                                                          //
    }, /*suppress autopublish warning*/{ is_auto: true });                                                         //
  });                                                                                                              //
};                                                                                                                 //
                                                                                                                   //
// Add to the list of fields or subfields to be automatically                                                      //
// published if autopublish is on. Must be called from top-level                                                   //
// code (ie, before Meteor.startup hooks run).                                                                     //
//                                                                                                                 //
// @param opts {Object} with:                                                                                      //
//   - forLoggedInUser {Array} Array of fields published to the logged-in user                                     //
//   - forOtherUsers {Array} Array of fields published to users that aren't logged in                              //
Ap.addAutopublishFields = function (opts) {                                                                        // 762
  this._autopublishFields.loggedInUser.push.apply(this._autopublishFields.loggedInUser, opts.forLoggedInUser);     // 763
  this._autopublishFields.otherUsers.push.apply(this._autopublishFields.otherUsers, opts.forOtherUsers);           // 765
};                                                                                                                 //
                                                                                                                   //
///                                                                                                                //
/// ACCOUNT DATA                                                                                                   //
///                                                                                                                //
                                                                                                                   //
// HACK: This is used by 'meteor-accounts' to get the loginToken for a                                             //
// connection. Maybe there should be a public way to do that.                                                      //
Ap._getAccountData = function (connectionId, field) {                                                              // 775
  var data = this._accountData[connectionId];                                                                      // 776
  return data && data[field];                                                                                      // 777
};                                                                                                                 //
                                                                                                                   //
Ap._setAccountData = function (connectionId, field, value) {                                                       // 780
  var data = this._accountData[connectionId];                                                                      // 781
                                                                                                                   //
  // safety belt. shouldn't happen. accountData is set in onConnection,                                            //
  // we don't have a connectionId until it is set.                                                                 //
  if (!data) return;                                                                                               // 785
                                                                                                                   //
  if (value === undefined) delete data[field];else data[field] = value;                                            // 788
};                                                                                                                 //
                                                                                                                   //
///                                                                                                                //
/// RECONNECT TOKENS                                                                                               //
///                                                                                                                //
/// support reconnecting using a meteor login token                                                                //
                                                                                                                   //
Ap._hashLoginToken = function (loginToken) {                                                                       // 800
  var hash = crypto.createHash('sha256');                                                                          // 801
  hash.update(loginToken);                                                                                         // 802
  return hash.digest('base64');                                                                                    // 803
};                                                                                                                 //
                                                                                                                   //
// {token, when} => {hashedToken, when}                                                                            //
Ap._hashStampedToken = function (stampedToken) {                                                                   // 808
  return _.extend(_.omit(stampedToken, 'token'), {                                                                 // 809
    hashedToken: this._hashLoginToken(stampedToken.token)                                                          // 810
  });                                                                                                              //
};                                                                                                                 //
                                                                                                                   //
// Using $addToSet avoids getting an index error if another client                                                 //
// logging in simultaneously has already inserted the new hashed                                                   //
// token.                                                                                                          //
Ap._insertHashedLoginToken = function (userId, hashedToken, query) {                                               // 818
  query = query ? _.clone(query) : {};                                                                             // 819
  query._id = userId;                                                                                              // 820
  this.users.update(query, {                                                                                       // 821
    $addToSet: {                                                                                                   // 822
      "services.resume.loginTokens": hashedToken                                                                   // 823
    }                                                                                                              //
  });                                                                                                              //
};                                                                                                                 //
                                                                                                                   //
// Exported for tests.                                                                                             //
Ap._insertLoginToken = function (userId, stampedToken, query) {                                                    // 830
  this._insertHashedLoginToken(userId, this._hashStampedToken(stampedToken), query);                               // 831
};                                                                                                                 //
                                                                                                                   //
Ap._clearAllLoginTokens = function (userId) {                                                                      // 839
  this.users.update(userId, {                                                                                      // 840
    $set: {                                                                                                        // 841
      'services.resume.loginTokens': []                                                                            // 842
    }                                                                                                              //
  });                                                                                                              //
};                                                                                                                 //
                                                                                                                   //
// test hook                                                                                                       //
Ap._getUserObserve = function (connectionId) {                                                                     // 848
  return this._userObservesForConnections[connectionId];                                                           // 849
};                                                                                                                 //
                                                                                                                   //
// Clean up this connection's association with the token: that is, stop                                            //
// the observe that we started when we associated the connection with                                              //
// this token.                                                                                                     //
Ap._removeTokenFromConnection = function (connectionId) {                                                          // 855
  if (_.has(this._userObservesForConnections, connectionId)) {                                                     // 856
    var observe = this._userObservesForConnections[connectionId];                                                  // 857
    if (typeof observe === 'number') {                                                                             // 858
      // We're in the process of setting up an observe for this connection. We                                     //
      // can't clean up that observe yet, but if we delete the placeholder for                                     //
      // this connection, then the observe will get cleaned up as soon as it has                                   //
      // been set up.                                                                                              //
      delete this._userObservesForConnections[connectionId];                                                       // 863
    } else {                                                                                                       //
      delete this._userObservesForConnections[connectionId];                                                       // 865
      observe.stop();                                                                                              // 866
    }                                                                                                              //
  }                                                                                                                //
};                                                                                                                 //
                                                                                                                   //
Ap._getLoginToken = function (connectionId) {                                                                      // 871
  return this._getAccountData(connectionId, 'loginToken');                                                         // 872
};                                                                                                                 //
                                                                                                                   //
// newToken is a hashed token.                                                                                     //
Ap._setLoginToken = function (userId, connection, newToken) {                                                      // 876
  var self = this;                                                                                                 // 877
                                                                                                                   //
  self._removeTokenFromConnection(connection.id);                                                                  // 879
  self._setAccountData(connection.id, 'loginToken', newToken);                                                     // 880
                                                                                                                   //
  if (newToken) {                                                                                                  // 882
    // Set up an observe for this token. If the token goes away, we need                                           //
    // to close the connection.  We defer the observe because there's                                              //
    // no need for it to be on the critical path for login; we just need                                           //
    // to ensure that the connection will get closed at some point if                                              //
    // the token gets deleted.                                                                                     //
    //                                                                                                             //
    // Initially, we set the observe for this connection to a number; this                                         //
    // signifies to other code (which might run while we yield) that we are in                                     //
    // the process of setting up an observe for this connection. Once the                                          //
    // observe is ready to go, we replace the number with the real observe                                         //
    // handle (unless the placeholder has been deleted or replaced by a                                            //
    // different placehold number, signifying that the connection was closed                                       //
    // already -- in this case we just clean up the observe that we started).                                      //
    var myObserveNumber = ++self._nextUserObserveNumber;                                                           // 896
    self._userObservesForConnections[connection.id] = myObserveNumber;                                             // 897
    Meteor.defer(function () {                                                                                     // 898
      // If something else happened on this connection in the meantime (it got                                     //
      // closed, or another call to _setLoginToken happened), just do                                              //
      // nothing. We don't need to start an observe for an old connection or old                                   //
      // token.                                                                                                    //
      if (self._userObservesForConnections[connection.id] !== myObserveNumber) {                                   // 903
        return;                                                                                                    // 904
      }                                                                                                            //
                                                                                                                   //
      var foundMatchingUser;                                                                                       // 907
      // Because we upgrade unhashed login tokens to hashed tokens at                                              //
      // login time, sessions will only be logged in with a hashed                                                 //
      // token. Thus we only need to observe hashed tokens here.                                                   //
      var observe = self.users.find({                                                                              // 911
        _id: userId,                                                                                               // 912
        'services.resume.loginTokens.hashedToken': newToken                                                        // 913
      }, { fields: { _id: 1 } }).observeChanges({                                                                  //
        added: function () {                                                                                       // 915
          foundMatchingUser = true;                                                                                // 916
        },                                                                                                         //
        removed: function () {                                                                                     // 918
          connection.close();                                                                                      // 919
          // The onClose callback for the connection takes care of                                                 //
          // cleaning up the observe handle and any other state we have                                            //
          // lying around.                                                                                         //
        }                                                                                                          //
      });                                                                                                          //
                                                                                                                   //
      // If the user ran another login or logout command we were waiting for the                                   //
      // defer or added to fire (ie, another call to _setLoginToken occurred),                                     //
      // then we let the later one win (start an observe, etc) and just stop our                                   //
      // observe now.                                                                                              //
      //                                                                                                           //
      // Similarly, if the connection was already closed, then the onClose                                         //
      // callback would have called _removeTokenFromConnection and there won't                                     //
      // be an entry in _userObservesForConnections. We can stop the observe.                                      //
      if (self._userObservesForConnections[connection.id] !== myObserveNumber) {                                   // 934
        observe.stop();                                                                                            // 935
        return;                                                                                                    // 936
      }                                                                                                            //
                                                                                                                   //
      self._userObservesForConnections[connection.id] = observe;                                                   // 939
                                                                                                                   //
      if (!foundMatchingUser) {                                                                                    // 941
        // We've set up an observe on the user associated with `newToken`,                                         //
        // so if the new token is removed from the database, we'll close                                           //
        // the connection. But the token might have already been deleted                                           //
        // before we set up the observe, which wouldn't have closed the                                            //
        // connection because the observe wasn't running yet.                                                      //
        connection.close();                                                                                        // 947
      }                                                                                                            //
    });                                                                                                            //
  }                                                                                                                //
};                                                                                                                 //
                                                                                                                   //
function setupDefaultLoginHandlers(accounts) {                                                                     // 953
  accounts.registerLoginHandler("resume", function (options) {                                                     // 954
    return defaultResumeLoginHandler.call(this, accounts, options);                                                // 955
  });                                                                                                              //
}                                                                                                                  //
                                                                                                                   //
// Login handler for resume tokens.                                                                                //
function defaultResumeLoginHandler(accounts, options) {                                                            // 960
  if (!options.resume) return undefined;                                                                           // 961
                                                                                                                   //
  check(options.resume, String);                                                                                   // 964
                                                                                                                   //
  var hashedToken = accounts._hashLoginToken(options.resume);                                                      // 966
                                                                                                                   //
  // First look for just the new-style hashed login token, to avoid                                                //
  // sending the unhashed token to the database in a query if we don't                                             //
  // need to.                                                                                                      //
  var user = accounts.users.findOne({ "services.resume.loginTokens.hashedToken": hashedToken });                   // 971
                                                                                                                   //
  if (!user) {                                                                                                     // 974
    // If we didn't find the hashed login token, try also looking for                                              //
    // the old-style unhashed token.  But we need to look for either                                               //
    // the old-style token OR the new-style token, because another                                                 //
    // client connection logging in simultaneously might have already                                              //
    // converted the token.                                                                                        //
    user = accounts.users.findOne({                                                                                // 980
      $or: [{ "services.resume.loginTokens.hashedToken": hashedToken }, { "services.resume.loginTokens.token": options.resume }]
    });                                                                                                            //
  }                                                                                                                //
                                                                                                                   //
  if (!user) return {                                                                                              // 988
    error: new Meteor.Error(403, "You've been logged out by the server. Please log in again.")                     // 990
  };                                                                                                               //
                                                                                                                   //
  // Find the token, which will either be an object with fields                                                    //
  // {hashedToken, when} for a hashed token or {token, when} for an                                                //
  // unhashed token.                                                                                               //
  var oldUnhashedStyleToken;                                                                                       // 996
  var token = _.find(user.services.resume.loginTokens, function (token) {                                          // 997
    return token.hashedToken === hashedToken;                                                                      // 998
  });                                                                                                              //
  if (token) {                                                                                                     // 1000
    oldUnhashedStyleToken = false;                                                                                 // 1001
  } else {                                                                                                         //
    token = _.find(user.services.resume.loginTokens, function (token) {                                            // 1003
      return token.token === options.resume;                                                                       // 1004
    });                                                                                                            //
    oldUnhashedStyleToken = true;                                                                                  // 1006
  }                                                                                                                //
                                                                                                                   //
  var tokenExpires = accounts._tokenExpiration(token.when);                                                        // 1009
  if (new Date() >= tokenExpires) return {                                                                         // 1010
    userId: user._id,                                                                                              // 1012
    error: new Meteor.Error(403, "Your session has expired. Please log in again.")                                 // 1013
  };                                                                                                               //
                                                                                                                   //
  // Update to a hashed token when an unhashed token is encountered.                                               //
  if (oldUnhashedStyleToken) {                                                                                     // 1017
    // Only add the new hashed token if the old unhashed token still                                               //
    // exists (this avoids resurrecting the token if it was deleted                                                //
    // after we read it).  Using $addToSet avoids getting an index                                                 //
    // error if another client logging in simultaneously has already                                               //
    // inserted the new hashed token.                                                                              //
    accounts.users.update({                                                                                        // 1023
      _id: user._id,                                                                                               // 1025
      "services.resume.loginTokens.token": options.resume                                                          // 1026
    }, { $addToSet: {                                                                                              //
        "services.resume.loginTokens": {                                                                           // 1029
          "hashedToken": hashedToken,                                                                              // 1030
          "when": token.when                                                                                       // 1031
        }                                                                                                          //
      } });                                                                                                        //
                                                                                                                   //
    // Remove the old token *after* adding the new, since otherwise                                                //
    // another client trying to login between our removing the old and                                             //
    // adding the new wouldn't find a token to login with.                                                         //
    accounts.users.update(user._id, {                                                                              // 1039
      $pull: {                                                                                                     // 1040
        "services.resume.loginTokens": { "token": options.resume }                                                 // 1041
      }                                                                                                            //
    });                                                                                                            //
  }                                                                                                                //
                                                                                                                   //
  return {                                                                                                         // 1046
    userId: user._id,                                                                                              // 1047
    stampedLoginToken: {                                                                                           // 1048
      token: options.resume,                                                                                       // 1049
      when: token.when                                                                                             // 1050
    }                                                                                                              //
  };                                                                                                               //
}                                                                                                                  //
                                                                                                                   //
// (Also used by Meteor Accounts server and tests).                                                                //
//                                                                                                                 //
Ap._generateStampedLoginToken = function () {                                                                      // 1057
  return {                                                                                                         // 1058
    token: Random.secret(),                                                                                        // 1059
    when: new Date()                                                                                               // 1060
  };                                                                                                               //
};                                                                                                                 //
                                                                                                                   //
///                                                                                                                //
/// TOKEN EXPIRATION                                                                                               //
///                                                                                                                //
                                                                                                                   //
// Deletes expired tokens from the database and closes all open connections                                        //
// associated with these tokens.                                                                                   //
//                                                                                                                 //
// Exported for tests. Also, the arguments are only used by                                                        //
// tests. oldestValidDate is simulate expiring tokens without waiting                                              //
// for them to actually expire. userId is used by tests to only expire                                             //
// tokens for the test user.                                                                                       //
Ap._expireTokens = function (oldestValidDate, userId) {                                                            // 1075
  var tokenLifetimeMs = this._getTokenLifetimeMs();                                                                // 1076
                                                                                                                   //
  // when calling from a test with extra arguments, you must specify both!                                         //
  if (oldestValidDate && !userId || !oldestValidDate && userId) {                                                  // 1079
    throw new Error("Bad test. Must specify both oldestValidDate and userId.");                                    // 1080
  }                                                                                                                //
                                                                                                                   //
  oldestValidDate = oldestValidDate || new Date(new Date() - tokenLifetimeMs);                                     // 1083
  var userFilter = userId ? { _id: userId } : {};                                                                  // 1085
                                                                                                                   //
  // Backwards compatible with older versions of meteor that stored login token                                    //
  // timestamps as numbers.                                                                                        //
  this.users.update(_.extend(userFilter, {                                                                         // 1090
    $or: [{ "services.resume.loginTokens.when": { $lt: oldestValidDate } }, { "services.resume.loginTokens.when": { $lt: +oldestValidDate } }]
  }), {                                                                                                            //
    $pull: {                                                                                                       // 1096
      "services.resume.loginTokens": {                                                                             // 1097
        $or: [{ when: { $lt: oldestValidDate } }, { when: { $lt: +oldestValidDate } }]                             // 1098
      }                                                                                                            //
    }                                                                                                              //
  }, { multi: true });                                                                                             //
  // The observe on Meteor.users will take care of closing connections for                                         //
  // expired tokens.                                                                                               //
};                                                                                                                 //
                                                                                                                   //
// @override from accounts_common.js                                                                               //
Ap.config = function (options) {                                                                                   // 1110
  // Call the overridden implementation of the method.                                                             //
  var superResult = AccountsCommon.prototype.config.apply(this, arguments);                                        // 1112
                                                                                                                   //
  // If the user set loginExpirationInDays to null, then we need to clear the                                      //
  // timer that periodically expires tokens.                                                                       //
  if (_.has(this._options, "loginExpirationInDays") && this._options.loginExpirationInDays === null && this.expireTokenInterval) {
    Meteor.clearInterval(this.expireTokenInterval);                                                                // 1119
    this.expireTokenInterval = null;                                                                               // 1120
  }                                                                                                                //
                                                                                                                   //
  return superResult;                                                                                              // 1123
};                                                                                                                 //
                                                                                                                   //
function setExpireTokensInterval(accounts) {                                                                       // 1126
  accounts.expireTokenInterval = Meteor.setInterval(function () {                                                  // 1127
    accounts._expireTokens();                                                                                      // 1128
  }, EXPIRE_TOKENS_INTERVAL_MS);                                                                                   //
}                                                                                                                  //
                                                                                                                   //
///                                                                                                                //
/// OAuth Encryption Support                                                                                       //
///                                                                                                                //
                                                                                                                   //
var OAuthEncryption = Package["oauth-encryption"] && Package["oauth-encryption"].OAuthEncryption;                  // 1137
                                                                                                                   //
function usingOAuthEncryption() {                                                                                  // 1141
  return OAuthEncryption && OAuthEncryption.keyIsLoaded();                                                         // 1142
}                                                                                                                  //
                                                                                                                   //
// OAuth service data is temporarily stored in the pending credentials                                             //
// collection during the oauth authentication process.  Sensitive data                                             //
// such as access tokens are encrypted without the user id because                                                 //
// we don't know the user id yet.  We re-encrypt these fields with the                                             //
// user id included when storing the service data permanently in                                                   //
// the users collection.                                                                                           //
//                                                                                                                 //
function pinEncryptedFieldsToUser(serviceData, userId) {                                                           // 1153
  _.each(_.keys(serviceData), function (key) {                                                                     // 1154
    var value = serviceData[key];                                                                                  // 1155
    if (OAuthEncryption && OAuthEncryption.isSealed(value)) value = OAuthEncryption.seal(OAuthEncryption.open(value), userId);
    serviceData[key] = value;                                                                                      // 1158
  });                                                                                                              //
}                                                                                                                  //
                                                                                                                   //
// Encrypt unencrypted login service secrets when oauth-encryption is                                              //
// added.                                                                                                          //
//                                                                                                                 //
// XXX For the oauthSecretKey to be available here at startup, the                                                 //
// developer must call Accounts.config({oauthSecretKey: ...}) at load                                              //
// time, instead of in a Meteor.startup block, because the startup                                                 //
// block in the app code will run after this accounts-base startup                                                 //
// block.  Perhaps we need a post-startup callback?                                                                //
                                                                                                                   //
Meteor.startup(function () {                                                                                       // 1172
  if (!usingOAuthEncryption()) {                                                                                   // 1173
    return;                                                                                                        // 1174
  }                                                                                                                //
                                                                                                                   //
  var ServiceConfiguration = Package['service-configuration'].ServiceConfiguration;                                // 1177
                                                                                                                   //
  ServiceConfiguration.configurations.find({                                                                       // 1180
    $and: [{                                                                                                       // 1181
      secret: { $exists: true }                                                                                    // 1182
    }, {                                                                                                           //
      "secret.algorithm": { $exists: false }                                                                       // 1184
    }]                                                                                                             //
  }).forEach(function (config) {                                                                                   //
    ServiceConfiguration.configurations.update(config._id, {                                                       // 1187
      $set: {                                                                                                      // 1188
        secret: OAuthEncryption.seal(config.secret)                                                                // 1189
      }                                                                                                            //
    });                                                                                                            //
  });                                                                                                              //
});                                                                                                                //
                                                                                                                   //
// XXX see comment on Accounts.createUser in passwords_server about adding a                                       //
// second "server options" argument.                                                                               //
function defaultCreateUserHook(options, user) {                                                                    // 1197
  if (options.profile) user.profile = options.profile;                                                             // 1198
  return user;                                                                                                     // 1200
}                                                                                                                  //
                                                                                                                   //
// Called by accounts-password                                                                                     //
Ap.insertUserDoc = function (options, user) {                                                                      // 1204
  // - clone user document, to protect from modification                                                           //
  // - add createdAt timestamp                                                                                     //
  // - prepare an _id, so that you can modify other collections (eg                                                //
  // create a first task for every new user)                                                                       //
  //                                                                                                               //
  // XXX If the onCreateUser or validateNewUser hooks fail, we might                                               //
  // end up having modified some other collection                                                                  //
  // inappropriately. The solution is probably to have onCreateUser                                                //
  // accept two callbacks - one that gets called before inserting                                                  //
  // the user document (in which you can modify its contents), and                                                 //
  // one that gets called after (in which you should change other                                                  //
  // collections)                                                                                                  //
  user = _.extend({                                                                                                // 1217
    createdAt: new Date(),                                                                                         // 1218
    _id: Random.id()                                                                                               // 1219
  }, user);                                                                                                        //
                                                                                                                   //
  if (user.services) {                                                                                             // 1222
    _.each(user.services, function (serviceData) {                                                                 // 1223
      pinEncryptedFieldsToUser(serviceData, user._id);                                                             // 1224
    });                                                                                                            //
  }                                                                                                                //
                                                                                                                   //
  var fullUser;                                                                                                    // 1228
  if (this._onCreateUserHook) {                                                                                    // 1229
    fullUser = this._onCreateUserHook(options, user);                                                              // 1230
                                                                                                                   //
    // This is *not* part of the API. We need this because we can't isolate                                        //
    // the global server environment between tests, meaning we can't test                                          //
    // both having a create user hook set and not having one set.                                                  //
    if (fullUser === 'TEST DEFAULT HOOK') fullUser = defaultCreateUserHook(options, user);                         // 1235
  } else {                                                                                                         //
    fullUser = defaultCreateUserHook(options, user);                                                               // 1238
  }                                                                                                                //
                                                                                                                   //
  _.each(this._validateNewUserHooks, function (hook) {                                                             // 1241
    if (!hook(fullUser)) throw new Meteor.Error(403, "User validation failed");                                    // 1242
  });                                                                                                              //
                                                                                                                   //
  var userId;                                                                                                      // 1246
  try {                                                                                                            // 1247
    userId = this.users.insert(fullUser);                                                                          // 1248
  } catch (e) {                                                                                                    //
    // XXX string parsing sucks, maybe                                                                             //
    // https://jira.mongodb.org/browse/SERVER-3069 will get fixed one day                                          //
    if (e.name !== 'MongoError') throw e;                                                                          // 1252
    if (e.code !== 11000) throw e;                                                                                 // 1253
    if (e.err.indexOf('emails.address') !== -1) throw new Meteor.Error(403, "Email already exists.");              // 1254
    if (e.err.indexOf('username') !== -1) throw new Meteor.Error(403, "Username already exists.");                 // 1256
    // XXX better error reporting for services.facebook.id duplicate, etc                                          //
    throw e;                                                                                                       // 1259
  }                                                                                                                //
  return userId;                                                                                                   // 1261
};                                                                                                                 //
                                                                                                                   //
// Helper function: returns false if email does not match company domain from                                      //
// the configuration.                                                                                              //
Ap._testEmailDomain = function (email) {                                                                           // 1266
  var domain = this._options.restrictCreationByEmailDomain;                                                        // 1267
  return !domain || _.isFunction(domain) && domain(email) || _.isString(domain) && new RegExp('@' + Meteor._escapeRegExp(domain) + '$', 'i').test(email);
};                                                                                                                 //
                                                                                                                   //
// Validate new user's email or Google/Facebook/GitHub account's email                                             //
function defaultValidateNewUserHook(user) {                                                                        // 1275
  var self = this;                                                                                                 // 1276
  var domain = self._options.restrictCreationByEmailDomain;                                                        // 1277
  if (!domain) return true;                                                                                        // 1278
                                                                                                                   //
  var emailIsGood = false;                                                                                         // 1281
  if (!_.isEmpty(user.emails)) {                                                                                   // 1282
    emailIsGood = _.any(user.emails, function (email) {                                                            // 1283
      return self._testEmailDomain(email.address);                                                                 // 1284
    });                                                                                                            //
  } else if (!_.isEmpty(user.services)) {                                                                          //
    // Find any email of any service and check it                                                                  //
    emailIsGood = _.any(user.services, function (service) {                                                        // 1288
      return service.email && self._testEmailDomain(service.email);                                                // 1289
    });                                                                                                            //
  }                                                                                                                //
                                                                                                                   //
  if (emailIsGood) return true;                                                                                    // 1293
                                                                                                                   //
  if (_.isString(domain)) throw new Meteor.Error(403, "@" + domain + " email required");else throw new Meteor.Error(403, "Email doesn't match the criteria.");
}                                                                                                                  //
                                                                                                                   //
///                                                                                                                //
/// MANAGING USER OBJECTS                                                                                          //
///                                                                                                                //
                                                                                                                   //
// Updates or creates a user after we authenticate with a 3rd party.                                               //
//                                                                                                                 //
// @param serviceName {String} Service name (eg, twitter).                                                         //
// @param serviceData {Object} Data to store in the user's record                                                  //
//        under services[serviceName]. Must include an "id" field                                                  //
//        which is a unique identifier for the user in the service.                                                //
// @param options {Object, optional} Other options to pass to insertUserDoc                                        //
//        (eg, profile)                                                                                            //
// @returns {Object} Object with token and id keys, like the result                                                //
//        of the "login" method.                                                                                   //
//                                                                                                                 //
Ap.updateOrCreateUserFromExternalService = function (serviceName, serviceData, options) {                          // 1317
  options = _.clone(options || {});                                                                                // 1322
                                                                                                                   //
  if (serviceName === "password" || serviceName === "resume") throw new Error("Can't use updateOrCreateUserFromExternalService with internal service " + serviceName);
  if (!_.has(serviceData, 'id')) throw new Error("Service data for service " + serviceName + " must include id");  // 1328
                                                                                                                   //
  // Look for a user with the appropriate service user id.                                                         //
  var selector = {};                                                                                               // 1333
  var serviceIdKey = "services." + serviceName + ".id";                                                            // 1334
                                                                                                                   //
  // XXX Temporary special case for Twitter. (Issue #629)                                                          //
  //   The serviceData.id will be a string representation of an integer.                                           //
  //   We want it to match either a stored string or int representation.                                           //
  //   This is to cater to earlier versions of Meteor storing twitter                                              //
  //   user IDs in number form, and recent versions storing them as strings.                                       //
  //   This can be removed once migration technology is in place, and twitter                                      //
  //   users stored with integer IDs have been migrated to string IDs.                                             //
  if (serviceName === "twitter" && !isNaN(serviceData.id)) {                                                       // 1343
    selector["$or"] = [{}, {}];                                                                                    // 1344
    selector["$or"][0][serviceIdKey] = serviceData.id;                                                             // 1345
    selector["$or"][1][serviceIdKey] = parseInt(serviceData.id, 10);                                               // 1346
  } else {                                                                                                         //
    selector[serviceIdKey] = serviceData.id;                                                                       // 1348
  }                                                                                                                //
                                                                                                                   //
  var user = this.users.findOne(selector);                                                                         // 1351
                                                                                                                   //
  if (user) {                                                                                                      // 1353
    pinEncryptedFieldsToUser(serviceData, user._id);                                                               // 1354
                                                                                                                   //
    // We *don't* process options (eg, profile) for update, but we do replace                                      //
    // the serviceData (eg, so that we keep an unexpired access token and                                          //
    // don't cache old email addresses in serviceData.email).                                                      //
    // XXX provide an onUpdateUser hook which would let apps update                                                //
    //     the profile too                                                                                         //
    var setAttrs = {};                                                                                             // 1361
    _.each(serviceData, function (value, key) {                                                                    // 1362
      setAttrs["services." + serviceName + "." + key] = value;                                                     // 1363
    });                                                                                                            //
                                                                                                                   //
    // XXX Maybe we should re-use the selector above and notice if the update                                      //
    //     touches nothing?                                                                                        //
    this.users.update(user._id, {                                                                                  // 1368
      $set: setAttrs                                                                                               // 1369
    });                                                                                                            //
                                                                                                                   //
    return {                                                                                                       // 1372
      type: serviceName,                                                                                           // 1373
      userId: user._id                                                                                             // 1374
    };                                                                                                             //
  } else {                                                                                                         //
    // Create a new user with the service data. Pass other options through to                                      //
    // insertUserDoc.                                                                                              //
    user = { services: {} };                                                                                       // 1380
    user.services[serviceName] = serviceData;                                                                      // 1381
    return {                                                                                                       // 1382
      type: serviceName,                                                                                           // 1383
      userId: this.insertUserDoc(options, user)                                                                    // 1384
    };                                                                                                             //
  }                                                                                                                //
};                                                                                                                 //
                                                                                                                   //
function setupUsersCollection(users) {                                                                             // 1389
  ///                                                                                                              //
  /// RESTRICTING WRITES TO USER OBJECTS                                                                           //
  ///                                                                                                              //
  users.allow({                                                                                                    // 1393
    // clients can modify the profile field of their own document, and                                             //
    // nothing else.                                                                                               //
    update: function (userId, user, fields, modifier) {                                                            // 1396
      // make sure it is our record                                                                                //
      if (user._id !== userId) return false;                                                                       // 1398
                                                                                                                   //
      // user can only modify the 'profile' field. sets to multiple                                                //
      // sub-keys (eg profile.foo and profile.bar) are merged into entry                                           //
      // in the fields list.                                                                                       //
      if (fields.length !== 1 || fields[0] !== 'profile') return false;                                            // 1404
                                                                                                                   //
      return true;                                                                                                 // 1407
    },                                                                                                             //
    fetch: ['_id'] // we only look at _id.                                                                         // 1409
  });                                                                                                              //
                                                                                                                   //
  /// DEFAULT INDEXES ON USERS                                                                                     //
  users._ensureIndex('username', { unique: 1, sparse: 1 });                                                        // 1413
  users._ensureIndex('emails.address', { unique: 1, sparse: 1 });                                                  // 1414
  users._ensureIndex('services.resume.loginTokens.hashedToken', { unique: 1, sparse: 1 });                         // 1415
  users._ensureIndex('services.resume.loginTokens.token', { unique: 1, sparse: 1 });                               // 1417
  // For taking care of logoutOtherClients calls that crashed before the                                           //
  // tokens were deleted.                                                                                          //
  users._ensureIndex('services.resume.haveLoginTokensToDelete', { sparse: 1 });                                    // 1421
  // For expiring login tokens                                                                                     //
  users._ensureIndex("services.resume.loginTokens.when", { sparse: 1 });                                           // 1424
}                                                                                                                  //
                                                                                                                   //
///                                                                                                                //
/// CLEAN UP FOR `logoutOtherClients`                                                                              //
///                                                                                                                //
                                                                                                                   //
Ap._deleteSavedTokensForUser = function (userId, tokensToDelete) {                                                 // 1431
  if (tokensToDelete) {                                                                                            // 1432
    this.users.update(userId, {                                                                                    // 1433
      $unset: {                                                                                                    // 1434
        "services.resume.haveLoginTokensToDelete": 1,                                                              // 1435
        "services.resume.loginTokensToDelete": 1                                                                   // 1436
      },                                                                                                           //
      $pullAll: {                                                                                                  // 1438
        "services.resume.loginTokens": tokensToDelete                                                              // 1439
      }                                                                                                            //
    });                                                                                                            //
  }                                                                                                                //
};                                                                                                                 //
                                                                                                                   //
Ap._deleteSavedTokensForAllUsersOnStartup = function () {                                                          // 1445
  var self = this;                                                                                                 // 1446
                                                                                                                   //
  // If we find users who have saved tokens to delete on startup, delete                                           //
  // them now. It's possible that the server could have crashed and come                                           //
  // back up before new tokens are found in localStorage, but this                                                 //
  // shouldn't happen very often. We shouldn't put a delay here because                                            //
  // that would give a lot of power to an attacker with a stolen login                                             //
  // token and the ability to crash the server.                                                                    //
  Meteor.startup(function () {                                                                                     // 1454
    self.users.find({                                                                                              // 1455
      "services.resume.haveLoginTokensToDelete": true                                                              // 1456
    }, {                                                                                                           //
      "services.resume.loginTokensToDelete": 1                                                                     // 1458
    }).forEach(function (user) {                                                                                   //
      self._deleteSavedTokensForUser(user._id, user.services.resume.loginTokensToDelete);                          // 1460
    });                                                                                                            //
  });                                                                                                              //
};                                                                                                                 //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function(){

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                 //
// packages/accounts-base/accounts_rate_limit.js                                                                   //
//                                                                                                                 //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                                   //
var Ap = AccountsCommon.prototype;                                                                                 // 1
var defaultRateLimiterRuleId;                                                                                      // 2
// Removes default rate limiting rule                                                                              //
Ap.removeDefaultRateLimit = function () {                                                                          // 4
  var resp = DDPRateLimiter.removeRule(defaultRateLimiterRuleId);                                                  // 5
  defaultRateLimiterRuleId = null;                                                                                 // 6
  return resp;                                                                                                     // 7
};                                                                                                                 //
                                                                                                                   //
// Add a default rule of limiting logins, creating new users and password reset                                    //
// to 5 times every 10 seconds per connection.                                                                     //
Ap.addDefaultRateLimit = function () {                                                                             // 12
  if (!defaultRateLimiterRuleId) {                                                                                 // 13
    defaultRateLimiterRuleId = DDPRateLimiter.addRule({                                                            // 14
      userId: null,                                                                                                // 15
      clientAddress: null,                                                                                         // 16
      type: 'method',                                                                                              // 17
      name: function (name) {                                                                                      // 18
        return _.contains(['login', 'createUser', 'resetPassword', 'forgotPassword'], name);                       // 19
      },                                                                                                           //
      connectionId: function (connectionId) {                                                                      // 22
        return true;                                                                                               // 23
      }                                                                                                            //
    }, 5, 10000);                                                                                                  //
  }                                                                                                                //
};                                                                                                                 //
                                                                                                                   //
Ap.addDefaultRateLimit();                                                                                          // 29
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function(){

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                 //
// packages/accounts-base/url_server.js                                                                            //
//                                                                                                                 //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                                   //
// XXX These should probably not actually be public?                                                               //
                                                                                                                   //
AccountsServer.prototype.urls = {                                                                                  // 3
  resetPassword: function (token) {                                                                                // 4
    return Meteor.absoluteUrl('#/reset-password/' + token);                                                        // 5
  },                                                                                                               //
                                                                                                                   //
  verifyEmail: function (token) {                                                                                  // 8
    return Meteor.absoluteUrl('#/verify-email/' + token);                                                          // 9
  },                                                                                                               //
                                                                                                                   //
  enrollAccount: function (token) {                                                                                // 12
    return Meteor.absoluteUrl('#/enroll-account/' + token);                                                        // 13
  }                                                                                                                //
};                                                                                                                 //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);






(function(){

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                 //
// packages/accounts-base/globals_server.js                                                                        //
//                                                                                                                 //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                                   //
/**                                                                                                                //
 * @namespace Accounts                                                                                             //
 * @summary The namespace for all server-side accounts-related methods.                                            //
 */                                                                                                                //
Accounts = new AccountsServer(Meteor.server);                                                                      // 5
                                                                                                                   //
// Users table. Don't use the normal autopublish, since we want to hide                                            //
// some fields. Code to autopublish this is in accounts_server.js.                                                 //
// XXX Allow users to configure this collection name.                                                              //
                                                                                                                   //
/**                                                                                                                //
 * @summary A [Mongo.Collection](#collections) containing user documents.                                          //
 * @locus Anywhere                                                                                                 //
 * @type {Mongo.Collection}                                                                                        //
 */                                                                                                                //
Meteor.users = Accounts.users;                                                                                     // 16
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

}).call(this);


/* Exports */
if (typeof Package === 'undefined') Package = {};
Package['accounts-base'] = {
  Accounts: Accounts,
  AccountsServer: AccountsServer,
  AccountsTest: AccountsTest
};

})();

//# sourceMappingURL=accounts-base.js.map
