import React, { useEffect, useState } from "react"; import { Box, Button, Chip, Paper, Typography } from "@mui/material"; import { PublicClientApplication } from "@azure/msal-browser"; import { api, getApiErrorMessage } from "../api"; import { clearAuthClientState, getAuthPersistencePreference } from "../auth"; import { useToast } from "../toast"; import { useI18n } from "../i18n/I18nProvider"; type MeResponse = { provider?: "local" | "google" | "microsoft" | "external"; email?: string; userName?: string; displayName?: string; firstName?: string; lastName?: string; microsoftLink?: { linked: boolean; email?: string | null; linkedAt?: string | null; } | null; }; let msalInstance: PublicClientApplication | null = null; function getMsalInstance(clientId: string): PublicClientApplication { msalInstance ??= new PublicClientApplication({ auth: { clientId, authority: "https://login.microsoftonline.com/common", redirectUri: window.location.origin }, }); return msalInstance; } export default function MicrosoftAuthCard({ onSignedIn }: { onSignedIn?: () => void }) { const { toast } = useToast(); const { t } = useI18n(); const [me, setMe] = useState(null); const [working, setWorking] = useState(false); const clientId = (process.env.REACT_APP_MICROSOFT_CLIENT_ID || "").trim(); const signedIn = Boolean(me?.provider); const actionLabel = !signedIn ? t("continueWithMicrosoft") : me?.provider === "local" && !me?.microsoftLink?.linked ? t("linkWithMicrosoft") : t("signInWithMicrosoft"); async function refreshMe() { try { const res = await api.get("/auth/me"); setMe(res.data); } catch { setMe(null); } } useEffect(() => { void refreshMe(); }, []); useEffect(() => { const onAuthChanged = () => { void refreshMe(); }; window.addEventListener("auth-changed", onAuthChanged); return () => window.removeEventListener("auth-changed", onAuthChanged); }, []); async function handleSignIn() { if (!clientId) return; setWorking(true); try { const msal = getMsalInstance(clientId); await msal.initialize(); const result = await msal.loginPopup({ scopes: ["openid", "profile", "email"] }); const idToken = result.idToken; if (!idToken) throw new Error(t("microsoftAuthFailed")); if (me?.provider === "local") { const res = await api.post<{ linked: boolean; email?: string | null }>("/auth/microsoft/link", { token: idToken, rememberMe: getAuthPersistencePreference() === "local" }); toast(res.data?.email ? t("microsoftLinkedSuccessWithEmail", { email: res.data.email }) : t("microsoftLinkedSuccess"), "success"); await refreshMe(); } else { await api.post("/auth/microsoft/exchange", { token: idToken, rememberMe: getAuthPersistencePreference() === "local" }); window.dispatchEvent(new Event("auth-changed")); toast(t("microsoftSignedIn"), "success"); onSignedIn?.(); } } catch (e: any) { toast(getApiErrorMessage(e, t("microsoftAuthFailed")), "error"); } finally { setWorking(false); } } const signedInName = me?.userName || me?.displayName || [me?.firstName, me?.lastName].filter(Boolean).join(" ") || me?.email || ""; return ( {t("microsoftAccountTitle")} {!clientId && ( {t("microsoftSetupHint")} )} {clientId && ( {me?.microsoftLink?.linkedAt ? : null} {!signedIn ? ( {t("microsoftSignInHint")} ) : me?.provider === "local" ? ( {me.microsoftLink?.linked ? t("microsoftLinkedTo", { email: me.microsoftLink.email || t("microsoftLinkedToYourAccount") }) : t("microsoftBindHint")} ) : ( {t("microsoftExchangeHint")} )} {actionLabel} {signedIn ? ( ) : null} {me?.provider === "local" && me.microsoftLink?.linked ? ( ) : null} {signedIn && me?.email ? ( {t("signedInAs", { name: signedInName })} ) : null} )} ); }