Vue Storefront is now Alokai! Learn More
LoginCustomer

LoginCustomer

Implements LoginCustomer Unified Method.

Source

/* eslint-disable etc/throw-error */
import { defineApi } from "@vsf-enterprise/unified-api-commercetools";
import type { CommercetoolsContext } from "@vsf-enterprise/commercetools-api";
import { CT_COOKIE_NAME, isAnonymousSession } from "@vsf-enterprise/commercetools-api";
import { getNormalizers } from "@vue-storefront/unified-data-model";

const MESSAGE_LOGIN_ERROR = "Could not login customer";
const MESSAGE_ALREADY_LOGGED_IN = "Customer is already logged in";

export const loginCustomer = defineApi.loginCustomer(async (context, args) => {
  if (isAlreadyLoggedIn(context)) {
    throw { statusCode: 403, message: MESSAGE_ALREADY_LOGGED_IN };
  }

  try {
    const loginData = await context.api.customerSignMeIn(args);
    const { normalizeCustomer } = getNormalizers(context);

    // @ts-expect-error -- CT types outdated
    const user = loginData.data?.user?.customer;

    if (!user) {
      throw { statusCode: 401, message: MESSAGE_LOGIN_ERROR };
    }

    return { customer: normalizeCustomer(user) };
  } catch (error) {
    throw mapGraphQlError(error);
  }
});

// CT doesn't expose GQL errors type
function mapGraphQlError(err: any) {
  const isInvalidCredentials = err?.graphQLErrors?.find(
    (error: any) => error.extensions.code === "InvalidCredentials",
  );

  if (isInvalidCredentials) {
    return { statusCode: 401, message: MESSAGE_LOGIN_ERROR };
  }

  return {
    statusCode: 500,
    message: "Internal Server Error",
  };
}

function isAlreadyLoggedIn(context: CommercetoolsContext) {
  const rawToken = context.req.cookies[CT_COOKIE_NAME];

  if (!rawToken) {
    return false;
  }

  let parsedToken: any;

  try {
    parsedToken = JSON.parse(rawToken);
  } catch {
    parsedToken = null;
  }

  return parsedToken && !isAnonymousSession(parsedToken);
}