@leanmcp/auth 0.4.2 → 0.4.3

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,181 @@
1
+ /**
2
+ * Shared types for @leanmcp/auth
3
+ */
4
+ /**
5
+ * Options for the Authenticated decorator
6
+ */
7
+ interface AuthenticatedOptions {
8
+ /**
9
+ * Whether to fetch and attach user information to authUser variable
10
+ * @default true
11
+ */
12
+ getUser?: boolean;
13
+ /**
14
+ * Project ID for fetching user environment variables.
15
+ * Required when @RequireEnv is used on the method or class.
16
+ * Used to scope user secrets to a specific project.
17
+ */
18
+ projectId?: string;
19
+ }
20
+
21
+ /**
22
+ * Global authUser type declaration
23
+ * This makes authUser available in @Authenticated methods without explicit declaration
24
+ */
25
+ declare global {
26
+ /**
27
+ * Authenticated user object automatically available in @Authenticated methods
28
+ *
29
+ * Implemented as a getter that reads from AsyncLocalStorage for concurrency safety.
30
+ * Each request has its own isolated context - 100% safe for concurrent requests.
31
+ */
32
+ const authUser: any;
33
+ }
34
+ /**
35
+ * Get the current authenticated user from the async context
36
+ * This is safe for concurrent requests as each request has its own context
37
+ */
38
+ declare function getAuthUser(): any;
39
+ /**
40
+ * Authentication error class for better error handling
41
+ */
42
+ declare class AuthenticationError extends Error {
43
+ code: string;
44
+ constructor(message: string, code: string);
45
+ }
46
+ /**
47
+ * Decorator to protect MCP tools, prompts, resources, or entire services with authentication
48
+ *
49
+ * CONCURRENCY SAFE: Uses AsyncLocalStorage to ensure each request has its own isolated
50
+ * authUser context, preventing race conditions in high-concurrency scenarios.
51
+ *
52
+ * Usage:
53
+ *
54
+ * 1. Protect individual methods with automatic user info:
55
+ * ```typescript
56
+ * @Tool({ description: 'Analyze sentiment' })
57
+ * @Authenticated(authProvider, { getUser: true })
58
+ * async analyzeSentiment(args: AnalyzeSentimentInput): Promise<AnalyzeSentimentOutput> {
59
+ * // authUser is automatically available in method scope
60
+ * console.log('User:', authUser);
61
+ * console.log('User ID:', authUser.sub);
62
+ * }
63
+ * ```
64
+ *
65
+ * 2. Protect without fetching user info:
66
+ * ```typescript
67
+ * @Tool({ description: 'Public tool' })
68
+ * @Authenticated(authProvider, { getUser: false })
69
+ * async publicTool(args: PublicToolInput): Promise<PublicToolOutput> {
70
+ * // Only verifies token, doesn't fetch user info
71
+ * }
72
+ * ```
73
+ *
74
+ * 3. Protect entire service (all tools/prompts/resources):
75
+ * ```typescript
76
+ * @Authenticated(authProvider)
77
+ * export class SentimentAnalysisService {
78
+ * @Tool({ description: 'Analyze sentiment' })
79
+ * async analyzeSentiment(args: AnalyzeSentimentInput) {
80
+ * // All methods in this service require authentication
81
+ * // authUser is automatically available in all methods
82
+ * console.log('User:', authUser);
83
+ * }
84
+ * }
85
+ * ```
86
+ *
87
+ * The decorator expects authentication token in the MCP request _meta field:
88
+ * ```json
89
+ * {
90
+ * "method": "tools/call",
91
+ * "params": {
92
+ * "name": "toolName",
93
+ * "arguments": { ...businessData },
94
+ * "_meta": {
95
+ * "authorization": {
96
+ * "type": "bearer",
97
+ * "token": "your-jwt-token"
98
+ * }
99
+ * }
100
+ * }
101
+ * }
102
+ * ```
103
+ *
104
+ * @param authProvider - Instance of AuthProviderBase to use for token verification
105
+ * @param options - Optional configuration for authentication behavior
106
+ */
107
+ declare function Authenticated(authProvider: AuthProviderBase, options?: AuthenticatedOptions): (target: any, propertyKey?: string | symbol, descriptor?: PropertyDescriptor) => any;
108
+ /**
109
+ * Check if a method or class requires authentication
110
+ */
111
+ declare function isAuthenticationRequired(target: any): boolean;
112
+ /**
113
+ * Get the auth provider for a method or class
114
+ */
115
+ declare function getAuthProvider(target: any): AuthProviderBase | undefined;
116
+
117
+ /**
118
+ * @leanmcp/auth - Authentication Module
119
+ *
120
+ * This module provides a base class for implementing authentication providers for MCP tools.
121
+ * Extend AuthProviderBase to integrate with different auth providers (Clerk, Stripe, Firebase, etc.)
122
+ */
123
+ /**
124
+ * Base class for authentication providers
125
+ * Extend this class to implement integrations with different auth providers
126
+ */
127
+ declare abstract class AuthProviderBase {
128
+ /**
129
+ * Initialize the auth provider with configuration
130
+ */
131
+ abstract init(config?: any): Promise<void>;
132
+ /**
133
+ * Refresh an authentication token
134
+ */
135
+ abstract refreshToken(refreshToken: string, username?: string): Promise<any>;
136
+ /**
137
+ * Verify if a token is valid
138
+ */
139
+ abstract verifyToken(token: string): Promise<boolean>;
140
+ /**
141
+ * Get user information from a token
142
+ */
143
+ abstract getUser(token: string): Promise<any>;
144
+ }
145
+ /**
146
+ * Unified AuthProvider class that dynamically selects the appropriate auth provider
147
+ * based on the provider parameter
148
+ */
149
+ declare class AuthProvider extends AuthProviderBase {
150
+ private providerInstance;
151
+ private providerType;
152
+ private config;
153
+ constructor(provider: string, config?: any);
154
+ /**
155
+ * Initialize the selected auth provider
156
+ */
157
+ init(config?: any): Promise<void>;
158
+ /**
159
+ * Refresh an authentication token
160
+ */
161
+ refreshToken(refreshToken: string, username?: string): Promise<any>;
162
+ /**
163
+ * Verify if a token is valid
164
+ */
165
+ verifyToken(token: string): Promise<boolean>;
166
+ /**
167
+ * Get user information from a token
168
+ */
169
+ getUser(token: string): Promise<any>;
170
+ /**
171
+ * Get user secrets for a project (LeanMCP provider only)
172
+ * Other providers will return empty object
173
+ */
174
+ getUserSecrets(token: string, projectId: string): Promise<Record<string, string>>;
175
+ /**
176
+ * Get the provider type
177
+ */
178
+ getProviderType(): string;
179
+ }
180
+
181
+ export { AuthProvider, AuthProviderBase, Authenticated, type AuthenticatedOptions, AuthenticationError, getAuthProvider, getAuthUser, isAuthenticationRequired };
@@ -0,0 +1,376 @@
1
+ /**
2
+ * OAuth Proxy Types
3
+ *
4
+ * Types and interfaces for the OAuth proxy that enables
5
+ * MCP servers to authenticate via external identity providers.
6
+ */
7
+ /**
8
+ * OAuth provider configuration
9
+ */
10
+ interface OAuthProviderConfig {
11
+ /** Display name of the provider */
12
+ name: string;
13
+ /** Provider identifier (e.g., 'google', 'github') */
14
+ id: string;
15
+ /** OAuth authorization endpoint */
16
+ authorizationEndpoint: string;
17
+ /** OAuth token endpoint */
18
+ tokenEndpoint: string;
19
+ /** UserInfo endpoint (optional, for fetching user profile) */
20
+ userInfoEndpoint?: string;
21
+ /** OAuth client ID */
22
+ clientId: string;
23
+ /** OAuth client secret */
24
+ clientSecret: string;
25
+ /** Scopes to request */
26
+ scopes: string[];
27
+ /** Token endpoint authentication method */
28
+ tokenEndpointAuthMethod?: 'client_secret_basic' | 'client_secret_post';
29
+ /** Whether the provider supports PKCE */
30
+ supportsPkce?: boolean;
31
+ /** Custom parameters for authorization */
32
+ authorizationParams?: Record<string, string>;
33
+ /** Custom parameters for token exchange */
34
+ tokenParams?: Record<string, string>;
35
+ }
36
+ /**
37
+ * User info from external provider
38
+ */
39
+ interface ExternalUserInfo {
40
+ /** User's unique ID from the provider */
41
+ sub: string;
42
+ /** User's email address */
43
+ email?: string;
44
+ /** Whether email is verified */
45
+ email_verified?: boolean;
46
+ /** User's display name */
47
+ name?: string;
48
+ /** User's profile picture URL */
49
+ picture?: string;
50
+ /** Provider-specific raw data */
51
+ raw?: Record<string, unknown>;
52
+ }
53
+ /**
54
+ * Token mapping function
55
+ * Maps external provider tokens to internal MCP tokens
56
+ */
57
+ type TokenMapper = (externalTokens: ExternalTokens, userInfo: ExternalUserInfo, provider: OAuthProviderConfig) => Promise<MappedTokens>;
58
+ /**
59
+ * External provider tokens
60
+ */
61
+ interface ExternalTokens {
62
+ access_token: string;
63
+ token_type: string;
64
+ expires_in?: number;
65
+ refresh_token?: string;
66
+ id_token?: string;
67
+ scope?: string;
68
+ }
69
+ /**
70
+ * Internal MCP tokens after mapping
71
+ */
72
+ interface MappedTokens {
73
+ /** Access token to use with MCP server */
74
+ access_token: string;
75
+ /** Token type (usually Bearer) */
76
+ token_type: string;
77
+ /** Token lifetime in seconds */
78
+ expires_in?: number;
79
+ /** Refresh token for obtaining new tokens */
80
+ refresh_token?: string;
81
+ /** MCP user ID */
82
+ user_id?: string;
83
+ }
84
+ /**
85
+ * OAuth proxy configuration
86
+ */
87
+ interface OAuthProxyConfig {
88
+ /** Base URL where the proxy is hosted */
89
+ baseUrl: string;
90
+ /** Path for authorization endpoint (default: /authorize) */
91
+ authorizePath?: string;
92
+ /** Path for token endpoint (default: /token) */
93
+ tokenPath?: string;
94
+ /** Path for callback from external provider (default: /callback) */
95
+ callbackPath?: string;
96
+ /** Configured upstream providers */
97
+ providers: OAuthProviderConfig[];
98
+ /** Custom token mapper (optional) */
99
+ tokenMapper?: TokenMapper;
100
+ /** Session storage for OAuth state */
101
+ sessionSecret: string;
102
+ /** Whether to forward PKCE to upstream provider */
103
+ forwardPkce?: boolean;
104
+ }
105
+ /**
106
+ * Pending authorization request
107
+ */
108
+ interface PendingAuthRequest {
109
+ /** Provider ID */
110
+ providerId: string;
111
+ /** Original client redirect URI */
112
+ clientRedirectUri: string;
113
+ /** Original client state */
114
+ clientState?: string;
115
+ /** PKCE code verifier (if forwarding PKCE) */
116
+ codeVerifier?: string;
117
+ /** Internal state for proxy */
118
+ proxyState: string;
119
+ /** Created timestamp */
120
+ createdAt: number;
121
+ }
122
+
123
+ /**
124
+ * Pre-configured OAuth Providers
125
+ *
126
+ * Ready-to-use configurations for popular identity providers.
127
+ * Just add your client credentials.
128
+ */
129
+
130
+ /**
131
+ * Google OAuth provider configuration
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * const google = googleProvider({
136
+ * clientId: process.env.GOOGLE_CLIENT_ID!,
137
+ * clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
138
+ * scopes: ['email', 'profile'],
139
+ * });
140
+ * ```
141
+ */
142
+ declare function googleProvider(options: {
143
+ clientId: string;
144
+ clientSecret: string;
145
+ scopes?: string[];
146
+ }): OAuthProviderConfig;
147
+ /**
148
+ * GitHub OAuth provider configuration
149
+ *
150
+ * @example
151
+ * ```typescript
152
+ * const github = githubProvider({
153
+ * clientId: process.env.GITHUB_CLIENT_ID!,
154
+ * clientSecret: process.env.GITHUB_CLIENT_SECRET!,
155
+ * });
156
+ * ```
157
+ */
158
+ declare function githubProvider(options: {
159
+ clientId: string;
160
+ clientSecret: string;
161
+ scopes?: string[];
162
+ }): OAuthProviderConfig;
163
+ /**
164
+ * Microsoft Azure AD / Entra ID provider configuration
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * const azure = azureProvider({
169
+ * clientId: process.env.AZURE_CLIENT_ID!,
170
+ * clientSecret: process.env.AZURE_CLIENT_SECRET!,
171
+ * tenantId: process.env.AZURE_TENANT_ID ?? 'common',
172
+ * });
173
+ * ```
174
+ */
175
+ declare function azureProvider(options: {
176
+ clientId: string;
177
+ clientSecret: string;
178
+ tenantId?: string;
179
+ scopes?: string[];
180
+ }): OAuthProviderConfig;
181
+ /**
182
+ * GitLab OAuth provider configuration
183
+ *
184
+ * @example
185
+ * ```typescript
186
+ * const gitlab = gitlabProvider({
187
+ * clientId: process.env.GITLAB_CLIENT_ID!,
188
+ * clientSecret: process.env.GITLAB_CLIENT_SECRET!,
189
+ * // Optional: use self-hosted GitLab
190
+ * baseUrl: 'https://gitlab.mycompany.com',
191
+ * });
192
+ * ```
193
+ */
194
+ declare function gitlabProvider(options: {
195
+ clientId: string;
196
+ clientSecret: string;
197
+ baseUrl?: string;
198
+ scopes?: string[];
199
+ }): OAuthProviderConfig;
200
+ /**
201
+ * Slack OAuth provider configuration
202
+ *
203
+ * @example
204
+ * ```typescript
205
+ * const slack = slackProvider({
206
+ * clientId: process.env.SLACK_CLIENT_ID!,
207
+ * clientSecret: process.env.SLACK_CLIENT_SECRET!,
208
+ * });
209
+ * ```
210
+ */
211
+ declare function slackProvider(options: {
212
+ clientId: string;
213
+ clientSecret: string;
214
+ scopes?: string[];
215
+ }): OAuthProviderConfig;
216
+ /**
217
+ * Discord OAuth provider configuration
218
+ *
219
+ * @example
220
+ * ```typescript
221
+ * const discord = discordProvider({
222
+ * clientId: process.env.DISCORD_CLIENT_ID!,
223
+ * clientSecret: process.env.DISCORD_CLIENT_SECRET!,
224
+ * });
225
+ * ```
226
+ */
227
+ declare function discordProvider(options: {
228
+ clientId: string;
229
+ clientSecret: string;
230
+ scopes?: string[];
231
+ }): OAuthProviderConfig;
232
+ /**
233
+ * Create a custom OAuth provider
234
+ *
235
+ * @example
236
+ * ```typescript
237
+ * const custom = customProvider({
238
+ * id: 'my-idp',
239
+ * name: 'My Identity Provider',
240
+ * authorizationEndpoint: 'https://idp.example.com/authorize',
241
+ * tokenEndpoint: 'https://idp.example.com/token',
242
+ * clientId: process.env.MY_IDP_CLIENT_ID!,
243
+ * clientSecret: process.env.MY_IDP_CLIENT_SECRET!,
244
+ * scopes: ['openid', 'profile'],
245
+ * });
246
+ * ```
247
+ */
248
+ declare function customProvider(config: OAuthProviderConfig): OAuthProviderConfig;
249
+
250
+ /**
251
+ * OAuth Proxy
252
+ *
253
+ * Enables MCP servers to authenticate users via external identity providers.
254
+ * Acts as an intermediary between MCP clients and providers like Google, GitHub, etc.
255
+ */
256
+
257
+ /**
258
+ * OAuth Proxy class
259
+ *
260
+ * Handles OAuth flows with external identity providers and maps
261
+ * their tokens to internal MCP tokens.
262
+ *
263
+ * @example
264
+ * ```typescript
265
+ * import { OAuthProxy, googleProvider, githubProvider } from '@leanmcp/auth/proxy';
266
+ *
267
+ * const proxy = new OAuthProxy({
268
+ * baseUrl: 'https://mcp.example.com/auth',
269
+ * sessionSecret: process.env.SESSION_SECRET!,
270
+ * providers: [
271
+ * googleProvider({ clientId: '...', clientSecret: '...' }),
272
+ * githubProvider({ clientId: '...', clientSecret: '...' }),
273
+ * ],
274
+ * });
275
+ *
276
+ * // Express integration
277
+ * app.get('/auth/authorize', (req, res) => {
278
+ * const url = proxy.handleAuthorize(req.query);
279
+ * res.redirect(url);
280
+ * });
281
+ *
282
+ * app.get('/auth/callback', async (req, res) => {
283
+ * const result = await proxy.handleCallback(req.query);
284
+ * res.redirect(result.redirectUri);
285
+ * });
286
+ * ```
287
+ */
288
+ declare class OAuthProxy {
289
+ private config;
290
+ private providersMap;
291
+ constructor(config: OAuthProxyConfig);
292
+ /**
293
+ * Get configured providers
294
+ */
295
+ getProviders(): OAuthProviderConfig[];
296
+ /**
297
+ * Get a provider by ID
298
+ */
299
+ getProvider(id: string): OAuthProviderConfig | undefined;
300
+ /**
301
+ * Generate state parameter with signature
302
+ */
303
+ private generateState;
304
+ /**
305
+ * Verify state parameter signature
306
+ */
307
+ private verifyState;
308
+ /**
309
+ * Handle authorization request
310
+ *
311
+ * Redirects the user to the external provider's authorization page.
312
+ *
313
+ * @param params - Request parameters
314
+ * @returns URL to redirect the user to
315
+ */
316
+ handleAuthorize(params: {
317
+ provider: string;
318
+ redirect_uri: string;
319
+ state?: string;
320
+ scope?: string;
321
+ code_challenge?: string;
322
+ code_challenge_method?: string;
323
+ }): string;
324
+ /**
325
+ * Handle callback from external provider
326
+ *
327
+ * Exchanges the authorization code for tokens and maps them.
328
+ *
329
+ * @param params - Callback query parameters
330
+ * @returns Result with redirect URI and tokens
331
+ */
332
+ handleCallback(params: {
333
+ code?: string;
334
+ state?: string;
335
+ error?: string;
336
+ error_description?: string;
337
+ }): Promise<{
338
+ redirectUri: string;
339
+ tokens?: MappedTokens;
340
+ error?: string;
341
+ }>;
342
+ /**
343
+ * Exchange authorization code for tokens with external provider
344
+ */
345
+ private exchangeCodeForTokens;
346
+ /**
347
+ * Fetch user info from external provider
348
+ */
349
+ private fetchUserInfo;
350
+ /**
351
+ * Generate an internal authorization code for the mapped tokens
352
+ * This code can be exchanged via the token endpoint
353
+ */
354
+ private generateInternalCode;
355
+ /**
356
+ * Handle token request (exchange internal code for tokens)
357
+ */
358
+ handleToken(params: {
359
+ grant_type: string;
360
+ code?: string;
361
+ redirect_uri?: string;
362
+ client_id?: string;
363
+ client_secret?: string;
364
+ refresh_token?: string;
365
+ }): Promise<MappedTokens>;
366
+ /**
367
+ * Express/Connect middleware factory
368
+ */
369
+ createMiddleware(): {
370
+ authorize: (req: any, res: any) => void;
371
+ callback: (req: any, res: any) => Promise<void>;
372
+ token: (req: any, res: any) => Promise<void>;
373
+ };
374
+ }
375
+
376
+ export { type ExternalTokens, type ExternalUserInfo, type MappedTokens, type OAuthProviderConfig, OAuthProxy, type OAuthProxyConfig, type PendingAuthRequest, type TokenMapper, azureProvider, customProvider, discordProvider, githubProvider, gitlabProvider, googleProvider, slackProvider };