"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.UserManagerInstance = exports.ANONYMOUS_USERNAME = void 0; function _isEmpty() { const data = _interopRequireDefault(require("lodash/isEmpty")); _isEmpty = function () { return data; }; return data; } function _camelCase() { const data = _interopRequireDefault(require("lodash/camelCase")); _camelCase = function () { return data; }; return data; } function _snakeCase() { const data = _interopRequireDefault(require("lodash/snakeCase")); _snakeCase = function () { return data; }; return data; } function _ApiV() { const data = _interopRequireDefault(require("./ApiV2")); _ApiV = function () { return data; }; return data; } function Analytics() { const data = _interopRequireWildcard(require("./Analytics")); Analytics = function () { return data; }; return data; } function _Config() { const data = _interopRequireDefault(require("./Config")); _Config = function () { return data; }; return data; } function _XDLError() { const data = _interopRequireDefault(require("./XDLError")); _XDLError = function () { return data; }; return data; } function _Logger() { const data = _interopRequireDefault(require("./Logger")); _Logger = function () { return data; }; return data; } function _UserSettings() { const data = _interopRequireDefault(require("./UserSettings")); _UserSettings = function () { return data; }; return data; } function _Utils() { const data = require("./Utils"); _Utils = function () { return data; }; return data; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } const ANONYMOUS_USERNAME = 'anonymous'; exports.ANONYMOUS_USERNAME = ANONYMOUS_USERNAME; class UserManagerInstance { constructor() { _defineProperty(this, "_currentUser", null); _defineProperty(this, "_getSessionLock", new (_Utils().Semaphore)()); _defineProperty(this, "_interactiveAuthenticationCallbackAsync", void 0); } static getGlobalInstance() { if (!__globalInstance) { __globalInstance = new UserManagerInstance(); } return __globalInstance; } initialize() { this._currentUser = null; this._getSessionLock = new (_Utils().Semaphore)(); } /** * Logs in a user for a given login type. * * Valid login types are: * - "user-pass": Username and password authentication * * If the login type is "user-pass", we directly make the request to www * to login a user. */ async loginAsync(loginType, loginArgs) { if (loginType === 'user-pass') { if (!loginArgs) { throw new Error(`The 'user-pass' login type requires a username and password.`); } const apiAnonymous = _ApiV().default.clientForUser(); const loginResp = await apiAnonymous.postAsync('auth/loginAsync', { username: loginArgs.username, password: loginArgs.password }); if (loginResp.error) { throw new (_XDLError().default)('INVALID_USERNAME_PASSWORD', loginResp['error_description']); } return this._getProfileAsync({ currentConnection: 'Username-Password-Authentication', sessionSecret: loginResp.sessionSecret }); } else { throw new Error(`Invalid login type provided. Must be 'user-pass'.`); } } async registerAsync(userData, user = null) { if (!user) { user = await this.getCurrentUserAsync(); } if (user) { await this.logoutAsync(); user = null; } try { // Create or update the profile let registeredUser = await this.createOrUpdateUserAsync({ connection: 'Username-Password-Authentication', // Always create/update username password email: userData.email, givenName: userData.givenName, familyName: userData.familyName, username: userData.username, password: userData.password }); registeredUser = await this.loginAsync('user-pass', { username: userData.username, password: userData.password }); return registeredUser; } catch (e) { console.error(e); throw new (_XDLError().default)('REGISTRATION_ERROR', 'Error registering user: ' + e.message); } } /** * Ensure user is logged in and has a valid token. * * If there are any issues with the login, this method throws. */ async ensureLoggedInAsync() { if (_Config().default.offline) { throw new (_XDLError().default)('NETWORK_REQUIRED', 'Can\'t verify user without network access'); } let user = await this.getCurrentUserAsync(); if (!user && this._interactiveAuthenticationCallbackAsync) { user = await this._interactiveAuthenticationCallbackAsync(); } if (!user) { throw new (_XDLError().default)('NOT_LOGGED_IN', 'Not logged in'); } return user; } setInteractiveAuthenticationCallback(callback) { this._interactiveAuthenticationCallbackAsync = callback; } async _readUserData() { let auth = await _UserSettings().default.getAsync('auth', null); if ((0, _isEmpty().default)(auth)) { // XXX(ville): // We sometimes read an empty string from ~/.expo/state.json, // even though it has valid credentials in it. // We don't know why. // An empty string can't be parsed as JSON, so an empty default object is returned. // In this case, retrying usually helps. auth = await _UserSettings().default.getAsync('auth', null); } if (typeof auth === 'undefined') { return null; } return auth; } /** * Get the current user based on the available token. * If there is no current token, returns null. */ async getCurrentUserAsync() { await this._getSessionLock.acquire(); try { // If user is cached and there is a sessionSecret, return the user if (this._currentUser && this._currentUser.sessionSecret) { return this._currentUser; } if (_Config().default.offline) { return null; } const data = await this._readUserData(); // No session, no current user. Need to login if (!data || !data.sessionSecret) { return null; } try { return await this._getProfileAsync({ currentConnection: data.currentConnection, sessionSecret: data.sessionSecret }); } catch (e) { _Logger().default.global.warn('Fetching the user profile failed'); _Logger().default.global.warn(e); return null; } } finally { this._getSessionLock.release(); } } async getCurrentUsernameAsync() { const data = await this._readUserData(); if (!data || !data.username) { return null; } return data.username; } async getSessionAsync() { const data = await this._readUserData(); if (!data || !data.sessionSecret) { return null; } return { sessionSecret: data.sessionSecret }; } /** * Create or update a user. */ async createOrUpdateUserAsync(userData) { let currentUser = this._currentUser; if (!currentUser) { // attempt to get the current user currentUser = await this.getCurrentUserAsync(); } const api = _ApiV().default.clientForUser(currentUser); const { user: updatedUser } = await api.postAsync('auth/createOrUpdateUser', { userData: _prepareAuth0Profile(userData) }); this._currentUser = { ...this._currentUser, ..._parseAuth0Profile(updatedUser), kind: 'user' }; return this._currentUser; } /** * Logout */ async logoutAsync() { if (this._currentUser) { Analytics().logEvent('Logout', { username: this._currentUser.username }); } this._currentUser = null; // Delete saved auth info await _UserSettings().default.deleteKeyAsync('auth'); } /** * Forgot Password */ async forgotPasswordAsync(usernameOrEmail) { const apiAnonymous = _ApiV().default.clientForUser(); return apiAnonymous.postAsync('auth/forgotPasswordAsync', { usernameOrEmail }); } /** * Get profile given token data. Errors if token is not valid or if no * user profile is returned. * * This method is called by all public authentication methods of `UserManager` * except `logoutAsync`. Therefore, we use this method as a way to: * - update the UserSettings store with the current token and user id * - update UserManager._currentUser * - Fire login analytics events * * Also updates UserManager._currentUser. * * @private */ async _getProfileAsync({ currentConnection, sessionSecret }) { let user; let api = _ApiV().default.clientForUser({ sessionSecret }); user = await api.postAsync('auth/userProfileAsync'); if (!user) { throw new Error('Unable to fetch user.'); } user = { ..._parseAuth0Profile(user), kind: 'user', currentConnection, sessionSecret }; await _UserSettings().default.setAsync('auth', { userId: user.userId, username: user.username, currentConnection, sessionSecret }); // If no currentUser, or currentUser.id differs from profiles // user id, that means we have a new login if ((!this._currentUser || this._currentUser.userId !== user.userId) && user.username && user.username !== '') { Analytics().logEvent('Login', { userId: user.userId, currentConnection: user.currentConnection, username: user.username }); Analytics().setUserProperties(user.username, { userId: user.userId, currentConnection: user.currentConnection, username: user.username }); } this._currentUser = user; return user; } } exports.UserManagerInstance = UserManagerInstance; let __globalInstance; var _default = UserManagerInstance.getGlobalInstance(); /** Private Methods **/ exports.default = _default; function _parseAuth0Profile(rawProfile) { if (!rawProfile || typeof rawProfile !== 'object') { return rawProfile; } return Object.keys(rawProfile).reduce((p, key) => { p[(0, _camelCase().default)(key)] = _parseAuth0Profile(rawProfile[key]); return p; }, {}); } function _prepareAuth0Profile(niceProfile) { if (typeof niceProfile !== 'object') { return niceProfile; } return Object.keys(niceProfile).reduce((p, key) => { p[(0, _snakeCase().default)(key)] = _prepareAuth0Profile(niceProfile[key]); return p; }, {}); } //# sourceMappingURL=__sourcemaps__/User.js.map