import { AuthDebuggerState, OAuthStep } from "@/lib/auth-types"; import { CheckCircle2, Circle, ExternalLink } from "lucide-react"; import { Button } from "./ui/button"; import { DebugInspectorOAuthClientProvider } from "@/lib/auth"; import { useEffect, useMemo, useState } from "react"; import { OAuthClientInformation } from "@modelcontextprotocol/sdk/shared/auth.js"; interface OAuthStepProps { label: string; isComplete: boolean; isCurrent: boolean; error?: Error | null; children?: React.ReactNode; } const OAuthStepDetails = ({ label, isComplete, isCurrent, error, children, }: OAuthStepProps) => { return (
{isComplete ? ( ) : ( )} {label}
{/* Show children if current step or complete and children exist */} {(isCurrent || isComplete) && children && (
{children}
)} {/* Display error if current step and an error exists */} {isCurrent && error && (

Error:

{error.message}

)}
); }; interface OAuthFlowProgressProps { serverUrl: string; authState: AuthDebuggerState; updateAuthState: (updates: Partial) => void; proceedToNextStep: () => Promise; } const steps: Array = [ "metadata_discovery", "client_registration", "authorization_redirect", "authorization_code", "token_request", "complete", ]; export const OAuthFlowProgress = ({ serverUrl, authState, updateAuthState, proceedToNextStep, }: OAuthFlowProgressProps) => { const provider = useMemo( () => new DebugInspectorOAuthClientProvider(serverUrl), [serverUrl], ); const [clientInfo, setClientInfo] = useState( null, ); const currentStepIdx = steps.findIndex((s) => s === authState.oauthStep); useEffect(() => { const fetchClientInfo = async () => { if (authState.oauthClientInfo) { setClientInfo(authState.oauthClientInfo); } else { try { const info = await provider.clientInformation(); if (info) { setClientInfo(info); } } catch (error) { console.error("Failed to fetch client information:", error); } } }; if (currentStepIdx > steps.indexOf("client_registration")) { fetchClientInfo(); } }, [ provider, authState.oauthStep, authState.oauthClientInfo, currentStepIdx, ]); // Helper to get step props const getStepProps = (stepName: OAuthStep) => ({ isComplete: currentStepIdx > steps.indexOf(stepName) || currentStepIdx === steps.length - 1, // last step is "complete" isCurrent: authState.oauthStep === stepName, error: authState.oauthStep === stepName ? authState.latestError : null, }); return (

OAuth Flow Progress

Follow these steps to complete OAuth authentication with the server.

{authState.oauthMetadata && (
OAuth Metadata Sources {!authState.resourceMetadata && " ℹ️"} {authState.resourceMetadata && (

Resource Metadata:

From{" "} { new URL( "/.well-known/oauth-protected-resource", serverUrl, ).href }

                    {JSON.stringify(authState.resourceMetadata, null, 2)}
                  
)} {authState.resourceMetadataError && (

ℹ️ No resource metadata available from{" "} { new URL( "/.well-known/oauth-protected-resource", serverUrl, ).href }

Resource metadata was added in the{" "} 2025-DRAFT-v2 specification update
{authState.resourceMetadataError.message} {authState.resourceMetadataError instanceof TypeError && " (This could indicate the endpoint doesn't exist or does not have CORS configured)"}

)} {authState.oauthMetadata && (

Authorization Server Metadata:

{authState.authServerUrl && (

From{" "} { new URL( "/.well-known/oauth-authorization-server", authState.authServerUrl, ).href }

)}
                    {JSON.stringify(authState.oauthMetadata, null, 2)}
                  
)}
)}
{clientInfo && (
Registered Client Information
                {JSON.stringify(clientInfo, null, 2)}
              
)}
{authState.authorizationUrl && (

Authorization URL:

{authState.authorizationUrl}

Click the link to authorize in your browser. After authorization, you'll be redirected back to continue the flow.

)}
{ updateAuthState({ authorizationCode: e.target.value, validationError: null, }); }} placeholder="Enter the code from the authorization server" className={`flex h-9 w-full rounded-md border bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${ authState.validationError ? "border-red-500" : "border-input" }`} />
{authState.validationError && (

{authState.validationError}

)}

Once you've completed authorization in the link, paste the code here.

{authState.oauthMetadata && (
Token Request Details

Token Endpoint:

{authState.oauthMetadata.token_endpoint}
)}
{authState.oauthTokens && (
Access Tokens

Authentication successful! You can now use the authenticated connection. These tokens will be used automatically for server requests.

                {JSON.stringify(authState.oauthTokens, null, 2)}
              
)}
{authState.oauthStep !== "complete" && ( <> )} {authState.oauthStep === "authorization_redirect" && authState.authorizationUrl && ( )}
); };