Untitled

Anonymous
plain_text
02/24/2026 10:39 PM
13.2 KB
10
Indexable
<!DOCTYPE html>
<html lang="pt-BR">
  <head>
    <meta charset="UTF-8" />
    <title>set-cookie</title>
  </head>
  <body>
    <script>
      console.log("[set-cookie] script carregou, aguardando mensagem...");

      window.addEventListener("message", (event) => {
        try {
          const origin = event.origin;
          const originURL = new URL(origin);

          const isHttps = originURL.protocol === "https:";
          const isAuthorizedHost =
            originURL.hostname.endsWith(".vrgente.com.br") ||
            originURL.hostname.endsWith(".vr.com.br");

          if (!isHttps || !isAuthorizedHost) {
            console.warn("[set-cookie] Origem não autorizada:", origin);
            return;
          }

          const payload = event.data && event.data.payload;

          if (!payload) {
            console.warn("[set-cookie] payload ausente");
            return;
          }

          document.cookie =
            `hcm-user=${payload}; ` +
            `SameSite=None; Secure; ` +
            `path=/; domain=.vrgente.com.br; ` +
            `expires=Fri, 31 Dec 9999 23:59:59 GMT`;

          // Verifica se o cookie foi realmente gravado
          const cookieGravado = document.cookie.match(/(^|;)\s*hcm-user\s*=/);
          if (!cookieGravado) {
            console.error("[set-cookie] Cookie NÃO foi gravado. Verifique CSP/headers do servidor.");
          } else {
            console.log("[set-cookie] Cookie gravado com sucesso.");
          }

          // Responde de volta para o minisite
          // event.source é a janela do shell/minisite — o targetOrigin deve ser o origin dele
          event.source?.postMessage("COOKIE_SUCCESS", { targetOrigin: origin });

          console.log("[set-cookie] ACK enviado para:", origin);
        } catch (error) {
          console.error("[set-cookie] erro:", error);
        }
      });
    </script>
  </body>
</html>

------------------------------

import Cookies from "js-cookie";
import { ReactNode, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import secureLocalStorage from "react-secure-storage";

import { UserData, UserResponse, zUserResponse } from "@config/schemas/user";
import { useAuthenticateHcmWithSpeToken } from "@hooks/useAuthenticateHcmWithSpeToken";
import { useFeatureFlagState } from "@hooks/useFeatureFlagState";
import { useToast } from "@hooks/useToast";
import { PageLoading, useLocalStorage } from "@vr-gente/hcm-components-lib";
import { setHcmUserCookie } from "@utilities/cookie";
import { useGlobalState } from "@vr-spe-toolkit/react";
import { getCookies } from "@vr-spe-toolkit/utils";
import { AuthenticationContext } from "./authentication-provider";
import { runtimeEnvironment } from "@helpers/env";

const NAVIGATE_EVENT_NAME = 'navigate_hcm';

// === Helpers ===
async function forceCleanSession() {
  try {
    secureLocalStorage.removeItem("user");
    localStorage.removeItem("user");
    Cookies.remove("access-token");
    Cookies.remove("hcm-user");
    document.cookie = "hcm-user=; SameSite=None; Secure; path=/; domain=.vrgente.com.br; expires=Thu, 01 Jan 1970 00:00:00 GMT";
  } catch (e) {
    console.error("Erro ao limpar sessão:", e);
  }
}

function isEnvironmentClean() {
  const token = Cookies.get("access-token");
  const storage = secureLocalStorage.getItem("user");
  const hcmUser = document.cookie.match(/(^|;)\s*hcm-user\s*=/);
  return !token && !storage && !hcmUser;
}

type AuthStage =
  | 'INITIAL_CLEANING'
  | 'AUTHENTICATING'
  | 'SETTING_SESSION'
  | 'LOADING_FLAGS'
  | 'READY';

export function SpeGedAuthenticationProvider({ children }: { children: ReactNode }) {
  const toast = useToast();
  const navigate = useNavigate();
  const { set, remove } = useLocalStorage<UserData | null>("user");
  const idToken = getCookies("spe-security/id-token");
  const { user } = useGlobalState();

  const [stage, setStage] = useState<AuthStage>('INITIAL_CLEANING');
  const [sessionData, setSessionData] = useState<UserResponse["data"] | null>(null);

  const { loadFeatureFlag, loading: isFeatureFlagLoading } = useFeatureFlagState();

  const { mutate } = useAuthenticateHcmWithSpeToken({
    onSuccess(res) {
      const parsedRes = zUserResponse.parse(res.data);
      setSessionData(parsedRes.data);
      setStage('SETTING_SESSION');
    },
    onError() {
      forceCleanSession();
      remove();
      toast?.current?.openToast({
        message: "O serviço está temporariamente indisponível.",
        type: "error",
      });
      navigate("/ged/sem-acesso");
    },
  });

  // PASSO 1: Limpeza Inicial
  useEffect(() => {
    setStage('INITIAL_CLEANING');
    console.log("[Auth] 1. Limpando ambiente...");

    const cleanEnvironment = async () => {
      remove();
      await forceCleanSession();

      if (isEnvironmentClean()) {
        setStage('AUTHENTICATING');
      } else {
        console.error("[Auth] Falha ao limpar ambiente.");
        window.location.href = '/';
        window.location.reload();
      }
    };

    cleanEnvironment();

    // PASSO 6: Limpar ao desmontar
    window.addEventListener('unmount_document_manager', async (res) => {
      const customEvent = res as CustomEvent;
      console.log("[Auth] Unmounting - Limpando tudo.");
      remove();
      setStage('INITIAL_CLEANING');
      await forceCleanSession();
      customEvent.detail.callback();
    });
  }, []);

  // PASSO 2: Autenticação
  useEffect(() => {
    if (stage === 'AUTHENTICATING' && idToken && user?.cnpj) {
      console.log("[Auth] 2. Autenticando...");
      mutate({ idToken, cnpj: user?.cnpj });
    }
  }, [stage, idToken, user, mutate]);

  // PASSO 3: Gravar Sessão via iframe cross-domain
  useEffect(() => {
    if (stage !== "SETTING_SESSION" || !sessionData) return;

    console.log("[Auth] 3. Gravando Sessão...");

    // Storage local
    secureLocalStorage.setItem("user", sessionData);
    set(sessionData);

    // Cookies locais (domínio do shell)
    Cookies.set("access-token", sessionData.access_token);
    setHcmUserCookie({
      access_token: sessionData.access_token,
      refresh_token: sessionData.refresh_token,
    });

    const cookiePayload = {
      access_token: sessionData.access_token,
      refresh_token: sessionData.refresh_token,
    };

    const encoded = encodeURIComponent(JSON.stringify(cookiePayload));

    const divRoot = document.getElementById("root") as HTMLDivElement | null;
    if (!divRoot) {
      console.error("[Auth] Root não encontrado.");
      return;
    }

    const IFRAME_ID = "ged-set-cookie-iframe";
    const portalOrigin = new URL(runtimeEnvironment.GED_SIGNATURE_PORTAL).origin;
    const iframeUrl = `${portalOrigin}/assets/set-cookie.html`;

    const sendMessage = (iframeEl: HTMLIFrameElement) => {
      try {
        console.log("[Auth] Enviando mensagem para o iframe...", { portalOrigin });
        iframeEl.contentWindow?.postMessage(
          { origin: window.location.origin, payload: encoded },
          portalOrigin
        );
      } catch (error) {
        console.error("[Auth] Erro ao enviar mensagem para o iframe:", error);
      }
    };

    const handleResponse = (event: MessageEvent) => {
      if (event.origin !== portalOrigin) {
        console.warn("[Auth] Origem não autorizada:", event.origin, "esperado:", portalOrigin);
        return;
      }

      if (event.data === "COOKIE_SUCCESS") {
        console.log("[Auth] Cookie configurado com sucesso pelo iframe.");
        window.removeEventListener("message", handleResponse);
        document.getElementById(IFRAME_ID)?.remove();
        setStage("LOADING_FLAGS");
        return;
      }

      console.error("[Auth] Resposta inesperada do iframe:", event.data);
    };

    window.addEventListener("message", handleResponse);

    // Sempre recria o iframe para garantir que vai carregar a URL correta
    // e disparar o evento onload — nunca confiamos em iframe reaproveitado cross-origin
    document.getElementById(IFRAME_ID)?.remove();

    const iframe = document.createElement("iframe");
    iframe.id = IFRAME_ID;
    iframe.src = iframeUrl;
    iframe.style.width = "0";
    iframe.style.height = "0";
    iframe.style.border = "none";
    iframe.style.position = "absolute";

    // Só envia a mensagem depois que o iframe confirmou que carregou a URL correta
    iframe.addEventListener("load", () => {
      console.log("[Auth] Iframe carregou, enviando mensagem...");
      sendMessage(iframe);
    });

    divRoot.appendChild(iframe);

    // Fallback: se nunca responder em 5s, segue o fluxo
    const timeout = window.setTimeout(() => {
      console.error("[Auth] Timeout esperando COOKIE_SUCCESS do iframe.");
      setStage("LOADING_FLAGS");
    }, 5000);

    return () => {
      window.clearTimeout(timeout);
      window.removeEventListener("message", handleResponse);
    };
  }, [stage, sessionData, set]);

  // PASSO 4: Carregar Feature Flags
  useEffect(() => {
    if (stage === 'LOADING_FLAGS') {
      console.log("[Auth] 4. Carregando Flags...");
      loadFeatureFlag();
    }
  }, [stage, loadFeatureFlag]);

  // PASSO 5: Monitorar Flags para liberar renderização
  useEffect(() => {
    if (stage === 'LOADING_FLAGS' && !isFeatureFlagLoading) {
      console.log("[Auth] 5. Pronto.");
      setStage('READY');
    }
  }, [stage, isFeatureFlagLoading]);

  // Roteamento interno via evento customizado (minisite no SPE)
  useEffect(() => {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const handleCustomEvent = (event: any) => {
      const { detail } = event;
      if (detail?.url) {
        navigate(detail.url, { replace: true });
      }
    };

    window.addEventListener(NAVIGATE_EVENT_NAME, handleCustomEvent);
    return () => window.removeEventListener(NAVIGATE_EVENT_NAME, handleCustomEvent);
  }, []);

  if (stage !== 'READY') {
    return <PageLoading />;
  }

  return (
    <AuthenticationContext.Provider value={{ session: sessionData, isSuccess: true }}>
      {children}
    </AuthenticationContext.Provider>
  );
}
Editor is loading...
Leave a Comment