"use client"; import { useState } from "react"; import PropTypes from "prop-types"; import { Modal, Button, Input, OAuthModal } from "@/shared/components"; const GITLAB_COM = "https://gitlab.com"; function getRedirectUri() { if (typeof window === "undefined") return "http://localhost/callback"; const port = window.location.port || (window.location.protocol === "https:" ? "443" : "80"); return `http://localhost:${port}/callback`; } /** * GitLab Duo Authentication Modal * Supports two modes: * - OAuth (PKCE): requires OAuth App Client ID (and optional Client Secret) * - PAT: requires Personal Access Token */ export default function GitLabAuthModal({ isOpen, providerInfo, onSuccess, onClose }) { const [mode, setMode] = useState(null); // null | "oauth" | "pat" const [baseUrl, setBaseUrl] = useState(GITLAB_COM); const [clientId, setClientId] = useState(""); const [clientSecret, setClientSecret] = useState(""); const [pat, setPat] = useState(""); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [showOAuth, setShowOAuth] = useState(false); const [oauthMeta, setOauthMeta] = useState(null); const reset = () => { setMode(null); setBaseUrl(GITLAB_COM); setClientId(""); setClientSecret(""); setPat(""); setError(null); setLoading(false); setShowOAuth(false); setOauthMeta(null); }; const handleClose = () => { reset(); onClose(); }; const handleOAuthStart = () => { if (!clientId.trim()) { setError("Client ID is required"); return; } setError(null); setOauthMeta({ baseUrl: baseUrl.trim() || GITLAB_COM, clientId: clientId.trim(), clientSecret: clientSecret.trim() }); setShowOAuth(true); }; const handlePATSubmit = async () => { if (!pat.trim()) { setError("Personal Access Token is required"); return; } setLoading(true); setError(null); try { const res = await fetch("/api/oauth/gitlab/pat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ token: pat.trim(), baseUrl: baseUrl.trim() || GITLAB_COM }), }); const data = await res.json(); if (!res.ok) throw new Error(data.error || "Authentication failed"); onSuccess?.(); handleClose(); } catch (err) { setError(err.message); } finally { setLoading(false); } }; if (!isOpen) return null; // Sub-modal for OAuth PKCE flow if (showOAuth && oauthMeta) { return ( { onSuccess?.(); handleClose(); }} onClose={() => { setShowOAuth(false); setOauthMeta(null); }} /> ); } return (
{/* Mode selection */} {!mode && ( <>

Choose how to authenticate with GitLab Duo:

)} {/* OAuth mode */} {mode === "oauth" && ( <>

Create an OAuth app at{" "} GitLab Applications {" "} with redirect URI{" "} {getRedirectUri()}

setBaseUrl(e.target.value)} placeholder={GITLAB_COM} /> setClientId(e.target.value)} placeholder="Your OAuth application client ID" /> setClientSecret(e.target.value)} placeholder="Leave empty for public PKCE app" /> {error &&

{error}

}
)} {/* PAT mode */} {mode === "pat" && ( <>

Create a PAT at{" "} GitLab Access Tokens {" "} with scopes: api,{" "} read_user, and{" "} ai_features.

setBaseUrl(e.target.value)} placeholder={GITLAB_COM} /> setPat(e.target.value)} placeholder="glpat-xxxxxxxxxxxxxxxxxxxx" type="password" /> {error &&

{error}

}
)}
); } GitLabAuthModal.propTypes = { isOpen: PropTypes.bool.isRequired, providerInfo: PropTypes.shape({ name: PropTypes.string }), onSuccess: PropTypes.func, onClose: PropTypes.func.isRequired, };