oauth-callback 1.2.5 → 2.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
@@ -31,33 +31,38 @@ A lightweight OAuth 2.0 callback handler for Node.js, Deno, and Bun with built-i
31
31
  ## Installation
32
32
 
33
33
  ```bash
34
- bun add oauth-callback
34
+ bun add oauth-callback open
35
35
  ```
36
36
 
37
37
  Or with npm:
38
38
 
39
39
  ```bash
40
- npm install oauth-callback
40
+ npm install oauth-callback open
41
41
  ```
42
42
 
43
+ > **Note:** The `open` package is optional but recommended for browser launching. If using pnpm, install it explicitly: `pnpm add open`
44
+
43
45
  ## Quick Start
44
46
 
45
47
  ```typescript
48
+ import open from "open";
46
49
  import { getAuthCode, OAuthError } from "oauth-callback";
47
50
 
48
- // Simple usage
49
- const result = await getAuthCode(
50
- "https://example.com/oauth/authorize?client_id=xxx&redirect_uri=http://localhost:3000/callback",
51
- );
51
+ // Simple usage - pass `open` to launch browser
52
+ const result = await getAuthCode({
53
+ authorizationUrl:
54
+ "https://example.com/oauth/authorize?client_id=xxx&redirect_uri=http://localhost:3000/callback",
55
+ launch: open,
56
+ });
52
57
  console.log("Authorization code:", result.code);
53
58
 
54
59
  // MCP SDK integration - use specific import
55
60
  import { browserAuth, fileStore } from "oauth-callback/mcp";
56
- const authProvider = browserAuth({ store: fileStore() });
61
+ const authProvider = browserAuth({ launch: open, store: fileStore() });
57
62
 
58
63
  // Or via namespace import
59
64
  import { mcp } from "oauth-callback";
60
- const authProvider = mcp.browserAuth({ store: mcp.fileStore() });
65
+ const authProvider = mcp.browserAuth({ launch: open, store: mcp.fileStore() });
61
66
  ```
62
67
 
63
68
  ## Usage Examples
@@ -65,6 +70,7 @@ const authProvider = mcp.browserAuth({ store: mcp.fileStore() });
65
70
  ### Basic OAuth Flow
66
71
 
67
72
  ```typescript
73
+ import open from "open";
68
74
  import { getAuthCode, OAuthError } from "oauth-callback";
69
75
 
70
76
  async function authenticate() {
@@ -78,7 +84,10 @@ async function authenticate() {
78
84
  });
79
85
 
80
86
  try {
81
- const result = await getAuthCode(authUrl);
87
+ const result = await getAuthCode({
88
+ authorizationUrl: authUrl,
89
+ launch: open,
90
+ });
82
91
  console.log("Authorization code:", result.code);
83
92
  console.log("State:", result.state);
84
93
 
@@ -98,10 +107,12 @@ async function authenticate() {
98
107
  ### Custom Port Configuration
99
108
 
100
109
  ```typescript
110
+ import open from "open";
101
111
  import { getAuthCode } from "oauth-callback";
102
112
 
103
113
  const result = await getAuthCode({
104
114
  authorizationUrl: authUrl,
115
+ launch: open,
105
116
  port: 8080, // Use custom port (default: 3000)
106
117
  timeout: 60000, // Custom timeout in ms (default: 30000)
107
118
  });
@@ -120,6 +131,7 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
120
131
  const authProvider = browserAuth({
121
132
  port: 3000,
122
133
  scope: "read write",
134
+ launch: open, // Opens browser for OAuth consent
123
135
  store: inMemoryStore(), // Or fileStore() for persistence
124
136
  });
125
137
 
@@ -144,17 +156,20 @@ import { browserAuth, inMemoryStore, fileStore } from "oauth-callback/mcp";
144
156
 
145
157
  // Ephemeral storage (tokens lost on restart)
146
158
  const ephemeralAuth = browserAuth({
159
+ launch: open,
147
160
  store: inMemoryStore(),
148
161
  });
149
162
 
150
163
  // Persistent file storage (default: ~/.mcp/tokens.json)
151
164
  const persistentAuth = browserAuth({
165
+ launch: open,
152
166
  store: fileStore(),
153
167
  storeKey: "my-app-tokens", // Namespace for multiple apps
154
168
  });
155
169
 
156
170
  // Custom file location
157
171
  const customAuth = browserAuth({
172
+ launch: open,
158
173
  store: fileStore("/path/to/tokens.json"),
159
174
  });
160
175
  ```
@@ -168,6 +183,7 @@ const authProvider = browserAuth({
168
183
  clientId: "your-client-id",
169
184
  clientSecret: "your-client-secret",
170
185
  scope: "read write",
186
+ launch: open, // Opens browser for OAuth consent
171
187
  store: fileStore(), // Persist tokens across sessions
172
188
  });
173
189
  ```
@@ -175,9 +191,12 @@ const authProvider = browserAuth({
175
191
  ### Advanced Usage
176
192
 
177
193
  ```typescript
194
+ import open from "open";
195
+
178
196
  // With custom HTML templates and logging
179
197
  const result = await getAuthCode({
180
198
  authorizationUrl: authUrl,
199
+ launch: open,
181
200
  port: 3000,
182
201
  hostname: "127.0.0.1", // Bind to specific IP
183
202
  successHtml: "<h1>Success! You can close this window.</h1>",
@@ -209,7 +228,7 @@ try {
209
228
 
210
229
  ### `getAuthCode(input)`
211
230
 
212
- Starts a local HTTP server and opens the authorization URL in the user's browser.
231
+ Starts a local HTTP server to capture OAuth callbacks. Optionally launches the authorization URL via the `launch` callback.
213
232
 
214
233
  #### Parameters
215
234
 
@@ -219,7 +238,7 @@ Starts a local HTTP server and opens the authorization URL in the user's browser
219
238
  - `hostname` (string): Hostname to bind the server to (default: "localhost")
220
239
  - `callbackPath` (string): URL path for the OAuth callback (default: "/callback")
221
240
  - `timeout` (number): Timeout in milliseconds (default: 30000)
222
- - `openBrowser` (boolean): Whether to open browser automatically (default: true)
241
+ - `launch` (function): Optional callback to launch the authorization URL (e.g., `open`)
223
242
  - `successHtml` (string): Custom HTML to display on successful authorization
224
243
  - `errorHtml` (string): Custom HTML to display on authorization error
225
244
  - `signal` (AbortSignal): AbortSignal for cancellation support
@@ -269,6 +288,7 @@ Available from `oauth-callback/mcp`. Creates an MCP SDK-compatible OAuth provide
269
288
  - `clientSecret` (string): Pre-registered client secret (optional)
270
289
  - `store` (TokenStore): Token storage implementation (default: inMemoryStore())
271
290
  - `storeKey` (string): Storage key for tokens (default: "mcp-tokens")
291
+ - `launch` (function): Callback to launch auth URL (e.g., `open`)
272
292
  - `authTimeout` (number): Authorization timeout in ms (default: 300000)
273
293
  - `successHtml` (string): Custom success page HTML
274
294
  - `errorHtml` (string): Custom error page HTML
@@ -311,7 +331,7 @@ TokenStore implementation for persistent token storage.
311
331
  ```
312
332
 
313
333
  1. **Server Creation** — Spins up a temporary localhost HTTP server
314
- 2. **Browser Launch** — Opens the authorization URL in the default browser
334
+ 2. **Browser Launch** — Opens the authorization URL (if `launch` callback provided)
315
335
  3. **User Authorization** — User grants permission on the OAuth provider's page
316
336
  4. **Callback Capture** — Provider redirects to localhost with the authorization code
317
337
  5. **Cleanup** — Server closes automatically, code is returned to your app
@@ -420,7 +440,11 @@ bun run example:notion # Notion MCP example with Dynamic Client Registration
420
440
  If port 3000 is already in use, specify a different port:
421
441
 
422
442
  ```typescript
423
- const result = await getAuthCode({ authorizationUrl: authUrl, port: 8080 });
443
+ const result = await getAuthCode({
444
+ authorizationUrl: authUrl,
445
+ launch: open,
446
+ port: 8080,
447
+ });
424
448
  ```
425
449
 
426
450
  ### Firewall Warnings
@@ -433,13 +457,9 @@ If the browser doesn't open automatically, manually navigate to the authorizatio
433
457
 
434
458
  ## Contributing
435
459
 
436
- Contributions are welcome! Please feel free to submit a Pull Request.
460
+ Contributions are welcome! See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for setup instructions.
437
461
 
438
- 1. Fork the repository
439
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
440
- 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
441
- 4. Push to the branch (`git push origin feature/amazing-feature`)
442
- 5. Open a Pull Request
462
+ **Maintainers wanted** — we're looking for people to help maintain this project. If interested, reach out on [Discord](https://discord.gg/bSsv7XM) or open an issue.
443
463
 
444
464
  ## License
445
465
 
@@ -8,9 +8,11 @@ import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.
8
8
  *
9
9
  * @example
10
10
  * ```typescript
11
+ * import open from "open";
12
+ *
11
13
  * const transport = new StreamableHTTPClientTransport(
12
14
  * new URL("https://mcp.notion.com/mcp"),
13
- * { authProvider: browserAuth() }
15
+ * { authProvider: browserAuth({ launch: open }) }
14
16
  * );
15
17
  * ```
16
18
  */
@@ -1 +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"}
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;AAItB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAQpF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CACzB,OAAO,GAAE,kBAAuB,GAC/B,mBAAmB,CAErB"}
package/dist/index.d.ts CHANGED
@@ -1,34 +1,58 @@
1
1
  import { type CallbackResult } from "./server";
2
2
  import type { GetAuthCodeOptions } from "./types";
3
3
  export type { CallbackResult, CallbackServer, ServerOptions } from "./server";
4
- export { OAuthError } from "./errors";
4
+ export { OAuthError, TimeoutError } from "./errors";
5
5
  export type { GetAuthCodeOptions } from "./types";
6
6
  export { inMemoryStore } from "./storage/memory";
7
7
  export { fileStore } from "./storage/file";
8
8
  import * as mcp from "./mcp";
9
9
  export { mcp };
10
+ /**
11
+ * Builds the redirect URI for OAuth configuration.
12
+ * Use this to construct the redirect_uri parameter for your authorization URL.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const redirectUri = getRedirectUrl({ port: 3000 });
17
+ * // => "http://localhost:3000/callback"
18
+ *
19
+ * const authUrl = `https://oauth.example.com/authorize?redirect_uri=${encodeURIComponent(redirectUri)}`;
20
+ * console.log('Open:', authUrl);
21
+ * await getAuthCode({ port: 3000 });
22
+ * ```
23
+ */
24
+ export declare function getRedirectUrl(options?: {
25
+ port?: number;
26
+ hostname?: string;
27
+ callbackPath?: string;
28
+ }): string;
10
29
  /**
11
30
  * Captures OAuth authorization code via localhost callback.
12
- * Opens browser to auth URL, waits for provider redirect to localhost.
31
+ * Starts a temporary server, optionally launches auth URL, waits for redirect.
13
32
  *
14
- * @param input - Auth URL string or GetAuthCodeOptions with config
33
+ * Two modes:
34
+ * - **Managed**: Pass both `authorizationUrl` and `launch` — library opens browser
35
+ * - **Headless**: Pass neither — caller handles URL display (CI/SSH/custom UI)
36
+ *
37
+ * @param input - Auth URL string (auto-launches browser) or GetAuthCodeOptions
15
38
  * @returns Promise<CallbackResult> with code and params
16
39
  * @throws {OAuthError} Provider errors (access_denied, invalid_scope)
17
40
  * @throws {Error} Timeout, network failures, port conflicts
18
41
  *
19
42
  * @example
20
43
  * ```typescript
21
- * // Simple
22
- * const result = await getAuthCode('https://oauth.example.com/authorize?...');
23
- * console.log('Code:', result.code);
44
+ * import open from "open";
24
45
  *
25
- * // Custom port/timeout
46
+ * // Managed mode: library launches browser
26
47
  * const result = await getAuthCode({
27
48
  * authorizationUrl: 'https://oauth.example.com/authorize?...',
28
- * port: 8080,
29
- * timeout: 60000,
30
- * onRequest: (req) => console.log('Request:', req.url)
49
+ * launch: open,
31
50
  * });
51
+ *
52
+ * // Headless mode: caller handles URL display
53
+ * const authUrl = 'https://oauth.example.com/authorize?...';
54
+ * console.log('Open this URL:', authUrl);
55
+ * const result = await getAuthCode({ port: 3000, timeout: 60000 });
32
56
  * ```
33
57
  */
34
58
  export declare function getAuthCode(input: GetAuthCodeOptions | string): Promise<CallbackResult>;
@@ -1 +1 @@
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,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3C,OAAO,KAAK,GAAG,MAAM,OAAO,CAAC;AAC7B,OAAO,EAAE,GAAG,EAAE,CAAC;AAEf;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,kBAAkB,GAAG,MAAM,GACjC,OAAO,CAAC,cAAc,CAAC,CA8DzB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,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,YAAY,EAAE,MAAM,UAAU,CAAC;AACpD,YAAY,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAGlD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3C,OAAO,KAAK,GAAG,MAAM,OAAO,CAAC;AAC7B,OAAO,EAAE,GAAG,EAAE,CAAC;AAEf;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,OAAO,GAAE;IACP,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CAClB,GACL,MAAM,CAOR;AASD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,kBAAkB,GAAG,MAAM,GACjC,OAAO,CAAC,cAAc,CAAC,CAmDzB"}