auth.js

import { post, postProcess } from './lib/fetch';
import error from './lib/error';

import Cookie from 'cookie';

/**
 * @module auth
 *
 * @example
 * import { auth } from 'playerme-js-api'
 */

/**
 * This is useful if you want to separate login into 2 steps.
 * It only requires an email or username, and if it exists,
 * it'll return a user object. You will still need to send a normal
 * login after this request to do the real login,
 * this is merely a "search by email/username".
 * Please note that this is heavily rate limited for security purposes.
 *
 * @param     {Object}  args
 * @param     {String}  args.login  The login (username OR email)
 *
 * @return    {Promise<Object>}     Resolves to user information
 *
 * @example
 * auth
 *   .prelogin({ login: '<login>' })
 *   .then((user) => user)
 *   .catch((error) => error.message)
 */
export function prelogin(args = {}) {
  const { login } = args;

  if (!login) {
    return Promise.reject({ message: error.INVALID_ARGUMENTS });
  }

  return post('auth/pre-login', { login }).then(postProcess);
}

/**
 * This method of login is the regular one,
 * also used in the official front-end app.
 * This will return a cookie named <code>playerme_session</code>,
 * make sure you keep that cookie and send it on consequent requests.
 *
 * @param     {Object}  args
 * @param     {String}  args.login      The login (username OR email)
 * @param     {String}  args.password   The password
 *
 * @return    {Promise<Object>}         Resolves to user information
 *
 * @example
 * auth
 *   .check({ login: '<login>', password: '<password>' })
 *   .then((user) => user)
 *   .catch((error) => error.message)
 */
export function check(args = {}) {
  const { login, password } = args;

  if (!login || !password) {
    return Promise.reject({ message: error.INVALID_ARGUMENTS });
  }

  return post('auth/login', { login, password })
    .then(response => {
      const cookies = Cookie.parse(response.headers.get('set-cookie'));

      // get subdomain / environemnt
      const matched = /^https?:\/\/([^\.]+)\./.exec(response.url);
      let sessionName = 'playerme_session';

      if (matched) {
        const subdomain = matched[1];
        sessionName = `${subdomain}_${sessionName}`;
      }

      const playermeSession = cookies[sessionName];

      return response.json().then(responseJSON => {
        // inject session key into response result
        const resultWithSessionKey = {
          ...responseJSON,
          results: {
            ...responseJSON.results,
            playerme_session: playermeSession
          }
        };

        return Promise.resolve(resultWithSessionKey);
      });
    })
    .then(postProcess);
}

/**
 * Request a reset link. This will send a code to the user's email.
 *
 * @param   {Object}  args
 * @param   {String}  args.username   The username
 *
 * @return  {Promise<Boolean>}  Resolves to <code>true</code> if successful
 *
 * @example
 * auth
 *   .forgot({ username: '<username>' })
 *   .then((success) => success)
 *   .catch((error) => error.message)
 */
export function forgot(args = {}) {
  const { username } = args;

  if (!username) {
    return Promise.reject({ message: error.INVALID_ARGUMENTS });
  }

  return post('auth/forgot', { username })
    .then(postProcess)
    .then(() => Promise.resolve({ message: 'Successful!' }));
}

/**
 * Register a new user
 *
 * @param   {Object}    args
 * @param   {String}    username    The username (min: 3, max: 20, regex: [a-zA-Z0-9_-]+$)
 * @param   {String}    email       The email
 * @param   {String}    password    The password (min: 8)
 * @param   {String}    confirm     This should be the same as password
 *
 * @return  {Promise<Object>}   Resolves to a success message
 *
 * @example
 * auth
 *   .register({
 *     email: '<email>',
 *     username: '<username>',
 *     password: '<password>',
 *     confirm: '<confirm>'
 *   })
 *   .then((success) => success.message)
 *   .catch((error) => error.message)
 */
export function register(args = {}) {
  const { username, email, password, confirm } = args;

  if (!(username && email && password && confirm)) {
    return Promise.reject({ message: error.INVALID_ARGUMENTS });
  }

  if (password !== confirm) {
    return Promise.reject({ message: error.PASSWORD_CONFIRM_NOT_MATCHED });
  }

  return post('auth/register', { username, email, password, confirm })
    .then(postProcess)
    .then(message => Promise.resolve({ message }));
}

/**
 * Resets the password
 *
 * @param   {Object}    args
 * @param   {String}    args.code       Password reset code
 * @param   {String}    args.password   The password (min: 8)
 * @param   {String}    args.confirm    This should be the same as password
 *
 * @return  {Promise<Boolean>}   Resolves <code>true</code> if successful
 *
 * @example
 * auth
 *   .reset({ code: '<code>', password: '<password>', confirm: '<confirm>' })
 *   .then((success) => success)
 *   .catch((error) => error.message)
 */
export function reset(args = {}) {
  const { code, password, confirm } = args;

  if (!(code && password && confirm)) {
    return Promise.reject({ message: error.INVALID_ARGUMENTS });
  }

  if (password !== confirm) {
    return Promise.reject({ message: error.PASSWORD_CONFIRM_NOT_MATCHED });
  }

  return post(`auth/reset/${code}`, { password, confirm })
    .then(postProcess)
    .then(() => Promise.resolve({ message: 'Successful!' }));
}