Vue Storefront is now Alokai! Learn More
LoginCustomer

LoginCustomer

Implements LoginCustomer Unified Method.

Source

import "./extended";
import { getNormalizers } from "@alokai/connect/integration-kit";
import { HttpError, HttpStatusCode } from "@alokai/connect/middleware";
import type { OAuthUserTokenResponse } from "@vsf-enterprise/sapcc-types";

import { defineApi, getCartFromContext } from "@vsf-enterprise/unified-api-sapcc";

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) => {
  const { api } = await context.getApiClient();
  const { email, password } = args;
  const { normalizeCustomer } = getNormalizers(context);
  const cart = await getCartFromContext(context);
  const authUserTokenCookieName = context.config.headersNames!.authUserTokenCookie!;
  const authUserCookieName = context.config.headersNames!.authUserCookie!;

  let loginData: OAuthUserTokenResponse;

  if (context.req.cookies[authUserTokenCookieName]) {
    throw context.createHttpError({
      message: MESSAGE_ALREADY_LOGGED_IN,
      statusCode: HttpStatusCode.FORBIDDEN,
    });
  }

  try {
    loginData = await context.extendedApi.auth.OAuthUserAuthorization({
      password: password,
      username: email,
    });
  } catch (error) {
    if (isInvalidCredentialsError(error)) {
      throw error.withStatusCode(HttpStatusCode.UNAUTHORIZED, MESSAGE_LOGIN_ERROR);
    }
    throw error;
  }

  context.req.cookies[authUserTokenCookieName] = JSON.stringify(loginData.token);
  const shouldCreateNewCartBasedOnGuestCart = cart.totalItems && cart.guid;

  if (shouldCreateNewCartBasedOnGuestCart) {
    await api.createCart({
      oldCartId: cart.guid,
    });
  }

  try {
    const { data: user } = await api.getUser({});

    return {
      customer: normalizeCustomer(user),
    };
  } catch (error) {
    context.res.clearCookie(authUserTokenCookieName);
    context.res.clearCookie(authUserCookieName);

    if (isUnknownUserError(error)) {
      throw error.withStatusCode(HttpStatusCode.UNAUTHORIZED, MESSAGE_LOGIN_ERROR);
    }
    throw error;
  }
});

/**
 * OAuth invalid_grant error indicates invalid credentials (wrong username/password).
 * Status 400 with message "invalid_grant" is the OAuth standard response.
 */
function isInvalidCredentialsError(error: unknown): error is HttpError {
  return HttpError.isHttpError(error) && error.message === "invalid_grant";
}

/**
 * SAPCC UnknownIdentifierError occurs when the user cannot be fetched.
 * This happens for ASM (Assisted Service Module) customers who can authenticate
 * but cannot be accessed via the regular user API.
 */
function isUnknownUserError(error: unknown): error is HttpError {
  return HttpError.isHttpError(error) && error.message.includes("Cannot find user");
}