// Local verification of the OAuth provider's durable mechanics (no external IdP): // the KeychainOAuthProvider must round-trip tokens, client info, and the PKCE // verifier through the macOS Keychain so a sign-in survives bridge restarts. import { KeychainOAuthProvider } from "../src/oauth.js"; import { kcDelete } from "../src/keychain.js"; const ID = "oauth-selftest"; const p = new KeychainOAuthProvider(ID, "drive.readonly"); let failures = 0; const check = (label: string, cond: boolean) => { console.log(`${cond ? "ok " : "FAIL"} ${label}`); if (!cond) failures++; }; // metadata + redirect shape check("redirectUrl points at loopback callback with id", p.redirectUrl.includes("/mcp/oauth/callback?id=oauth-selftest")); check("clientMetadata is PKCE public client", p.clientMetadata.token_endpoint_auth_method === "none" && p.clientMetadata.response_types.includes("code")); check("state carries serverId", p.state() === ID); // tokens round-trip through Keychain await p.saveTokens({ access_token: "tok-abc", token_type: "bearer", expires_in: 3600 }); const t = await p.tokens(); check("tokens persisted + read back", t?.access_token === "tok-abc"); check("hasTokens true after save", await p.hasTokens()); // client info (dynamic registration) round-trip await p.saveClientInformation({ client_id: "cid-1", redirect_uris: [p.redirectUrl] } as any); const ci = await p.clientInformation(); check("client info persisted + read back", (ci as any)?.client_id === "cid-1"); // PKCE verifier round-trip await p.saveCodeVerifier("verifier-xyz"); check("code verifier persisted + read back", (await p.codeVerifier()) === "verifier-xyz"); // invalidate clears everything await p.invalidateCredentials("all"); check("invalidate(all) clears tokens", !(await p.hasTokens())); // cleanup any stragglers await kcDelete(`${ID}:oauth`); await kcDelete(`${ID}:client`); await kcDelete(`${ID}:verifier`); console.log(failures === 0 ? "\nALL OAUTH MECHANICS OK" : `\n${failures} FAILURE(S)`); process.exit(failures === 0 ? 0 : 1);