UNPKG

pi-mcp-extension

Version:

Full-featured, lightweight MCP extension for the Pi coding agent: stdio, SSE & Streamable HTTP transports, OAuth 2.1 (PKCE + DCR), lazy/eager lifecycles — minimal dependencies, zero bloat.

82 lines (77 loc) • 2.55 kB
/** * Stored OAuth state inspection, credential reset, and token invalidation helpers. * * Owns stored OAuth state inspection (`getAuthStatus`), credential reset (`resetAuth`), * and best-effort token invalidation (`invalidateStoredTokens`). */ import { unlink } from "node:fs/promises"; import { acquireAuthLock, statePath, type AuthLockHandle } from "./auth-lock.js"; import { canonicalServerUrl } from "./oauth-policy.js"; import { loadState, saveState } from "./oauth-state-store.js"; /** * Best-effort invalidation of stored tokens on disk when a resource server * rejects an access token with HTTP 401/403. Preserves clientInfo and discoveryState. */ export async function invalidateStoredTokens( serverName: string, storageDir?: string, serverUrl?: string, ): Promise<void> { let handle: AuthLockHandle | undefined; try { handle = await acquireAuthLock(serverName, storageDir); const state = await loadState(serverName, storageDir, serverUrl); if (state.tokens !== undefined) { state.tokens = undefined; if (serverUrl) { state.serverUrl = canonicalServerUrl(serverUrl); } await saveState(serverName, state, storageDir); } } catch { // WHY: Best-effort token cleanup when a server rejects credentials; ignore lock contention or I/O errors. } finally { await handle?.(); } } /** * Get auth status info for a server — whether tokens exist, when they were saved, etc. * Returns null if no auth state file exists at all. */ export async function getAuthStatus( serverName: string, storageDir?: string, serverUrl?: string, ): Promise<{ hasTokens: boolean; hasClientInfo: boolean; savedAt: string | undefined; scope: string | undefined; } | null> { const state = await loadState(serverName, storageDir, serverUrl); if ( state.clientInfo === undefined && state.tokens === undefined && state.discoveryState === undefined ) { return null; } return { hasTokens: state.tokens !== undefined, hasClientInfo: state.clientInfo !== undefined, savedAt: state.tokens?.saved_at, scope: state.tokens?.scope, }; } /** * Reset all OAuth state for a server (tokens, client info, PKCE verifier, discovery). * Used to force re-authorization on next connection. */ export async function resetAuth(serverName: string, storageDir?: string): Promise<void> { try { await unlink(statePath(serverName, storageDir)); } catch (err) { if ((err as NodeJS.ErrnoException).code === "ENOENT") return; throw err; } }