oauth-callback 0.1.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,17 +5,23 @@
5
5
  [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/kriasoft/oauth-callback/blob/main/LICENSE)
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
7
7
 
8
- A lightweight local HTTP server for handling OAuth 2.0 authorization callbacks in Node.js, Deno, and Bun applications. Perfect for CLI tools, desktop applications, and development environments that need to capture OAuth authorization codes.
8
+ A lightweight OAuth 2.0 callback handler for Node.js, Deno, and Bun with built-in browser flow and MCP SDK integration. Perfect for CLI tools, desktop applications, and development environments that need to capture OAuth authorization codes.
9
+
10
+ <div align="center">
11
+ <img src="https://raw.githubusercontent.com/kriasoft/oauth-callback/main/examples/notion.gif" alt="OAuth Callback Demo" width="100%" style="max-width: 800px; height: auto;">
12
+ </div>
9
13
 
10
14
  ## Features
11
15
 
12
16
  - 🚀 **Multi-runtime support** - Works with Node.js 18+, Deno, and Bun
13
17
  - 🔒 **Secure localhost-only server** for OAuth callbacks
18
+ - 🤖 **MCP SDK integration** - Built-in OAuth provider for Model Context Protocol
14
19
  - ⚡ **Minimal dependencies** - Only requires `open` package
15
20
  - 🎯 **TypeScript support** out of the box
16
21
  - 🛡️ **Comprehensive OAuth error handling** with detailed error classes
17
22
  - 🔄 **Automatic server cleanup** after callback
18
- - 🎪 **Auto-closing success pages** with countdown timer
23
+ - 💾 **Flexible token storage** - In-memory and file-based options
24
+ - 🎪 **Clean success pages** with animated checkmark
19
25
  - 🎨 **Customizable HTML templates** with placeholder support
20
26
  - 🚦 **AbortSignal support** for programmatic cancellation
21
27
  - 📝 **Request logging and debugging** callbacks
@@ -36,13 +42,21 @@ npm install oauth-callback
36
42
  ## Quick Start
37
43
 
38
44
  ```typescript
39
- import { getAuthCode, OAuthError } from "oauth-callback";
45
+ import {
46
+ getAuthCode,
47
+ OAuthError,
48
+ browserAuth,
49
+ fileStore,
50
+ } from "oauth-callback";
40
51
 
41
52
  // Simple usage
42
53
  const result = await getAuthCode(
43
54
  "https://example.com/oauth/authorize?client_id=xxx&redirect_uri=http://localhost:3000/callback",
44
55
  );
45
56
  console.log("Authorization code:", result.code);
57
+
58
+ // MCP SDK integration
59
+ const authProvider = browserAuth({ store: fileStore() });
46
60
  ```
47
61
 
48
62
  ## Usage Examples
@@ -120,6 +134,71 @@ const microsoftAuth = await getAuthCode(
120
134
  );
121
135
  ```
122
136
 
137
+ ### MCP SDK Integration
138
+
139
+ The `browserAuth()` function provides a drop-in OAuth provider for the Model Context Protocol SDK:
140
+
141
+ ```typescript
142
+ import { browserAuth, inMemoryStore } from "oauth-callback";
143
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
144
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
145
+
146
+ // Create MCP-compatible OAuth provider
147
+ const authProvider = browserAuth({
148
+ port: 3000,
149
+ scope: "read write",
150
+ store: inMemoryStore(), // Or fileStore() for persistence
151
+ });
152
+
153
+ // Use with MCP SDK transport
154
+ const transport = new StreamableHTTPClientTransport(
155
+ new URL("https://mcp.notion.com/mcp"),
156
+ { authProvider },
157
+ );
158
+
159
+ const client = new Client(
160
+ { name: "my-app", version: "1.0.0" },
161
+ { capabilities: {} },
162
+ );
163
+
164
+ await client.connect(transport);
165
+ ```
166
+
167
+ #### Token Storage Options
168
+
169
+ ```typescript
170
+ import { browserAuth, inMemoryStore, fileStore } from "oauth-callback";
171
+
172
+ // Ephemeral storage (tokens lost on restart)
173
+ const ephemeralAuth = browserAuth({
174
+ store: inMemoryStore(),
175
+ });
176
+
177
+ // Persistent file storage (default: ~/.mcp/tokens.json)
178
+ const persistentAuth = browserAuth({
179
+ store: fileStore(),
180
+ storeKey: "my-app-tokens", // Namespace for multiple apps
181
+ });
182
+
183
+ // Custom file location
184
+ const customAuth = browserAuth({
185
+ store: fileStore("/path/to/tokens.json"),
186
+ });
187
+ ```
188
+
189
+ #### Pre-configured Client Credentials
190
+
191
+ If you have pre-registered OAuth client credentials:
192
+
193
+ ```typescript
194
+ const authProvider = browserAuth({
195
+ clientId: "your-client-id",
196
+ clientSecret: "your-client-secret",
197
+ scope: "read write",
198
+ store: fileStore(), // Persist tokens across sessions
199
+ });
200
+ ```
201
+
123
202
  ### Advanced Usage
124
203
 
125
204
  ```typescript
@@ -202,6 +281,50 @@ class OAuthError extends Error {
202
281
  }
203
282
  ```
204
283
 
284
+ ### `browserAuth(options)`
285
+
286
+ Creates an MCP SDK-compatible OAuth provider for browser-based flows. Handles Dynamic Client Registration (DCR), token storage, and automatic refresh.
287
+
288
+ #### Parameters
289
+
290
+ - `options` (BrowserAuthOptions): Configuration object with:
291
+ - `port` (number): Port for callback server (default: 3000)
292
+ - `hostname` (string): Hostname to bind to (default: "localhost")
293
+ - `callbackPath` (string): URL path for OAuth callback (default: "/callback")
294
+ - `scope` (string): OAuth scopes to request
295
+ - `clientId` (string): Pre-registered client ID (optional)
296
+ - `clientSecret` (string): Pre-registered client secret (optional)
297
+ - `store` (TokenStore): Token storage implementation (default: inMemoryStore())
298
+ - `storeKey` (string): Storage key for tokens (default: "mcp-tokens")
299
+ - `authTimeout` (number): Authorization timeout in ms (default: 300000)
300
+ - `successHtml` (string): Custom success page HTML
301
+ - `errorHtml` (string): Custom error page HTML
302
+ - `onRequest` (function): Request logging callback
303
+
304
+ #### Returns
305
+
306
+ OAuthClientProvider compatible with MCP SDK transports.
307
+
308
+ ### `inMemoryStore()`
309
+
310
+ Creates an ephemeral in-memory token store. Tokens are lost when the process exits.
311
+
312
+ #### Returns
313
+
314
+ TokenStore implementation for temporary token storage.
315
+
316
+ ### `fileStore(filepath?)`
317
+
318
+ Creates a persistent file-based token store.
319
+
320
+ #### Parameters
321
+
322
+ - `filepath` (string): Optional custom file path (default: `~/.mcp/tokens.json`)
323
+
324
+ #### Returns
325
+
326
+ TokenStore implementation for persistent token storage.
327
+
205
328
  ## How It Works
206
329
 
207
330
  1. **Server Creation**: Creates a temporary HTTP server on the specified port
@@ -239,9 +362,11 @@ The demo showcases:
239
362
  - Custom HTML templates for success/error pages
240
363
  - Token exchange and API usage simulation
241
364
 
242
- ### Real OAuth Example (GitHub)
365
+ ### Real OAuth Examples
243
366
 
244
- For testing with a real OAuth provider:
367
+ #### GitHub OAuth
368
+
369
+ For testing with GitHub OAuth:
245
370
 
246
371
  ```bash
247
372
  # Set up GitHub OAuth App credentials
@@ -259,6 +384,23 @@ This example demonstrates:
259
384
  - Exchanging the code for an access token
260
385
  - Using the token to fetch user information
261
386
 
387
+ #### Notion MCP with Dynamic Client Registration
388
+
389
+ For testing with Notion's Model Context Protocol server:
390
+
391
+ ```bash
392
+ # No credentials needed - uses Dynamic Client Registration!
393
+ bun run example:notion
394
+ ```
395
+
396
+ This example demonstrates:
397
+
398
+ - Dynamic Client Registration (OAuth 2.0 DCR) - no pre-configured client ID/secret needed
399
+ - Integration with Model Context Protocol (MCP) servers
400
+ - Automatic client registration with the authorization server
401
+ - Using `browserAuth()` provider with MCP SDK's `StreamableHTTPClientTransport`
402
+ - Token persistence with `inMemoryStore()` for ephemeral sessions
403
+
262
404
  ## Development
263
405
 
264
406
  ```bash
@@ -274,6 +416,7 @@ bun run build
274
416
  # Run examples
275
417
  bun run example:demo # Interactive demo
276
418
  bun run example:github # GitHub OAuth example
419
+ bun run example:notion # Notion MCP example with Dynamic Client Registration
277
420
  ```
278
421
 
279
422
  ## Requirements
@@ -314,6 +457,17 @@ Contributions are welcome! Please feel free to submit a Pull Request.
314
457
 
315
458
  This project is released under the MIT License. Feel free to use it in your projects, modify it to suit your needs, and share it with others. We believe in open source and hope this tool makes OAuth integration easier for everyone!
316
459
 
460
+ ## Related Projects
461
+
462
+ - [**MCP Client Generator**](https://github.com/kriasoft/mcp-client-gen) - Generate TypeScript clients from MCP server specifications. Perfect companion for building MCP-enabled applications with OAuth support ([npm](https://www.npmjs.com/package/mcp-client-gen)).
463
+ - [**React Starter Kit**](https://github.com/kriasoft/react-starter-kit) - Full-stack React application template with authentication, including OAuth integration examples.
464
+
465
+ ## Backers
466
+
467
+ Support this project by becoming a backer. Your logo will show up here with a link to your website.
468
+
469
+ <a href="https://reactstarter.com/b/1"><img src="https://reactstarter.com/b/1.png" height="60" /></a>&nbsp;&nbsp;<a href="https://reactstarter.com/b/2"><img src="https://reactstarter.com/b/2.png" height="60" /></a>&nbsp;&nbsp;<a href="https://reactstarter.com/b/3"><img src="https://reactstarter.com/b/3.png" height="60" /></a>&nbsp;&nbsp;<a href="https://reactstarter.com/b/4"><img src="https://reactstarter.com/b/4.png" height="60" /></a>&nbsp;&nbsp;<a href="https://reactstarter.com/b/5"><img src="https://reactstarter.com/b/5.png" height="60" /></a>&nbsp;&nbsp;<a href="https://reactstarter.com/b/6"><img src="https://reactstarter.com/b/6.png" height="60" /></a>&nbsp;&nbsp;<a href="https://reactstarter.com/b/7"><img src="https://reactstarter.com/b/7.png" height="60" /></a>&nbsp;&nbsp;<a href="https://reactstarter.com/b/8"><img src="https://reactstarter.com/b/8.png" height="60" /></a>
470
+
317
471
  ## Support
318
472
 
319
473
  Found a bug or have a question? Please open an issue on the [GitHub issue tracker](https://github.com/kriasoft/oauth-callback/issues) and we'll be happy to help. If this project saves you time and you'd like to support its continued development, consider [becoming a sponsor](https://github.com/sponsors/koistya). Every bit of support helps maintain and improve this tool for the community. Thank you!
@@ -0,0 +1,18 @@
1
+ import type { BrowserAuthOptions } from "../mcp-types";
2
+ import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
3
+ /**
4
+ * Factory for MCP SDK-compatible OAuth provider using browser flow.
5
+ *
6
+ * @param options Configuration for OAuth flow behavior
7
+ * @returns OAuthClientProvider for MCP SDK transport
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const transport = new StreamableHTTPClientTransport(
12
+ * new URL("https://mcp.notion.com/mcp"),
13
+ * { authProvider: browserAuth() }
14
+ * );
15
+ * ```
16
+ */
17
+ export declare function browserAuth(options?: BrowserAuthOptions): OAuthClientProvider;
18
+ //# sourceMappingURL=browser-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-auth.d.ts","sourceRoot":"","sources":["../../src/auth/browser-auth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,kBAAkB,EAMnB,MAAM,cAAc,CAAC;AAKtB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAQpF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CACzB,OAAO,GAAE,kBAAuB,GAC/B,mBAAmB,CAErB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=browser-auth.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-auth.test.d.ts","sourceRoot":"","sources":["../../src/auth/browser-auth.test.ts"],"names":[],"mappings":""}
package/dist/index.d.ts CHANGED
@@ -3,27 +3,26 @@ import type { GetAuthCodeOptions } from "./types";
3
3
  export type { CallbackResult, CallbackServer, ServerOptions } from "./server";
4
4
  export { OAuthError } from "./errors";
5
5
  export type { GetAuthCodeOptions } from "./types";
6
+ export { browserAuth } from "./auth/browser-auth";
7
+ export { inMemoryStore } from "./storage/memory";
8
+ export { fileStore } from "./storage/file";
9
+ export type { BrowserAuthOptions, Tokens, TokenStore } from "./mcp-types";
6
10
  /**
7
- * Get the OAuth authorization code from the authorization URL.
11
+ * Captures OAuth authorization code via localhost callback.
12
+ * Opens browser to auth URL, waits for provider redirect to localhost.
8
13
  *
9
- * Creates a temporary local HTTP server to handle the OAuth callback,
10
- * opens the authorization URL in the user's browser, and waits for the
11
- * OAuth provider to redirect back with an authorization code.
12
- *
13
- * @param input - Either a string containing the OAuth authorization URL,
14
- * or a GetAuthCodeOptions object with detailed configuration
15
- * @returns Promise that resolves to CallbackResult containing the authorization code
16
- * and other parameters, or rejects with OAuthError if authorization fails
17
- * @throws {OAuthError} When OAuth provider returns an error (e.g., access_denied)
18
- * @throws {Error} For timeout, network errors, or other unexpected failures
14
+ * @param input - Auth URL string or GetAuthCodeOptions with config
15
+ * @returns Promise<CallbackResult> with code and params
16
+ * @throws {OAuthError} Provider errors (access_denied, invalid_scope)
17
+ * @throws {Error} Timeout, network failures, port conflicts
19
18
  *
20
19
  * @example
21
20
  * ```typescript
22
- * // Simple usage with URL string
21
+ * // Simple
23
22
  * const result = await getAuthCode('https://oauth.example.com/authorize?...');
24
23
  * console.log('Code:', result.code);
25
24
  *
26
- * // Advanced usage with options
25
+ * // Custom port/timeout
27
26
  * const result = await getAuthCode({
28
27
  * authorizationUrl: 'https://oauth.example.com/authorize?...',
29
28
  * port: 8080,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAGlD,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9E,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,YAAY,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,kBAAkB,GAAG,MAAM,GACjC,OAAO,CAAC,cAAc,CAAC,CAqEzB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9E,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,YAAY,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAGlD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGlD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3C,YAAY,EAAE,kBAAkB,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,kBAAkB,GAAG,MAAM,GACjC,OAAO,CAAC,cAAc,CAAC,CA8DzB"}