import { useEffect, useState } from "react"; import { Button } from "@executor-js/react/components/button"; // --------------------------------------------------------------------------- // CLI device-login verification page. Better Auth's deviceAuthorization() // plugin ships the JSON device API but no UI, so this is the page its // `verificationUri` (/device) points at. `executor login` prints the code and // opens this URL; the user confirms it and authorizes the device. // // Flow: on load we claim the pending code for the signed-in session (GET // /api/auth/device?user_code, the plugin's bind step), then Approve/Deny POST // to /api/auth/device/{approve,deny}. Like McpConsentPage, this is rendered // chromeless INSIDE the auth gate (so the user is already signed in) via a // static import in routes/__root.tsx, and drives the endpoints with plain fetch // (the same convention the MCP consent screen uses). // --------------------------------------------------------------------------- type Phase = "binding" | "ready" | "approved" | "denied" | "error"; export const DevicePage = () => { const params = new URLSearchParams(globalThis.window?.location.search ?? ""); const userCode = params.get("user_code") ?? ""; const [phase, setPhase] = useState("binding"); const [busy, setBusy] = useState<"approve" | "deny" | null>(null); const [error, setError] = useState(null); useEffect(() => { if (!userCode) { setPhase("error"); setError("This link is missing its device code."); return; } let alive = true; // Claim the pending code for this session; only the same session can then // approve or deny it. const bind = async () => { const res = await fetch( `${globalThis.location.origin}/api/auth/device?user_code=${encodeURIComponent(userCode)}`, { credentials: "include", headers: { accept: "application/json" } }, ); if (!alive) return; if (res.ok) { setPhase("ready"); } else { setPhase("error"); setError("This device code is unknown, already used, or expired."); } }; void bind(); return () => { alive = false; }; }, [userCode]); const decide = async (accept: boolean) => { setBusy(accept ? "approve" : "deny"); setError(null); const res = await fetch( `${globalThis.location.origin}/api/auth/device/${accept ? "approve" : "deny"}`, { method: "POST", credentials: "include", headers: { "content-type": "application/json" }, body: JSON.stringify({ userCode }), }, ); setBusy(null); if (!res.ok) { setError("Could not record your decision. The code may have expired, try again."); return; } setPhase(accept ? "approved" : "denied"); }; return (

Authorize this device

Confirm the code shown in your terminal.

{userCode || "XXXX-XXXX"}
{phase === "binding" &&

Checking the code…

} {phase === "ready" && ( <>

A signed-in device will be able to act as your account through the CLI.

{error &&

{error}

}
)} {phase === "approved" && (

Device approved. Return to your terminal.

)} {phase === "denied" && (

Request denied. You can close this window.

)} {phase === "error" &&

{error}

}
); };