Skip to content

v2.2.0

Latest

Choose a tag to compare

@koistya koistya released this 25 Jan 16:18
aa06439

OAuth Flow

  • Complete token exchange synchronously within redirectToAuthorization() — tokens are now available immediately after the first connect() attempt
  • Add CSRF protection via state parameter validation
  • New authServerUrl option for separate auth/token endpoint origins

Storage API (breaking)

  • Remove clear() from TokenStore interface
  • Add explicit deleteClient() and deleteCodeVerifier() to OAuthStore
  • Use symbol brand for type-safe OAuthStore detection vs duck typing
  • Atomic file writes via temp file + rename

Bug Fixes

  • invalidateCredentials('all') was clearing the entire store; now only clears the provider's own storeKey
  • Token expiry check was reading from store unnecessarily; now uses in-memory expiresAt

Usage

import { browserAuth, fileStore } from "oauth-callback/mcp";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const authProvider = browserAuth({
  launch: open,
  store: fileStore(),
});

const client = new Client({ name: "my-app", version: "1.0.0" }, { capabilities: {} });
const serverUrl = new URL("https://mcp.example.com/mcp");

// First attempt completes OAuth, second succeeds with stored tokens
const transport = new StreamableHTTPClientTransport(serverUrl, { authProvider });
try {
  await client.connect(transport);
} catch (error) {
  if (error.message === "Unauthorized") {
    await client.connect(new StreamableHTTPClientTransport(serverUrl, { authProvider }));
  } else throw error;
}

Full Changelog: v2.1.1...v2.2.0