Vue Storefront is now Alokai! Learn More
LoginCustomer

LoginCustomer

Implements LoginCustomer Unified Method.

Source

import { getNormalizers } from "@alokai/connect/integration-kit";
import { HttpError, HttpStatusCode } from "@alokai/connect/middleware";
import {
  type CommercetoolsIntegrationContext,
  CT_COOKIE_NAME,
  isAnonymousSession,
  type Token,
} from "@vsf-enterprise/commercetools-api";

import { defineApi } from "@vsf-enterprise/unified-api-commercetools";

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 context.createHttpError({
      message: MESSAGE_ALREADY_LOGGED_IN,
      statusCode: HttpStatusCode.FORBIDDEN,
    });
  }

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

    const user = loginData.user.customer;

    if (!user) {
      throw context.createHttpError({
        message: MESSAGE_LOGIN_ERROR,
        statusCode: HttpStatusCode.UNAUTHORIZED,
      });
    }

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

// CT doesn't expose GQL errors type
function mapGraphQlError(err: unknown) {
  // Check if it's an HttpError from the Apollo adapter
  if (HttpError.isHttpError(err)) {
    const graphQLErrors = err.data?.graphQLErrors as
      | { extensions?: { code?: string } }[]
      | undefined;

    const isInvalidCredentials = graphQLErrors?.find(
      (error) => error.extensions?.code === "InvalidCredentials",
    );

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

  return new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "Internal Server Error", {
    cause: err,
  });
}

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

  if (!rawToken) {
    return false;
  }

  let parsedToken: null | Token = null;

  try {
    parsedToken = JSON.parse(rawToken) as Token;
  } catch {
    return false;
  }

  return parsedToken ? !isAnonymousSession(parsedToken) : false;
}