@leanmcp/auth 0.3.1 → 0.4.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.
@@ -0,0 +1,499 @@
1
+ import { T as TokenStorage, O as OAuthTokens } from '../types-DMpGN530.js';
2
+
3
+ /**
4
+ * PKCE (Proof Key for Code Exchange) utilities for OAuth 2.1
5
+ *
6
+ * RFC 7636: https://tools.ietf.org/html/rfc7636
7
+ *
8
+ * PKCE protects against authorization code interception attacks
9
+ * by using a cryptographic challenge during the OAuth flow.
10
+ */
11
+ /**
12
+ * PKCE code challenge and verifier pair
13
+ */
14
+ interface PKCEPair {
15
+ /** High-entropy random string (43-128 chars) */
16
+ verifier: string;
17
+ /** SHA256 hash of verifier, base64url encoded */
18
+ challenge: string;
19
+ /** Challenge method - always S256 for security */
20
+ method: 'S256';
21
+ }
22
+ /**
23
+ * Generate a cryptographically secure PKCE code verifier.
24
+ *
25
+ * The verifier is a high-entropy random string between 43-128 characters
26
+ * using unreserved characters: [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
27
+ *
28
+ * @param length - Length of the verifier (default: 64, min: 43, max: 128)
29
+ * @returns Base64url-encoded random string
30
+ */
31
+ declare function generateCodeVerifier(length?: number): string;
32
+ /**
33
+ * Generate a PKCE code challenge from a code verifier.
34
+ *
35
+ * Uses the S256 method: BASE64URL(SHA256(verifier))
36
+ *
37
+ * @param verifier - The code verifier string
38
+ * @returns Base64url-encoded SHA256 hash
39
+ */
40
+ declare function generateCodeChallenge(verifier: string): string;
41
+ /**
42
+ * Generate a complete PKCE pair (verifier and challenge).
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * const pkce = generatePKCE();
47
+ * // Send challenge in authorization request
48
+ * const authUrl = `${authorizationEndpoint}?code_challenge=${pkce.challenge}&code_challenge_method=S256`;
49
+ * // Send verifier in token request
50
+ * const tokenPayload = { code_verifier: pkce.verifier, ... };
51
+ * ```
52
+ *
53
+ * @param verifierLength - Length of the verifier (default: 64)
54
+ * @returns PKCE pair with verifier, challenge, and method
55
+ */
56
+ declare function generatePKCE(verifierLength?: number): PKCEPair;
57
+ /**
58
+ * Verify that a code verifier matches a code challenge.
59
+ *
60
+ * Used server-side to validate PKCE during token exchange.
61
+ *
62
+ * @param verifier - The code verifier from token request
63
+ * @param challenge - The code challenge from authorization request
64
+ * @param method - The challenge method (only 'S256' supported)
65
+ * @returns True if verifier matches challenge
66
+ */
67
+ declare function verifyPKCE(verifier: string, challenge: string, method?: 'S256' | 'plain'): boolean;
68
+ /**
69
+ * Validate that a string is a valid PKCE code verifier.
70
+ *
71
+ * @param verifier - String to validate
72
+ * @returns True if valid verifier format
73
+ */
74
+ declare function isValidCodeVerifier(verifier: string): boolean;
75
+
76
+ /**
77
+ * OAuth 2.1 Client for MCP
78
+ *
79
+ * Handles browser-based OAuth flows with PKCE support.
80
+ * Compatible with MCP servers and external OAuth providers.
81
+ */
82
+
83
+ /**
84
+ * OAuth client configuration
85
+ */
86
+ interface OAuthClientOptions {
87
+ /** MCP server URL or OAuth authorization server URL */
88
+ serverUrl: string;
89
+ /** OAuth scopes to request */
90
+ scopes?: string[];
91
+ /** Client name for dynamic registration */
92
+ clientName?: string;
93
+ /** Token storage backend */
94
+ storage?: TokenStorage;
95
+ /** Pre-configured client credentials (skip dynamic registration) */
96
+ clientId?: string;
97
+ clientSecret?: string;
98
+ /** Custom OAuth endpoints (auto-discovered if not provided) */
99
+ authorizationEndpoint?: string;
100
+ tokenEndpoint?: string;
101
+ registrationEndpoint?: string;
102
+ /** Enable PKCE (default: true) */
103
+ pkceEnabled?: boolean;
104
+ /** Automatically refresh tokens before expiry (default: true) */
105
+ autoRefresh?: boolean;
106
+ /** Seconds before expiry to trigger refresh (default: 60) */
107
+ refreshBuffer?: number;
108
+ /** Callback server port (default: auto) */
109
+ callbackPort?: number;
110
+ /** OAuth timeout in ms (default: 5 minutes) */
111
+ timeout?: number;
112
+ }
113
+ /**
114
+ * OAuth 2.1 client with PKCE and browser-based authentication
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * const client = new OAuthClient({
119
+ * serverUrl: 'https://mcp.example.com',
120
+ * scopes: ['read', 'write'],
121
+ * });
122
+ *
123
+ * // Start browser-based OAuth flow
124
+ * await client.authenticate();
125
+ *
126
+ * // Get token for API calls
127
+ * const token = await client.getValidToken();
128
+ * ```
129
+ */
130
+ declare class OAuthClient {
131
+ private serverUrl;
132
+ private scopes;
133
+ private clientName;
134
+ private storage;
135
+ private pkceEnabled;
136
+ private autoRefresh;
137
+ private refreshBuffer;
138
+ private callbackPort?;
139
+ private timeout;
140
+ private authorizationEndpoint?;
141
+ private tokenEndpoint?;
142
+ private registrationEndpoint?;
143
+ private preConfiguredClientId?;
144
+ private preConfiguredClientSecret?;
145
+ private pendingRefresh?;
146
+ private metadata?;
147
+ constructor(options: OAuthClientOptions);
148
+ /**
149
+ * Discover OAuth metadata from .well-known endpoint
150
+ */
151
+ private discoverMetadata;
152
+ /**
153
+ * Get or register OAuth client credentials
154
+ */
155
+ private getClientCredentials;
156
+ /**
157
+ * Start the browser-based OAuth flow
158
+ *
159
+ * Opens the user's browser to the authorization URL and waits for the callback.
160
+ *
161
+ * @returns OAuth tokens
162
+ */
163
+ authenticate(): Promise<OAuthTokens>;
164
+ /**
165
+ * Open URL in browser
166
+ */
167
+ private openBrowser;
168
+ /**
169
+ * Exchange authorization code for tokens
170
+ */
171
+ private exchangeCodeForTokens;
172
+ /**
173
+ * Refresh the access token using the refresh token
174
+ */
175
+ refreshTokens(): Promise<OAuthTokens>;
176
+ private doRefreshTokens;
177
+ /**
178
+ * Get a valid access token, refreshing if necessary
179
+ */
180
+ getValidToken(): Promise<string>;
181
+ /**
182
+ * Get current tokens (may be expired)
183
+ */
184
+ getTokens(): Promise<OAuthTokens | null>;
185
+ /**
186
+ * Check if we have valid (non-expired) tokens
187
+ */
188
+ isAuthenticated(): Promise<boolean>;
189
+ /**
190
+ * Clear stored tokens and log out
191
+ */
192
+ logout(): Promise<void>;
193
+ /**
194
+ * Create an auth handler for HTTP requests
195
+ *
196
+ * @example
197
+ * ```typescript
198
+ * const authHandler = client.asAuthHandler();
199
+ * const authedRequest = await authHandler(request);
200
+ * ```
201
+ */
202
+ asAuthHandler(): (request: Request) => Promise<Request>;
203
+ }
204
+
205
+ /**
206
+ * Local HTTP callback server for OAuth redirects
207
+ *
208
+ * Spins up a temporary HTTP server to catch OAuth authorization callbacks.
209
+ * Used during browser-based OAuth flows.
210
+ */
211
+ /**
212
+ * Result from OAuth callback
213
+ */
214
+ interface CallbackResult {
215
+ /** Authorization code from the OAuth server */
216
+ code: string;
217
+ /** State parameter for CSRF protection */
218
+ state: string;
219
+ }
220
+ /**
221
+ * Error from OAuth callback
222
+ */
223
+ interface CallbackError {
224
+ error: string;
225
+ error_description?: string;
226
+ }
227
+ /**
228
+ * Options for the callback server
229
+ */
230
+ interface CallbackServerOptions {
231
+ /** Port to listen on (0 = auto-assign) */
232
+ port?: number;
233
+ /** Host to bind to (default: 127.0.0.1) */
234
+ host?: string;
235
+ /** Callback path (default: /callback) */
236
+ path?: string;
237
+ /** Timeout in milliseconds (default: 5 minutes) */
238
+ timeout?: number;
239
+ /** Custom success HTML */
240
+ successHtml?: string;
241
+ /** Custom error HTML */
242
+ errorHtml?: string;
243
+ }
244
+ /**
245
+ * Running callback server instance
246
+ */
247
+ interface CallbackServer {
248
+ /** Full URL to use as redirect_uri */
249
+ redirectUri: string;
250
+ /** Port the server is listening on */
251
+ port: number;
252
+ /** Wait for the OAuth callback */
253
+ waitForCallback(): Promise<CallbackResult>;
254
+ /** Shutdown the server */
255
+ shutdown(): Promise<void>;
256
+ }
257
+ /**
258
+ * Start a local callback server for OAuth redirects
259
+ *
260
+ * @example
261
+ * ```typescript
262
+ * const server = await startCallbackServer({ port: 0 });
263
+ * console.log('Redirect URI:', server.redirectUri);
264
+ *
265
+ * // Open browser to authorization URL with redirect_uri set to server.redirectUri
266
+ *
267
+ * const result = await server.waitForCallback();
268
+ * console.log('Got code:', result.code);
269
+ *
270
+ * await server.shutdown();
271
+ * ```
272
+ */
273
+ declare function startCallbackServer(options?: CallbackServerOptions): Promise<CallbackServer>;
274
+ /**
275
+ * Find an available port
276
+ */
277
+ declare function findAvailablePort(preferredPort?: number): Promise<number>;
278
+
279
+ /**
280
+ * OAuth Metadata Discovery (RFC 8414)
281
+ *
282
+ * Discovers OAuth 2.0 authorization server metadata from .well-known endpoints.
283
+ * Supports both OAuth 2.0 and OpenID Connect discovery.
284
+ */
285
+ /**
286
+ * OAuth 2.0 Authorization Server Metadata
287
+ * https://tools.ietf.org/html/rfc8414
288
+ */
289
+ interface OAuthMetadata {
290
+ /** Authorization server's issuer identifier URL */
291
+ issuer: string;
292
+ /** URL of the authorization endpoint */
293
+ authorization_endpoint: string;
294
+ /** URL of the token endpoint */
295
+ token_endpoint: string;
296
+ /** URL of the dynamic client registration endpoint */
297
+ registration_endpoint?: string;
298
+ /** URL of the token revocation endpoint */
299
+ revocation_endpoint?: string;
300
+ /** URL of the token introspection endpoint */
301
+ introspection_endpoint?: string;
302
+ /** URL of the userinfo endpoint (OpenID Connect) */
303
+ userinfo_endpoint?: string;
304
+ /** URL of the JWKS endpoint */
305
+ jwks_uri?: string;
306
+ /** Scopes supported by the authorization server */
307
+ scopes_supported?: string[];
308
+ /** Response types supported */
309
+ response_types_supported?: string[];
310
+ /** Response modes supported */
311
+ response_modes_supported?: string[];
312
+ /** Grant types supported */
313
+ grant_types_supported?: string[];
314
+ /** Token endpoint auth methods supported */
315
+ token_endpoint_auth_methods_supported?: string[];
316
+ /** Code challenge methods supported (PKCE) */
317
+ code_challenge_methods_supported?: string[];
318
+ /** Whether server requires PKCE */
319
+ require_pkce?: boolean;
320
+ }
321
+ /**
322
+ * Discovery options
323
+ */
324
+ interface DiscoveryOptions {
325
+ /** Request timeout in ms (default: 10000) */
326
+ timeout?: number;
327
+ /** Whether to cache the metadata (default: true) */
328
+ cache?: boolean;
329
+ /** Custom fetch function */
330
+ fetch?: typeof fetch;
331
+ }
332
+ /**
333
+ * Discover OAuth metadata from a server URL
334
+ *
335
+ * Tries the following .well-known endpoints in order:
336
+ * 1. /.well-known/oauth-authorization-server
337
+ * 2. /.well-known/openid-configuration
338
+ *
339
+ * @example
340
+ * ```typescript
341
+ * const metadata = await discoverOAuthMetadata('https://auth.example.com');
342
+ * console.log('Auth endpoint:', metadata.authorization_endpoint);
343
+ * console.log('Supports PKCE:', metadata.code_challenge_methods_supported?.includes('S256'));
344
+ * ```
345
+ */
346
+ declare function discoverOAuthMetadata(serverUrl: string, options?: DiscoveryOptions): Promise<OAuthMetadata>;
347
+ /**
348
+ * Clear cached metadata for a server
349
+ */
350
+ declare function clearMetadataCache(serverUrl?: string): void;
351
+ /**
352
+ * Check if server supports a specific feature based on metadata
353
+ */
354
+ declare function serverSupports(metadata: OAuthMetadata, feature: 'pkce' | 'refresh_token' | 'dynamic_registration' | 'revocation'): boolean;
355
+ /**
356
+ * Validate that metadata has required fields
357
+ */
358
+ declare function validateMetadata(metadata: OAuthMetadata): {
359
+ valid: boolean;
360
+ errors: string[];
361
+ };
362
+
363
+ /**
364
+ * Background Token Refresh Manager
365
+ *
366
+ * Automatically refreshes tokens before they expire, ensuring
367
+ * uninterrupted access without user-visible authentication prompts.
368
+ */
369
+
370
+ /**
371
+ * Token refresh callback
372
+ */
373
+ type RefreshCallback = (refreshToken: string) => Promise<OAuthTokens>;
374
+ /**
375
+ * Refresh event types
376
+ */
377
+ type RefreshEvent = {
378
+ type: 'refresh_started';
379
+ serverUrl: string;
380
+ } | {
381
+ type: 'refresh_success';
382
+ serverUrl: string;
383
+ tokens: OAuthTokens;
384
+ } | {
385
+ type: 'refresh_failed';
386
+ serverUrl: string;
387
+ error: Error;
388
+ } | {
389
+ type: 'token_expired';
390
+ serverUrl: string;
391
+ };
392
+ /**
393
+ * Event listener callback
394
+ */
395
+ type RefreshEventListener = (event: RefreshEvent) => void;
396
+ /**
397
+ * Refresh manager options
398
+ */
399
+ interface RefreshManagerOptions {
400
+ /** Token storage backend */
401
+ storage: TokenStorage;
402
+ /** Function to refresh tokens */
403
+ refreshFn: RefreshCallback;
404
+ /** Server URL this manager is for */
405
+ serverUrl: string;
406
+ /** Seconds before expiry to trigger refresh (default: 300 = 5 minutes) */
407
+ refreshBuffer?: number;
408
+ /** Check interval in ms (default: 60000 = 1 minute) */
409
+ checkInterval?: number;
410
+ /** Maximum retry attempts on failure (default: 3) */
411
+ maxRetries?: number;
412
+ /** Retry delay in ms (default: 5000) */
413
+ retryDelay?: number;
414
+ }
415
+ /**
416
+ * Background Token Refresh Manager
417
+ *
418
+ * Monitors token expiry and automatically refreshes before expiration.
419
+ *
420
+ * @example
421
+ * ```typescript
422
+ * const manager = new RefreshManager({
423
+ * storage,
424
+ * serverUrl: 'https://mcp.example.com',
425
+ * refreshFn: async (refreshToken) => {
426
+ * // Call token endpoint with refresh_token grant
427
+ * return await exchangeRefreshToken(refreshToken);
428
+ * },
429
+ * });
430
+ *
431
+ * manager.on((event) => {
432
+ * if (event.type === 'refresh_failed') {
433
+ * console.error('Token refresh failed:', event.error);
434
+ * }
435
+ * });
436
+ *
437
+ * manager.start();
438
+ *
439
+ * // Later...
440
+ * manager.stop();
441
+ * ```
442
+ */
443
+ declare class RefreshManager {
444
+ private storage;
445
+ private refreshFn;
446
+ private serverUrl;
447
+ private refreshBuffer;
448
+ private checkInterval;
449
+ private maxRetries;
450
+ private retryDelay;
451
+ private intervalId?;
452
+ private pendingRefresh?;
453
+ private retryCount;
454
+ private listeners;
455
+ private isRunning;
456
+ constructor(options: RefreshManagerOptions);
457
+ /**
458
+ * Start background refresh monitoring
459
+ */
460
+ start(): void;
461
+ /**
462
+ * Stop background refresh monitoring
463
+ */
464
+ stop(): void;
465
+ /**
466
+ * Register event listener
467
+ */
468
+ on(listener: RefreshEventListener): () => void;
469
+ /**
470
+ * Emit event to all listeners
471
+ */
472
+ private emit;
473
+ /**
474
+ * Check token and refresh if needed
475
+ */
476
+ private checkAndRefresh;
477
+ /**
478
+ * Perform token refresh with retry logic
479
+ */
480
+ private performRefresh;
481
+ /**
482
+ * Actually perform the refresh
483
+ */
484
+ private doRefresh;
485
+ /**
486
+ * Force an immediate refresh
487
+ */
488
+ forceRefresh(): Promise<OAuthTokens>;
489
+ /**
490
+ * Get current running state
491
+ */
492
+ get running(): boolean;
493
+ }
494
+ /**
495
+ * Create a simple refresh manager for an OAuth client
496
+ */
497
+ declare function createRefreshManager(storage: TokenStorage, serverUrl: string, tokenEndpoint: string, clientId: string, clientSecret?: string): RefreshManager;
498
+
499
+ export { type CallbackError, type CallbackResult, type CallbackServer, type CallbackServerOptions, type DiscoveryOptions, OAuthClient, type OAuthClientOptions, type OAuthMetadata, type PKCEPair, type RefreshCallback, type RefreshEvent, type RefreshEventListener, RefreshManager, type RefreshManagerOptions, clearMetadataCache, createRefreshManager, discoverOAuthMetadata, findAvailablePort, generateCodeChallenge, generateCodeVerifier, generatePKCE, isValidCodeVerifier, serverSupports, startCallbackServer, validateMetadata, verifyPKCE };