"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