ThinkDeepTech/thinkdeep

View on GitHub
packages/deep-economic-analyzer/user.js

Summary

Maintainability
A
1 hr
Test Coverage
import createAuth0Client from '@auth0/auth0-spa-js';

globalThis.auth0 = null;
/**
 * Fetch user data.
 * @param {Object} options
 * @return {Object} User object.
 */
const user = async (
  options = {
    domain: process.env.PREDECOS_AUTH_DOMAIN,
    clientId: process.env.PREDECOS_AUTH_CLIENT_ID,
    audience: process.env.PREDECOS_AUTH_AUDIENCE,
  }
) => {
  if (!globalThis.auth0) {
    await initAuth(options);
  }
  const auth0 = globalThis.auth0;
  const profile = await auth0.getUser();
  const loggedIn = await auth0.isAuthenticated();
  let accessToken = '';
  let idToken = '';
  if (loggedIn) {
    accessToken = await auth0.getTokenSilently();

    idToken = (await auth0.getIdTokenClaims()).__raw;
  }

  return {profile, login, logout, loggedIn, accessToken, idToken};
};

/**
 * Initialize auth library.
 * @param {Object} options - Object including the auth domain, clientId and audience.
 *
 * NOTE: Excluding the audience in the call to createAuth0Client results in a malformed
 * access token being returned. This can cause a great deal of confusion when encountered.
 */
const initAuth = async (options) => {
  if (!options?.domain || !options.clientId || !options.audience) {
    throw new Error('The domain, clientId and audience are required.');
  }

  const {domain, clientId, audience} = options;

  try {
    globalThis.auth0 = await createAuth0Client({
      domain,
      client_id: clientId,
      redirect_uri: globalThis.location.origin,
      cacheLocation: 'localstorage',
      audience,
      scope: 'openid profile email read:all',
    });
  } catch (e) {
    console.log(
      `An error occurred while creating the auth client: ${e.message}`
    );
  }

  if (!globalThis.auth0) {
    throw new Error('Failed to create auth client.');
  }

  const auth0 = globalThis.auth0;
  const isAuthenticated = await auth0.isAuthenticated();
  const query = globalThis.location.search || '';
  if (!isAuthenticated && query.includes('code=') && query.includes('state=')) {
    // Parse auth info in url
    await auth0.handleRedirectCallback();

    // Remove parsed info from url
    globalThis.history.replaceState({}, document.title, '/');
  }
};

/**
 * Show the login window and prompt for user login.
 * @param {String} redirectUri Uri to which the user will be redirected after login.
 */
const login = async (redirectUri = globalThis.location.origin) => {
  return globalThis.auth0.loginWithRedirect({
    redirect_uri: redirectUri,
  });
};

/**
 * Log the user out.
 */
const logout = async () => {
  return globalThis.auth0.logout({
    returnTo: globalThis.location.origin,
  });
};

/**
 * Set the auth0 object.
 *
 * NOTE: This is included purely for testing purposes and should not be done
 * in source code.
 *
 * @param {Object} authClient - Auth client for use with testing.
 */
const setAuthClientForTesting = (authClient) => {
  globalThis.auth0 = authClient;
};

/**
 * Get the user role.
 *
 * @param {Object} usr User object returned from Auth API.
 *
 * @return {Array<String>} The user roles.
 */
const roles = (usr) => {
  return usr?.profile && usr?.profile['https://predecos.com/roles']
    ? usr.profile['https://predecos.com/roles']
    : [];
};

/**
 * Determine if the specified user is premium or not.
 *
 * @param {Object} usr User object returned from Auth API.
 *
 * @return {Boolean} True if the user is a premium user. False otherwise.
 */
const premium = (usr) => {
  return !!usr && roles(usr).includes('premium-user');
};

export {user, premium, setAuthClientForTesting};