import React, { useEffect, useState } from 'react'; import { Brain, Bot, AlertCircle, ExternalLink } from 'lucide-react'; import { ApiKeyScope, OAuthAuthorizeInfo, User } from '../../lib/types'; import { SCOPES, SCOPE_GROUPS } from '../../lib/apiScopes'; import { api, triggerHaptic } from '../../lib/api'; import { ToggleSwitch } from '../../components/ToggleSwitch'; interface OAuthConsentViewProps { user: User; instanceName: string; onLogout: () => void; } // Consent page of the OAuth flow (/oauth/authorize): an AI client such as claude.ai wants to use // MindSift over MCP. The person picks the rights; the server answers with the URL to return to the // client (with a code or access_denied). Shown instead of the app while the address is /oauth/authorize. export const OAuthConsentView: React.FC = ({ user, instanceName, onLogout }) => { const query = window.location.search; const [info, setInfo] = useState(null); const [scopes, setScopes] = useState([]); const [error, setError] = useState(null); const [busy, setBusy] = useState(false); useEffect(() => { api .getOAuthAuthorizeInfo(query) .then((res) => { // Invalid request the client must hear about: straight back if (res.redirect) { window.location.replace(res.redirect); return; } setInfo(res); setScopes(res.scopes || []); }) .catch((err) => setError(err?.message || 'Die Anfrage ist ungültig.')); }, [query]); const toggle = (id: ApiKeyScope) => setScopes((prev) => (prev.includes(id) ? prev.filter((s) => s !== id) : [...prev, id])); const answer = async (approve: boolean) => { triggerHaptic(15); setBusy(true); setError(null); try { const res = await api.answerOAuthAuthorize(query, approve, scopes); window.location.replace(res.redirect); } catch (err: any) { setError(err?.message || 'Das hat nicht geklappt.'); setBusy(false); } }; const offered = info?.offeredScopes || []; const groups = SCOPE_GROUPS.filter(([group]) => SCOPES.some((sc) => sc.group === group && offered.includes(sc.id))); return (

{instanceName}

{!info && !error &&

Anfrage wird geprüft…

} {error && (
{error}
)} {info && ( <>

{info.clientName} möchte auf dein MindSift zugreifen

Angemeldet als {user.displayName || user.email}. Freigaben gelten wie für dich selbst; Einstellungen bleiben gesperrt.

{groups.map(([group, heading]) => (

{heading}

{SCOPES.filter((sc) => sc.group === group && offered.includes(sc.id)).map((sc) => (
{sc.label} {sc.hint}
toggle(sc.id)} title={sc.chip} />
))}
))}

Danach geht es zurück zu {info.redirectHost}. Die Verbindung steht unter Einstellungen → API-Schlüssel und lässt sich dort jederzeit widerrufen.

)}
); };