Manual Reference Source Test

src/modules/identity-api.js

import IdentityCookie from './identity-cookie.js';
import IdentityProperty from './identity-property.js';
import IdentityUser from './identity-user.js';
import IdentityCrossApp from './identity-crossapp.js';

import {checkIsLocal} from '../helpers/local';

/**
 * Authentication API calls
 */
class IdentityAPI {
  /**
   * @param {Object} [options={}] - Options
   * @param {IdentityServer} [options.server] - Server instance api methods are called on.
   * @param {IdentityConfig} [options.config] - Identity config object
   * @param {Boolean} useCookie - Enable Identity Cookie module
   */
  constructor({
    server,
    config,
    useCookie = false,
  } = {}) {
    /**
     * Save local Identity Server instance
     *
     * @type {IdentityServer}
     */
    this._server = server;

    /**
     * @type {IdentityCookie}
     */
    this._cookieHelpers = new IdentityCookie(config);
    this._cookieHelpers.setEnabled(useCookie);

    /**
     * Check if environment proxies the api calls to fix issues with openidm endpoints
     * @type {boolean}
     */
    this._local = checkIsLocal();
    const aws = location.origin.endsWith('id.nbc.com-sdk.s3-website-us-east-1.amazonaws.com');

    /**
     * Class variable that defines if API calls should be proxied
     *
     * @type {boolean}
     */
    this._proxied = (this._local||aws);
  }

  /**
   * Login user using email address and password
   *
   * @param {string} email - User email address
   * @param {string} password - User password
   * @param {Object} options - Additional options
   * @param {Object} options.headers - additional headers to add to the call
   * @return {Object} - IDM response
   */
  loginByEmail(email, password, options = {headers: {}}) {
    const b64Password = btoa(encodeURIComponent(password).replace(/%([0-9A-F]{2})/g, function(match, p1) {
      return String.fromCharCode(parseInt(p1, 16));
    }));

    return this._server.call('api/2020-10/profile/email/session', {
      method: 'GET',
      headers: {
        'X-IDM-Password': `=?UTF-8?B?${b64Password}?=`,
        'X-IDM-Username': email,
        ...options.headers,
      },
    });
  }

  /**
   * Register user by email address and password
   *
   * @param {Object} user - User properties
   * @param {string} user.mail - User email address
   * @param {string} user.password - User password
   * @param {string} [user.userName=user.mail] - User name
   * @param {string} [user.givenName] - User given name
   * @param {string} [user.sn] - User surname
   * @param {number} [user.termsAndConditionsAgreementDate] - Unix timestamp at which terms where agreed to
   * @param {string} [user.facebookId] - User Facebook id
   * @param {Object} options - Additional options
   * @param {Object} options.headers - additional headers to add to the call
   * @return {Promise} - Promise of IDM response
   */
  registerByEmail(user, options = {headers: {}}) {
    if (!user.userName) {
      user.userName = user.mail;
    }
    return this._server.call('api/2020-10/profile/email/register', {
      method: 'POST',
      headers: options.headers,
      body: user,
    }).then((response) => {
      if (response.result.code === 200) {
        return this.loginByEmail(user.mail, user.password, {headers: {
          'vppa_re_opt_in': false,
          ...options.headers,
        }});
      }

      return response;
    });
  }

  /**
   * Register external library user
   *
   * @param {string} idToken - Auth token
   * @param {string} type - External library type
   * @param {Object} options - Additional options
   * @param {Object} options.headers - additional headers to add to the call
   * @return {Promise}
   */
  externalRegister(idToken, type, options = {headers: {}}) {
    const headers = {
      ...options.headers,
    };

    switch (type) {
      case 'facebook':
        headers.access_token = idToken;
        break;
      default:
        headers.id_token = idToken;
    }

    return this._server.call(`api/2020-10/profile/${type}/register`, {
      method: 'POST',
      headers,
    });
  }

  /**
   * Get external library user session
   *
   * @param {string} idToken - Auth token
   * @param {string} type - External library type
   * @param {Object} options - Additional options
   * @param {Object} options.headers - additional headers to add to the call
   * @return {Promise}
   */
  externalSession(idToken, type, options = {headers: {}}) {
    const headers = {
      ...options.headers,
    };

    switch (type) {
      case 'facebook':
        headers.access_token = idToken;
        break;
      default:
        headers.id_token = idToken;
    }

    return this._server.call(`api/2020-10/profile/${type}/session`, {
      method: 'GET',
      headers,
    });
  }

  /**
   * Get user info by token
   * @param {string} token - IDM session token
   * @param {Object} userData - existing user data
   * @param {Object} options - additional options
   * @param {Object} options.headers - additional headers to add to the call
   * @return {Promise} Promise of userinfo
   */
  getUserInfo(token, userData = null, options = {headers: {}}) {
    const appendProps = (data) => {
      // Get any userinfo from the cookie
      let savedProps = {};
      const userId = data._id;

      if (!userId) return data;

      if (this._cookieHelpers.getCookie('properties', {userId})) {
        savedProps = JSON.parse(this._cookieHelpers.getCookie('properties', {userId}));
      }

      return Object.assign(data, savedProps);
    };

    return new Promise((resolve, reject) => {
      if (userData) {
        resolve(appendProps(userData));
      } else {
        // Get userinfo from the server
        this._server.call('api/2020-10/profile', {
          method: 'GET',
          headers: {
            session_token: token,
            ...options.headers,
          },
        }).then((response) => {
          if (response.result.code === 200 && response.profile) {
            resolve(appendProps(response.profile));
          } else {
            reject(response.result);
          }
        }).catch((error) => reject(error));
      }
    });
  }

  /**
   * Update user profile properties
   *
   * @param {string} token - IDM session token
   * @param {number} revisionId Profile revision id
   * @param {string} userId - IDM user id
   * @param {Object} properties - User profile properties to change
   * @param {Object} options - Additional options
   * @param {Object} options.headers - additional headers to add to the call
   * @return {Promise} - Promise of user info
   */
  updateProfile(token, revisionId, userId = null, properties, options = {headers: {}}) {
    return new Promise((resolve, reject) => {
      const serverUpdates = [];
      const cookieUpdates = {};
      const {standardMap} = IdentityProperty;
      const {serverKeyMap} = IdentityUser;

      Object.keys(properties).forEach((key) => {
        const obj = {
          field: serverKeyMap[key] || key,
          value: properties[key],
        };

        // Only push properties that are in the standardMap
        // Save all others to a cookie
        if (Object.keys(standardMap).includes(key)) {
          obj.operation = 'replace';

          serverUpdates.push(obj);
        } else {
          if (obj.field === 'mail') return;
          cookieUpdates[obj.field] = obj.value;
        }
      });

      // Update profile cookie
      if (Object.keys(cookieUpdates).length > 0) {
        const existingCookie = this._cookieHelpers.getCookie('properties', {userId});
        const savedProps = (existingCookie) ? JSON.parse(existingCookie) : {};
        const newProps = Object.assign(savedProps, cookieUpdates);
        const cookieValue = JSON.stringify(newProps);
        this._cookieHelpers.setCookie('properties', cookieValue, {userId});
      }

      if (serverUpdates.length > 0) {
        return this._server.call('api/2020-10/profile', {
          method: 'PATCH',
          headers: {
            'session_token': token,
            'X-IDM-Update-Rev': revisionId,
            ...options.headers,
          },
          body: serverUpdates,
        }).then(resolve, reject);
      }

      return resolve({status: 'Success'});
    });
  }

  /**
   * Unauthenticate user
   *
   * @param {string} token - IDM session token
   * @param {function} nativeController - native flag, defaults to false
   * @param {Object} options - Additional options
   * @param {Object} options.headers - additional headers to add to the call
   * @return {Promise<Identity, Error>} - Promise of unauthenticated Identity
   */
  unauthenticate(token, nativeController, options = {headers: {}}) {
    if (nativeController) {
      const crossAppBridge = new IdentityCrossApp(nativeController);

      crossAppBridge.init().then(() => {
        if (window.nativeWrapper.os === 'iOS') {
          // Remove session from keychain
          crossAppBridge.remove(token);
        } else if (window.nativeWrapper.os === 'Android') {
          // Disable Android auto sign in
          nativeController.sendCommand('DisableAutoSignIn', {});
        }
      });
    }

    return this._server.call('openam/json/realms/root/sessions/?_action=logout', {
      method: 'POST',
      headers: {
        iplanetdirectorypro: token,
        ...options.headers,
      },
    });
  }
}

export default IdentityAPI;