"use client"; import { useState, useEffect } from "react"; import PropTypes from "prop-types"; import { Modal, Button, Input } from "@/shared/components"; import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard"; /** * Kiro Social OAuth Modal (Google/GitHub) * Handles manual callback URL flow for social login */ export default function KiroSocialOAuthModal({ isOpen, provider, onSuccess, onClose }) { const [step, setStep] = useState("loading"); // loading | input | success | error const [authUrl, setAuthUrl] = useState(""); const [authData, setAuthData] = useState(null); const [callbackUrl, setCallbackUrl] = useState(""); const [error, setError] = useState(null); const { copied, copy } = useCopyToClipboard(); // Initialize auth flow useEffect(() => { if (!isOpen || !provider) return; const initAuth = async () => { try { setError(null); setStep("loading"); const res = await fetch(`/api/oauth/kiro/social-authorize?provider=${provider}`); const data = await res.json(); if (!res.ok) { throw new Error(data.error); } setAuthData(data); setAuthUrl(data.authUrl); setStep("input"); // Auto-open browser window.open(data.authUrl, "_blank"); } catch (err) { setError(err.message); setStep("error"); } }; initAuth(); }, [isOpen, provider]); const handleManualSubmit = async () => { try { setError(null); // Parse callback URL - can be either kiro:// or http://localhost format let url; try { url = new URL(callbackUrl); } catch (e) { // If URL parsing fails, might be malformed throw new Error("Invalid callback URL format"); } const code = url.searchParams.get("code"); const state = url.searchParams.get("state"); const errorParam = url.searchParams.get("error"); if (errorParam) { throw new Error(url.searchParams.get("error_description") || errorParam); } if (!code) { throw new Error("No authorization code found in URL"); } // Exchange code for tokens const res = await fetch("/api/oauth/kiro/social-exchange", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ code, codeVerifier: authData.codeVerifier, provider, }), }); const data = await res.json(); if (!res.ok) throw new Error(data.error); setStep("success"); onSuccess?.(); } catch (err) { setError(err.message); setStep("error"); } }; const providerName = provider === "google" ? "Google" : "GitHub"; return (
{/* Loading */} {step === "loading" && (
progress_activity

Initializing...

Setting up {providerName} authentication

)} {/* Manual Input Step */} {step === "input" && ( <>

Step 1: Open this URL in your browser

Step 2: Paste the callback URL here

After authorization, copy the full URL from your browser address bar.

setCallbackUrl(e.target.value)} placeholder="kiro://kiro.kiroAgent/authenticate-success?code=..." className="font-mono text-xs" />
)} {/* Success */} {step === "success" && (
check_circle

Connected Successfully!

Your Kiro account via {providerName} has been connected.

)} {/* Error */} {step === "error" && (
error

Connection Failed

{error}

)}
); } KiroSocialOAuthModal.propTypes = { isOpen: PropTypes.bool.isRequired, provider: PropTypes.oneOf(["google", "github"]).isRequired, onSuccess: PropTypes.func, onClose: PropTypes.func.isRequired, };