./knowledge-base/frontend/src/pages/LoginCallbackPage.tsx

/**
 * Okta ログインコールバックページ
 *
 * Okta が認証後にリダイレクトしてくる /login/callback を処理する。
 * 処理フロー:
 *   1. handleLoginRedirect() でトークンを取得・保存
 *   2. accessToken を Cookie に即座に設定(CloudFront Function の /era-assist/* チェック用)
 *   3. login() 実行時に保存した originalUri から redirect_to を取出
 *   4. redirect_to があればそこへフルページ遷移、なければ originalUri に遷移
 *
 * このページのURLは Okta Application の "Sign-in redirect URIs" に
 * 登録されている必要がある。
 */
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { Box, CircularProgress, Typography } from "@mui/material";
import type { AccessToken } from "@okta/okta-auth-js";
import { oktaAuth } from "../auth/oktaConfig";

export default function LoginCallbackPage() {
  const navigate = useNavigate();

  useEffect(() => {
    oktaAuth
      .handleLoginRedirect()
      .then(async () => {
        // ── Cookie 即座設定 ────────────────────────────────────────────────
        // CF Function (/era-assist/*) がフルページ遷移前に Cookie を読むため、
        // window.location.href で遷移する前にトークンを Cookie に書き込む。
        try {
          const tokenObj = (await oktaAuth.tokenManager.get(
            "accessToken",
          )) as AccessToken | undefined;
          if (tokenObj?.accessToken) {
            const maxAge =
              tokenObj.expiresAt - Math.floor(Date.now() / 1000);
            if (maxAge > 0) {
              document.cookie = `okta_access_token=${tokenObj.accessToken}; path=/; max-age=${maxAge}; SameSite=Lax; Secure`;
            }
          }
        } catch {
          // Cookie 設定失敗は無視(AuthContext の useEffect が後ほど設定する)
        }

        // ── redirect_to の取得 (認証前の URL から) ──────────────────────
        // login() 時に signInWithRedirect({ originalUri }) で保存した URL を復元
        const originalUri = oktaAuth.getOriginalUri() || "/";
        try {
          const url = new URL(originalUri, window.location.origin);
          const redirectTo = url.searchParams.get("redirect_to");
          if (redirectTo && redirectTo.startsWith("/")) {
            // セキュリティ: 相対パスのみ許可
            window.location.href = redirectTo; // Cookie 設定後にフルページ遷移
            return;
          }
        } catch {
          // URLパース失敗は無視
        }
        navigate(originalUri, { replace: true });
      })
      .catch((err: unknown) => {
        console.error("Okta callback error:", err);
        navigate("/", { replace: true });
      });
  }, [navigate]);

  return (
    <Box
      sx={{
        minHeight: "100vh",
        display: "flex",
        flexDirection: "column",
        alignItems: "center",
        justifyContent: "center",
        gap: 2,
      }}
    >
      <CircularProgress />
      <Typography color="text.secondary">ログイン処理中...</Typography>
    </Box>
  );
}