# account-base package ## Files documented * accounts_common.js * accounts_rate_limit.js * globals_client.js * globals_server.js * package.js * url_client.js * url_server.js ## Collections * `users`. Indexes (other indexes are added by other packages, like `account-password`: * `('username', {unique: 1, sparse: 1});` * `('emails.address', {unique: 1, sparse: 1});` * `('services.resume.loginTokens.hashedToken', {unique: 1, sparse: 1});` * `('services.resume.loginTokens.token', {unique: 1, sparse: 1});` * `('services.resume.haveLoginTokensToDelete', { sparse: 1 });` : For taking care of logoutOtherClients calls that crashed before the tokens were deleted. * `"services.resume.loginTokens.when", { sparse: 1 });` : For expiring login tokens ## Constants * `DEFAULT_LOGIN_EXPIRATION_DAYS` = 90. Default login token lifetime. Used by `AccountsCommon_getTokenLifetimeMs()`. * `MIN_TOKEN_LIFETIME_CAP_SECS` = 3600. Maximum value of "soon". Used by `AccountsCommon._tokenExpiresSoon(when)`. * `EXPIRE_TOKENS_INTERVAL_MS` = 100000. Frequency of token expiration checks. Used by `setExpireTokensInterval(accounts)` in `accounts_server.js`. * `CONNECTION_CLOSE_DELAY_MS` = 10000. Logout delay for other clients. Used by `Meteor.logoutOtherClients()`, added from `accounts_server.js`. ## Classes ### `AccountsClient` * `url_client.js` additions: * `_attemptToMatchHash()` : Try to match the saved value of window.location.hash to one of the reserved hashes, to trigger an Accounts operation. On success, invokes passed handler which, when called from `_initUrlMatching()`, will always be `defaultSuccessHandler()`. * `_initUrlMatching()`: called by constructor. Inits extra data on instance and invokes `_attemptToMatchHash()` * `onResetPasswordLink()` Register a function to call when a reset password link is clicked in an email sent by on of the hash handlers. See [Accounts-onResetPasswordLink](http://localhost:3333/#Accounts-onResetPasswordLink) * `onEmailVerificationLink()` Register a function to call when an email verification link is clicked in an email sent by a hash handler. See [Accounts-onEmailVerificationLink](http://localhost:3333/#Accounts-onEmailVerificationLink) * `onEnrollmentLink()` Register a function to call when an account enrollment link is clicked in an email sent by a hash handler. See [Accounts-onEnrollmentLink](http://localhost:3333/#Accounts-onEnrollmentLink) * Globals read * `window.location.hash`. Reserved hashes: * `reset-password` * `verify-email` * `enroll-account` * `defaultSuccessHandler()` * `attemptToMatchHash()` ### `AccountsCommon` (`accounts_common.js`) Base class for `AccountsClient` / `AccountsServer`. * `constructor(options)`. * initializes `connection`, then `users`. * Options can contain: * `connection`, `ddpUrl` see `initConnection()` * `sendVerificationEmail`, `forbidClientAccountCreation`, `restrictCreationByEmailDomain`, `loginExpirationInDays`, and `oauthSecretKey` (side-effect, not saved). see `config(options)` * `addDefaultRateLimit()` : enable per-connection, per-method rate limiter for `login`, `createUser`, `resetPassword` `forgotPassword` to 5 calls every 10 seconds. Added from `accounts_rate_limits.js`. * `config(options)`. Set up config for the accounts system. Call this on both the client the server. * Checks and filters options, before saving them to `_options`. * Setting an unknown option throws * Setting an already set option throws * Options can contain: * `sendVerificationEmail` {Boolean}: Send email address verification emails to new users created from client signups. * `forbidClientAccountCreation` {Boolean} Do not allow clients to create accounts directly. [Security issue #828](https://github.com/meteor/meteor/issues/828) exists if this is not called on both client and server * `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). * `oauthSecretKey` When using the `oauth-encryption` package, the 16 byte key using to encrypt sensitive account credentials in the database, encoded in base64. * Warns if the `oauth-encryption` package is not present * Throws if used on client * Removed from saved config after passing if to the `oauth-encryption` package * `ConfigError`: legacy, initialized from `service-configuration` package during `Meteor.startup()`. * `connection`: the MongoDB connection. If set to null, the `users` collection will be local (avoid !) * `LoginCancelledError`: specific error class to use when a login sequence is cancelled * `loginServiceConfiguration`: legacy, initialized from `service-configuration` package during `Meteor.startup()`. * `removeDefaultRateLimit()` : disable the rate limiter for the methods below (from `accounts_rate_limits.js`). * `user()`: returns the currently logged-in user by finding it from Mongo based on the `userId()` value. Defaults to `null`. * `userId()`: `Error("userId method not implemented")` Basically an abstract method to be refined in child classes * `users`: the users collection * `onLogin(func)`: Register a callback to be called after a login attempt succeeds. * `onLoginFailure(func)`: Register a callback to be called after a login attempt fails. * `_getTokenLifetimeMs()`: get the remaining login token lifetime in msec. Taken from `loginExpirationInDays` if it exists. Defaults to `DEFAULT_LOGIN_EXPIRATION_DAYS` (= 90) days in msec. * `_initConnection(options)` - Options can contain * `connection`: the connection on which to load the `users` collection * `ddpUrl`: if connection is not set, connect to this URL * some non-portable, going-away, mechanism for OAuth * if none if available, `Meteor.connection` will be used as a default * `_onLoginHook()`. As per hook.js, Hook system is under development. Use `onLogin(func)` to make use of it. * `_onLoginFailureHook()`. As per hook.js, Hook system is under development. Use `onLoginFailure(func)` to make use of it. * `_options = {}` - used directly by packages like `accounts-password` and `accounts-ui-unstyled. * `_tokenExpiration(when)`: `when` is a token (timestamp, used to be any number in earlier versions). It is converted to Date, and added with `_getTokenLifetimeMs()` to return the expiration date for the `when`. * `_tokenExpiresSoon(when)`: `when` is a token (timestamp). True if it expires in less the smaller of `0.1 * _getTokenLifetimeMs()`and 1 hour. * **side-effect** in `accounts_rate_limits.js` : loading this file initializes the rate-limiter for `addDefaultRateLimit()` and `removeDefaultRateLimit()`. This is why the package has a dependency on `ddp-rate-limiter`. ### `AccountsServer extends AccountsCommon` (`accounts_server.js`) * `constructor(server)` * invokes the `AccountsCommon` constructor * initializes `_server` from the server argument if not empty, defaulting to `Meteor.server` otherwise * initializes methods using `_initServerMethods()` * initializes `_accountData` using `_initAccountDataHooks()` * if `autopublish` is present, mark user fields as published: * for everyone: `profile` and `username` * for current user only: `emails` * publishes account system collections using `_initServerPublications()` * sets up the `users` collection using `setupUsersCollection(users)`. * sets up the default login handler using `setupDefaultLoginHandlers()`. * sets up token expiration using `setExpireTokensInterval(accounts)`. * initializes `_validateLoginHook` * initializes `_validateNewUserHooks` to the single `defaultValidateNewUserHook()`. * deletes saved tokens for all users using `_deleteSavedTokensForAllUsersOnStartup()` * initializes `_skipCaseInsensitiveChecksForTest`: used by tests only. * `addAutopublishFields(opts)`: allow packages to declare extra fields when autopublish is active. * `destroyToken(userId, loginToken)`: Deletes the given loginToken from the database. For new-style hashed token, this will cause all connections associated with the token to be closed. * `onCreateUser(func)`: registers `func` as the single user creation hook allowed. * `registerLoginHandler(name, handler)`: 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. * `name` {String} Optional. The service name, used by default if a specific service name isn't returned in the result. * `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; or a login method result object as described on `_loginUser`. * `setExpireTokensInterval(accounts)`: starts a low-frequency (`EXPIRE_TOKENS_INTERVAL_MS` = 10 sec) task expiring tokens. * `setupDefaultLoginHandlers()`: registers `defaultResumeLoginHandler()` as a login handler called `resume` using `registerLoginHandler()`. * `setupUsersCollection(users)` : configures the `users` collection obtained from the parent constructor, by applying `users.allow` to limite update rights to the document for the current user, and ensuring multiple MongoDB indexes. * `userId()`: overrides the unimplemented version in `AccountsCommon`. This function only works if called inside a method, throws otherwise. * `validateLoginAttempt(func)`: registers `func` as a login attempt validation hook, returning an object with a `stop()` method to unregister it. * `validateNewUser(func)`: register `func` as a user account creation validation hook, not returning anything. * `_accountData`: see `setAccountData()`. * `_attemptLogin(methodInvocation, methodName, methodArgs, result)`: After a login method has completed, call the login hooks: validation (which can turn allowed into disallowed), and login or loginFailure 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. Use `_loginMethod()` instead. * `_deleteSavedTokensForAllUsersOnStartup()`: on `Meteor.startup()`, immediately clean discovered saved tokens which applied to the previous instance of the application. * `_failedLogin()`: invokes the `AccountsCommon.onLoginFailureHook` implementations. * `_getAccountData(connectionId, field)`: get the login token for a connection. Documented as a "**HACK**: This is used by 'meteor-accounts' to get the loginToken for a connection. Maybe there should be a public way to do that." * `_initAccountDataHooks()`: add a DDP `onConnect` callback which * stores the connection in `_accountData[connection.id]` * add a DDP `onClose` callback removing the current user token from the connection using `_removeTokenFromConnection(connection.id);` and removing the connection stored in `_accountData[connection.id]` on connection. * `_initServerMethods()`: adds the account-related methods defined by the class. See `_server._methodHandlers`. * `_initServerPublications()`: starts account-related publications: * `meteor.loginServiceConfiguration` from the (OAuth) service configuration. If encryption is used, the `secret` field is not published * `users` cursor publish: * no autopublish and logged: just fields `profile`, `username`, and `emails` for the current user if applicable, * no autopublish and not logger: `null` * autopublish and logged: all autopublished fields for current user, including those added by other packages using `addAutopublishFields(forLoggedInUser)` * autopublish and not logged: `_id` and `username` (if available) for all users, and fields added by other packages using `addAutopublishFields(forOtherUsers)` * `users` anonymous with more fields, * `_loginHandlers`: list of all registered handlers. Starts as `[]`. * `_loginMethod(methodInvocation, methodName, methodArgs, type, fn)` the safe version of `_attemptLogin()` is the one to use to login a user. * `_loginUser(methodInvocation, userId, stampedLoginToken)`: Log in a user on a connection after success. Returns `{ id: userId, token: stampedLoginToken.token, tokenExpires: self._tokenExpiration(stampedLoginToken.when) };`. Invoked from `_attemptLogin()`. * `_nextUserObserveNumber`: the next observer number for `_userObservesForConnections`. * `_reportLoginFailure(methodInvocation, methodName, methodArgs, result)`: 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. * `_runLoginHandlers(methodInvocation, options)`: 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. Works by invoking `tryLoginMethod()` on each handler until one returns something not undefined. Throws Error 400 on incorrect options for a handler, or a handler returning neither undefined nor a result object as described on `_loginUser`. * `_server`: a `Server` (from `ddp-server/livedata_server.js`) instance. Notable properties in this context: * `_methodHandlers`: the Meteor methods table * `login(options)`: runs `result = _runLoginHandlers(self, options); _attemptLogin(self, "login", arguments, result);`. The method ensures `options` is an object, but it is up to login handlers to check whatever field they look at in options: Meteor doesn't check them. * `logout()`: deletes the login token for the connection with `destroyToken` and clears the current user id using method `setUserId(null)`. * `logoutOtherClients()`: obsolete for compatibility with 0.7.2, use `getNewToken()` and `removeOtherTokens()` instead * `getNewToken()`: 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. * `removeOtherTokens()`: 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. * `configureLoginService(options)`: Allow a one-time configuration for a login service. Modifications to this collection are also allowed in insecure mode. Option keys: * `service`: String, required. The name of the service to configure. **Bug** : this method can only be used to configure OAuth login providers. There is a XXX in the code about this problem having to be fixed, either by moving use of `service`to the `accounts-oauth` package, or to move the list of services from `accounts-oauth` to `accounts-base` * `_setAccountData(connectionId, field, value)`: modifies or deletes data from a connection kept on `_accountData`. * `_successfulLogin()`: invokes the `AccountsCommon.onLoginHook` implementations. * `_userObservesForConnections`: 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). Numbers obtained from `_nextUserObserveNumber`. * `_validateLogin(connection, attempt)`: perform login validation using available implementations in `_validateLoginHook`. Note that all validators run even if one or more of them denies access or throws an error. The first reported error is the one the user receives by default, but later validators may override this behavior. Invoked from `_attemptLogin()`. * `_validateLoginHook`: holds a login validation hook. Doc in `hook.js` warns "This pattern is under development. Do not add more callsites using this package for now" * `_validateNewUserHooks`: holds an array of new user hooks. * note about methods. These 3 methods are public but marked (in 1.2.1) as likely not to remain so: * `resetPassword()` : generates a password reset link (from token) * `verifyEmail()`: generates an email verification link (from token) * `enrollAccount()`: generates an account enrollment link (from token) ### `AccountsTest` * methods * `attemptToMatchHash()` facade for `attemptToMatchHash()` function * Globals read * `Accounts` (see `globals_server.js`) ### Meteor * `userId`: a copy of the `Accounts.usedId()` method * `user()`: a copy of the `Accounts.user()` method ## Functions ### `accounts_server.js` * `defaultValidateNewUserHook()` a weak email validation method based on the domain name. See `AccountsServer._validateNewUserHooks`. * `cloneAttemptWithConnection(connection, attempt)` clone the attempt object, preserving the connection inside it instead of cloning it too * `tryLoginMethod(type, fn)`: 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. ### `url_client.js` * `defaultSuccessHandler()` : suspends autologin, invokes other handles for the same hash, passing them a closure capable of enabling autologin. ## Dependencies / Exports (`package.js` _et al._) ### Exports | Symbol | Client | Server | Test | |------------------|:------:|:------:|:----:| | Accounts | O | O | O | | AccountsClient | O | | | | AccountsServer | | O | | | AccountsTest | | | O | ### Dependencies | Package | Client | Server | Specifics | |-----------------------|:------:|:-------:|------------| | underscore | O | O | | ecmascript | O | O | | ddp-rate-limiter | O | O | | localstorage | O | | | tracker | O | | | check | | O | | random | O | O | | ejson | | O | | callback-hook | O | O | | service-configuration | O | O | unordered (needs Accounts.connection) | ddp | O | O | | mongo | O | O | expected abstraction in the future | blaze | O | | weak: define {{currentUser}} | autopublish | | O | weak: publish extra users fields | oauth-encryption | | O | weak | NPM crypto | | O | in accounts_server.js | ## Objects (`globals_(client|server).js`) * `Accounts` * on client: `new AccountsClient()` (extends `AccountsCommon`) * on server: `new AccountsServer(Meteor.server)` (extends `AccountsCommon`) * `Meteor` * new field `users` for the `users` collection. Name is expected to become configurable in future versions.