Alokai
ReferenceMagentoMethods

LoginCustomer

Implements LoginCustomer Unified Method.

Source

import type { Maybe } from "@alokai/connect";
import { getNormalizers } from "@alokai/connect/integration-kit";
import { getLogger, HttpStatusCode } from "@alokai/connect/middleware";
import type { Context } from "@vsf-enterprise/magento-api";

import { clearStaleCustomerToken, defineApi, query, setCartId } from "@vsf-enterprise/unified-api-magento";
import type { Customer, GenerateCustomerTokenMutation } from "@vsf-enterprise/unified-api-magento/ecommerceTypes";

const MESSAGE_LOGIN_ERROR = "Could not login customer";

export const loginCustomer = defineApi.loginCustomer(async (context, args) => {
  const { api } = await context.getApiClient();
  const { email, password } = args;
  const { normalizeCustomer } = getNormalizers(context);
  let loginData: GenerateCustomerTokenMutation;
  const tokenCookieName = context.config.cookies.customerCookieName;
  const isGuestSession = !context.req.cookies[tokenCookieName];
  const guestCartId = isGuestSession ? context.config.state.getCartId() : null;

  if (!isGuestSession) {
    // An explicit login is intent to re-authenticate: drop any existing
    // (possibly stale) token instead of rejecting with "already logged in", so
    // the stale Bearer token isn't sent on the token mutation. Also expire the
    // response cookie up front, so a failed re-auth can't leave a stale
    // customer cookie wedged on the client. The cookie is re-set below on
    // success (or stays cleared on failure via the catch).
    clearStaleCustomerToken(context);
  }

  try {
    let cartIdToMerge: Maybe<string>;
    [loginData, cartIdToMerge] = await Promise.all([
      query(
        api.generateCustomerToken({
          email,
          password,
          recaptchaToken: "",
        }),
      ),
      // Must run before the customer token is set: Magento rejects guest-cart reads made with it.
      getNonEmptyGuestCartId(context, guestCartId),
    ]);
    context.req.cookies[tokenCookieName] = loginData.generateCustomerToken?.token;

    const [user] = await Promise.all([
      query(api.customer({})),
      mergeIntoCustomerCart(context, cartIdToMerge),
    ]);

    context.res.cookie(
      context.config.cookies.customerCookieName,
      loginData.generateCustomerToken?.token,
    );
    // customer's 'id' field is deprecated in Magento
    return {
      customer: normalizeCustomer(user.customer as Customer),
    };
  } catch {
    context.config.state.setCustomerToken(null);
    throw context.createHttpError({
      message: MESSAGE_LOGIN_ERROR,
      statusCode: HttpStatusCode.UNAUTHORIZED,
    });
  }
});

async function getNonEmptyGuestCartId(
  context: Context,
  guestCartId: Maybe<string>,
): Promise<Maybe<string>> {
  if (!guestCartId) return null;

  try {
    const { api } = await context.getApiClient();
    const { cart } = await query(api.cart(guestCartId));
    return cart?.total_quantity ? guestCartId : null;
  } catch (error) {
    const logger = getLogger(context);
    logger.warning("Could not read the guest cart to merge on login", { error });
    return null;
  }
}

async function mergeIntoCustomerCart(context: Context, sourceCartId: Maybe<string>) {
  if (!sourceCartId) return;

  try {
    const { api } = await context.getApiClient();
    const { customerCart } = await query(api.customerCart());
    await query(
      api.mergeCarts({
        destination_cart_id: customerCart.id,
        source_cart_id: sourceCartId,
      }),
    );
    setCartId(context, customerCart.id);
  } catch (error) {
    const logger = getLogger(context);
    logger.error("Could not merge the guest cart into the customer cart on login", { error });
  }
}

On this page