import { getPlugin } from '@/lib/admin/plugin-registry'; import { listApprovals } from '@/lib/admin/plugin-approvals'; import type { CredentialSpec, UserCredentialSpec } from './credentials'; /** * The user-scope credential `credentialId` as the manifest of `pluginId` * declares it, when this server knows that manifest: an installed (registry) * plugin, or a user-uploaded package an admin approved. * * The connect dialog and the proxy used to take the plugin id at its word, * so any signed-in user could store a credential under a made-up plugin id * and relay requests through the server to any origin with it, and a * plugin could reach for a credential declared by none. A credential is * only ever for a plugin the server can name, and only on the server the * manifest fixes, when it fixes one. */ export async function declaredUserCredential(pluginId: string, credentialId: string): Promise { const plugin = await getPlugin(pluginId); const fromRegistry = (plugin as { credentials?: Record } | null)?.credentials?.[credentialId]; if (plugin && plugin.enabled && fromRegistry?.scope === 'user') return fromRegistry; if (plugin) return null; for (const entry of await listApprovals()) { if (entry.pluginId !== pluginId || entry.status !== 'approved') continue; const spec = entry.manifest.credentials?.[credentialId]; if (spec?.scope === 'user') return spec; } return null; }