@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.
- package/README.md +148 -661
- package/dist/{auth0-54GZT2EI.mjs → auth0-UTD4QBG6.mjs} +4 -2
- package/dist/chunk-LPEX4YW6.mjs +13 -0
- package/dist/{chunk-EVD2TRPR.mjs → chunk-P4HFKA5R.mjs} +50 -21
- package/dist/chunk-RGCCBQWG.mjs +113 -0
- package/dist/chunk-ZOPKMOPV.mjs +53 -0
- package/dist/{clerk-FR7ITM33.mjs → clerk-3SDKGD6C.mjs} +4 -2
- package/dist/client/index.d.mts +499 -0
- package/dist/client/index.d.ts +499 -0
- package/dist/client/index.js +1039 -0
- package/dist/client/index.mjs +869 -0
- package/dist/{cognito-I6V5YNYM.mjs → cognito-QQT7LK2Y.mjs} +4 -2
- package/dist/index.d.mts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +186 -15
- package/dist/index.mjs +2 -1
- package/dist/leanmcp-Y7TXNSTD.mjs +140 -0
- package/dist/proxy/index.d.mts +376 -0
- package/dist/proxy/index.d.ts +376 -0
- package/dist/proxy/index.js +536 -0
- package/dist/proxy/index.mjs +480 -0
- package/dist/server/index.d.mts +496 -0
- package/dist/server/index.d.ts +496 -0
- package/dist/server/index.js +882 -0
- package/dist/server/index.mjs +847 -0
- package/dist/storage/index.d.mts +181 -0
- package/dist/storage/index.d.ts +181 -0
- package/dist/storage/index.js +499 -0
- package/dist/storage/index.mjs +372 -0
- package/dist/types-DMpGN530.d.mts +122 -0
- package/dist/types-DMpGN530.d.ts +122 -0
- package/package.json +45 -8
package/README.md
CHANGED
|
@@ -1,17 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img
|
|
3
|
+
src="https://raw.githubusercontent.com/LeanMCP/leanmcp-sdk/refs/heads/main/assets/logo.svg"
|
|
4
|
+
alt="LeanMCP Logo"
|
|
5
|
+
width="400"
|
|
6
|
+
/>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<strong>@leanmcp/auth</strong><br/>
|
|
11
|
+
Token-based authentication decorators and multi-provider support for MCP tools.
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://www.npmjs.com/package/@leanmcp/auth">
|
|
16
|
+
<img src="https://img.shields.io/npm/v/@leanmcp/auth" alt="npm version" />
|
|
17
|
+
</a>
|
|
18
|
+
<a href="https://www.npmjs.com/package/@leanmcp/auth">
|
|
19
|
+
<img src="https://img.shields.io/npm/dm/@leanmcp/auth" alt="npm downloads" />
|
|
20
|
+
</a>
|
|
21
|
+
<a href="https://docs.leanmcp.com/sdk/auth">
|
|
22
|
+
<img src="https://img.shields.io/badge/Docs-leanmcp-0A66C2?" />
|
|
23
|
+
</a>
|
|
24
|
+
<a href="https://discord.com/invite/DsRcA3GwPy">
|
|
25
|
+
<img src="https://img.shields.io/badge/Discord-Join-5865F2?logo=discord&logoColor=white" />
|
|
26
|
+
</a>
|
|
27
|
+
<a href="https://x.com/LeanMcp">
|
|
28
|
+
<img src="https://img.shields.io/badge/@LeanMCP-f5f5f5?logo=x&logoColor=000000" />
|
|
29
|
+
</a>
|
|
30
|
+
</p>
|
|
4
31
|
|
|
5
32
|
## Features
|
|
6
33
|
|
|
7
|
-
- **@Authenticated
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **Type-safe** - Full TypeScript support with type inference and global type declarations
|
|
14
|
-
- **OAuth & Session modes** - Support for both session-based and OAuth refresh token flows
|
|
34
|
+
- **@Authenticated Decorator** — Protect tools, prompts, and resources with a simple decorator
|
|
35
|
+
- **Multi-Provider Support** — AWS Cognito, Clerk, Auth0, and LeanMCP providers
|
|
36
|
+
- **Automatic authUser** — Decoded user info injected as global `authUser` variable
|
|
37
|
+
- **Concurrency Safe** — Uses AsyncLocalStorage for request-isolated context
|
|
38
|
+
- **Method or Class-Level** — Apply to individual methods or entire services
|
|
39
|
+
- **OAuth & Session Modes** — Support for both session-based and OAuth refresh token flows
|
|
15
40
|
|
|
16
41
|
## Installation
|
|
17
42
|
|
|
@@ -21,17 +46,17 @@ npm install @leanmcp/auth @leanmcp/core
|
|
|
21
46
|
|
|
22
47
|
### Provider Dependencies
|
|
23
48
|
|
|
24
|
-
|
|
49
|
+
**AWS Cognito:**
|
|
25
50
|
```bash
|
|
26
51
|
npm install @aws-sdk/client-cognito-identity-provider axios jsonwebtoken jwk-to-pem
|
|
27
52
|
```
|
|
28
53
|
|
|
29
|
-
|
|
54
|
+
**Clerk:**
|
|
30
55
|
```bash
|
|
31
56
|
npm install axios jsonwebtoken jwk-to-pem
|
|
32
57
|
```
|
|
33
58
|
|
|
34
|
-
|
|
59
|
+
**Auth0:**
|
|
35
60
|
```bash
|
|
36
61
|
npm install axios jsonwebtoken jwk-to-pem
|
|
37
62
|
```
|
|
@@ -43,7 +68,6 @@ npm install axios jsonwebtoken jwk-to-pem
|
|
|
43
68
|
```typescript
|
|
44
69
|
import { AuthProvider } from "@leanmcp/auth";
|
|
45
70
|
|
|
46
|
-
// Initialize with AWS Cognito
|
|
47
71
|
const authProvider = new AuthProvider('cognito', {
|
|
48
72
|
region: 'us-east-1',
|
|
49
73
|
userPoolId: 'us-east-1_XXXXXXXXX',
|
|
@@ -53,22 +77,20 @@ const authProvider = new AuthProvider('cognito', {
|
|
|
53
77
|
await authProvider.init();
|
|
54
78
|
```
|
|
55
79
|
|
|
56
|
-
### 2. Protect
|
|
80
|
+
### 2. Protect Methods with @Authenticated
|
|
57
81
|
|
|
58
82
|
```typescript
|
|
59
83
|
import { Tool } from "@leanmcp/core";
|
|
60
84
|
import { Authenticated } from "@leanmcp/auth";
|
|
61
85
|
|
|
62
86
|
export class SentimentService {
|
|
63
|
-
// This method requires authentication with automatic user info
|
|
64
87
|
@Tool({ description: 'Analyze sentiment (requires auth)' })
|
|
65
|
-
@Authenticated(authProvider)
|
|
88
|
+
@Authenticated(authProvider)
|
|
66
89
|
async analyzeSentiment(input: { text: string }) {
|
|
67
|
-
//
|
|
68
|
-
// authUser is automatically available with decoded JWT payload
|
|
90
|
+
// authUser is automatically available with user info
|
|
69
91
|
console.log('User ID:', authUser.sub);
|
|
70
92
|
console.log('Email:', authUser.email);
|
|
71
|
-
|
|
93
|
+
|
|
72
94
|
return {
|
|
73
95
|
sentiment: 'positive',
|
|
74
96
|
score: 0.8,
|
|
@@ -76,8 +98,8 @@ export class SentimentService {
|
|
|
76
98
|
};
|
|
77
99
|
}
|
|
78
100
|
|
|
79
|
-
//
|
|
80
|
-
@Tool({ description: 'Get
|
|
101
|
+
// Public method - no authentication
|
|
102
|
+
@Tool({ description: 'Get categories (public)' })
|
|
81
103
|
async getCategories() {
|
|
82
104
|
return { categories: ['positive', 'negative', 'neutral'] };
|
|
83
105
|
}
|
|
@@ -87,40 +109,27 @@ export class SentimentService {
|
|
|
87
109
|
### 3. Protect Entire Service
|
|
88
110
|
|
|
89
111
|
```typescript
|
|
90
|
-
import { Authenticated } from "@leanmcp/auth";
|
|
91
|
-
|
|
92
112
|
// All methods in this class require authentication
|
|
93
113
|
@Authenticated(authProvider)
|
|
94
114
|
export class SecureService {
|
|
95
|
-
@Tool({ description: 'Protected tool
|
|
96
|
-
async
|
|
97
|
-
// authUser is
|
|
98
|
-
console.log('Authenticated user:', authUser.email);
|
|
99
|
-
return { data: input.data, userId: authUser.sub };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
@Tool({ description: 'Protected tool 2' })
|
|
103
|
-
async tool2(input: { data: string }) {
|
|
104
|
-
// authUser is available here too
|
|
115
|
+
@Tool({ description: 'Protected tool' })
|
|
116
|
+
async protectedTool(input: { data: string }) {
|
|
117
|
+
// authUser is available in all methods
|
|
105
118
|
return { data: input.data, userId: authUser.sub };
|
|
106
119
|
}
|
|
107
120
|
}
|
|
108
121
|
```
|
|
109
122
|
|
|
110
|
-
|
|
123
|
+
---
|
|
111
124
|
|
|
112
|
-
|
|
125
|
+
## The authUser Variable
|
|
113
126
|
|
|
114
|
-
When
|
|
127
|
+
When using `@Authenticated`, a global `authUser` variable is automatically injected containing the decoded JWT payload:
|
|
115
128
|
|
|
116
129
|
```typescript
|
|
117
130
|
@Tool({ description: 'Create post' })
|
|
118
131
|
@Authenticated(authProvider)
|
|
119
132
|
async createPost(input: { title: string, content: string }) {
|
|
120
|
-
// authUser is automatically available - no need to pass it as a parameter
|
|
121
|
-
console.log('User ID:', authUser.sub);
|
|
122
|
-
console.log('Email:', authUser.email);
|
|
123
|
-
|
|
124
133
|
return {
|
|
125
134
|
id: generateId(),
|
|
126
135
|
title: input.title,
|
|
@@ -131,30 +140,8 @@ async createPost(input: { title: string, content: string }) {
|
|
|
131
140
|
}
|
|
132
141
|
```
|
|
133
142
|
|
|
134
|
-
### Controlling User Data Fetching
|
|
135
|
-
|
|
136
|
-
You can control whether user information is fetched using the `getUser` option:
|
|
137
|
-
|
|
138
|
-
```typescript
|
|
139
|
-
// Fetch user info (default behavior)
|
|
140
|
-
@Authenticated(authProvider, { getUser: true })
|
|
141
|
-
async methodWithUserInfo(input: any) {
|
|
142
|
-
// authUser is available
|
|
143
|
-
console.log(authUser);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Only verify token, don't fetch user info
|
|
147
|
-
@Authenticated(authProvider, { getUser: false })
|
|
148
|
-
async methodWithoutUserInfo(input: any) {
|
|
149
|
-
// authUser is undefined
|
|
150
|
-
// Faster execution, use when you only need token validation
|
|
151
|
-
}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
143
|
### Provider-Specific User Data
|
|
155
144
|
|
|
156
|
-
The structure of `authUser` depends on your authentication provider:
|
|
157
|
-
|
|
158
145
|
**AWS Cognito:**
|
|
159
146
|
```typescript
|
|
160
147
|
{
|
|
@@ -162,8 +149,7 @@ The structure of `authUser` depends on your authentication provider:
|
|
|
162
149
|
email: 'user@example.com',
|
|
163
150
|
email_verified: true,
|
|
164
151
|
'cognito:username': 'username',
|
|
165
|
-
'cognito:groups': ['admin', 'users']
|
|
166
|
-
// ... other Cognito claims
|
|
152
|
+
'cognito:groups': ['admin', 'users']
|
|
167
153
|
}
|
|
168
154
|
```
|
|
169
155
|
|
|
@@ -175,8 +161,7 @@ The structure of `authUser` depends on your authentication provider:
|
|
|
175
161
|
email: 'user@example.com',
|
|
176
162
|
firstName: 'John',
|
|
177
163
|
lastName: 'Doe',
|
|
178
|
-
imageUrl: 'https://img.clerk.com/...'
|
|
179
|
-
// ... other Clerk claims
|
|
164
|
+
imageUrl: 'https://img.clerk.com/...'
|
|
180
165
|
}
|
|
181
166
|
```
|
|
182
167
|
|
|
@@ -187,135 +172,29 @@ The structure of `authUser` depends on your authentication provider:
|
|
|
187
172
|
email: 'user@example.com',
|
|
188
173
|
email_verified: true,
|
|
189
174
|
name: 'John Doe',
|
|
190
|
-
picture: 'https://s.gravatar.com/avatar/...'
|
|
191
|
-
// ... other Auth0 claims
|
|
192
|
-
}
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
### TypeScript Support
|
|
196
|
-
|
|
197
|
-
The `authUser` variable is globally declared and available without TypeScript errors:
|
|
198
|
-
|
|
199
|
-
```typescript
|
|
200
|
-
// No need to import or declare authUser
|
|
201
|
-
@Authenticated(authProvider)
|
|
202
|
-
async myMethod(input: any) {
|
|
203
|
-
// TypeScript knows about authUser
|
|
204
|
-
const userId: string = authUser.sub;
|
|
205
|
-
const email: string = authUser.email;
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
For better type safety, you can create a typed interface:
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
interface MyAuthUser {
|
|
213
|
-
sub: string;
|
|
214
|
-
email: string;
|
|
215
|
-
name?: string;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
@Authenticated(authProvider)
|
|
219
|
-
async myMethod(input: any) {
|
|
220
|
-
const user = authUser as MyAuthUser;
|
|
221
|
-
console.log(user.email); // Fully typed
|
|
175
|
+
picture: 'https://s.gravatar.com/avatar/...'
|
|
222
176
|
}
|
|
223
177
|
```
|
|
224
178
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
### Client Side - Calling Protected Methods
|
|
228
|
-
|
|
229
|
-
Authentication tokens are passed via the `_meta` field following MCP protocol standards:
|
|
179
|
+
### Controlling User Fetch
|
|
230
180
|
|
|
231
181
|
```typescript
|
|
232
|
-
//
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
text: "Hello world"
|
|
237
|
-
},
|
|
238
|
-
_meta: {
|
|
239
|
-
authorization: {
|
|
240
|
-
type: "bearer",
|
|
241
|
-
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
// Without token (fails with MISSING_TOKEN error)
|
|
247
|
-
await mcpClient.callTool({
|
|
248
|
-
name: "analyzeSentiment",
|
|
249
|
-
arguments: {
|
|
250
|
-
text: "Hello world"
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
### Raw MCP Request Format
|
|
256
|
-
|
|
257
|
-
```json
|
|
258
|
-
{
|
|
259
|
-
"method": "tools/call",
|
|
260
|
-
"params": {
|
|
261
|
-
"name": "analyzeSentiment",
|
|
262
|
-
"arguments": {
|
|
263
|
-
"text": "Hello world"
|
|
264
|
-
},
|
|
265
|
-
"_meta": {
|
|
266
|
-
"authorization": {
|
|
267
|
-
"type": "bearer",
|
|
268
|
-
"token": "your-jwt-token"
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
182
|
+
// Fetch user info (default)
|
|
183
|
+
@Authenticated(authProvider, { getUser: true })
|
|
184
|
+
async withUserInfo(input: any) {
|
|
185
|
+
console.log(authUser); // User data available
|
|
272
186
|
}
|
|
273
|
-
```
|
|
274
187
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
try {
|
|
281
|
-
await service.protectedMethod({ text: "test" });
|
|
282
|
-
} catch (error) {
|
|
283
|
-
if (error instanceof AuthenticationError) {
|
|
284
|
-
switch (error.code) {
|
|
285
|
-
case 'MISSING_TOKEN':
|
|
286
|
-
console.log('No token provided in request');
|
|
287
|
-
break;
|
|
288
|
-
case 'INVALID_TOKEN':
|
|
289
|
-
console.log('Token is invalid or expired');
|
|
290
|
-
break;
|
|
291
|
-
case 'VERIFICATION_FAILED':
|
|
292
|
-
console.log('Token verification failed:', error.message);
|
|
293
|
-
break;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
188
|
+
// Only verify token, skip user fetch (faster)
|
|
189
|
+
@Authenticated(authProvider, { getUser: false })
|
|
190
|
+
async tokenOnlyValidation(input: any) {
|
|
191
|
+
// authUser is undefined
|
|
296
192
|
}
|
|
297
193
|
```
|
|
298
194
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
| Code | When | Message |
|
|
302
|
-
|------|------|---------|
|
|
303
|
-
| `MISSING_TOKEN` | No token in `_meta` | "Authentication required. Please provide a valid token in _meta.authorization.token" |
|
|
304
|
-
| `INVALID_TOKEN` | Token invalid/expired | "Invalid or expired token. Please authenticate again." |
|
|
305
|
-
| `VERIFICATION_FAILED` | Verification error | "Token verification failed: [details]" |
|
|
306
|
-
|
|
307
|
-
## Supported Auth Providers
|
|
308
|
-
|
|
309
|
-
### Provider Comparison
|
|
195
|
+
---
|
|
310
196
|
|
|
311
|
-
|
|
312
|
-
|---------|-------------|-------|-------|
|
|
313
|
-
| **JWT Verification** | ✅ JWKS | ✅ JWKS | ✅ JWKS |
|
|
314
|
-
| **Refresh Tokens** | ✅ Yes | ✅ Yes (OAuth mode) | ✅ Yes |
|
|
315
|
-
| **Session Mode** | ❌ No | ✅ Yes (default) | ❌ No |
|
|
316
|
-
| **OAuth Mode** | ✅ Yes | ✅ Yes | ✅ Yes |
|
|
317
|
-
| **User Data** | ✅ Yes | ✅ Yes | ✅ Yes |
|
|
318
|
-
| **Setup Complexity** | Low | Low | Low |
|
|
197
|
+
## Supported Providers
|
|
319
198
|
|
|
320
199
|
### AWS Cognito
|
|
321
200
|
|
|
@@ -328,11 +207,6 @@ const authProvider = new AuthProvider('cognito', {
|
|
|
328
207
|
await authProvider.init();
|
|
329
208
|
```
|
|
330
209
|
|
|
331
|
-
**Token Requirements:**
|
|
332
|
-
- JWT token from AWS Cognito User Pool
|
|
333
|
-
- Token must be valid and not expired
|
|
334
|
-
- Token must be issued by the configured User Pool
|
|
335
|
-
|
|
336
210
|
**Environment Variables:**
|
|
337
211
|
```bash
|
|
338
212
|
AWS_REGION=us-east-1
|
|
@@ -342,31 +216,14 @@ COGNITO_CLIENT_ID=your-client-id
|
|
|
342
216
|
|
|
343
217
|
### Clerk
|
|
344
218
|
|
|
345
|
-
Clerk supports both **Session Mode** (default) and **OAuth Mode** (with refresh tokens).
|
|
346
|
-
|
|
347
|
-
#### Session Mode (Default)
|
|
348
|
-
|
|
349
219
|
```typescript
|
|
220
|
+
// Session Mode (default)
|
|
350
221
|
const authProvider = new AuthProvider('clerk', {
|
|
351
222
|
frontendApi: 'your-frontend-api.clerk.accounts.dev',
|
|
352
223
|
secretKey: 'sk_test_...'
|
|
353
224
|
});
|
|
354
|
-
await authProvider.init();
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
**Configuration:**
|
|
358
|
-
- `frontendApi` - Your Clerk Frontend API domain
|
|
359
|
-
- `secretKey` - Your Clerk Secret Key
|
|
360
225
|
|
|
361
|
-
|
|
362
|
-
```bash
|
|
363
|
-
CLERK_FRONTEND_API=your-frontend-api.clerk.accounts.dev
|
|
364
|
-
CLERK_SECRET_KEY=sk_test_...
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
#### OAuth Mode (Refresh Tokens)
|
|
368
|
-
|
|
369
|
-
```typescript
|
|
226
|
+
// OAuth Mode (with refresh tokens)
|
|
370
227
|
const authProvider = new AuthProvider('clerk', {
|
|
371
228
|
frontendApi: 'your-frontend-api.clerk.accounts.dev',
|
|
372
229
|
secretKey: 'sk_test_...',
|
|
@@ -374,27 +231,8 @@ const authProvider = new AuthProvider('clerk', {
|
|
|
374
231
|
clientSecret: 'your-oauth-client-secret',
|
|
375
232
|
redirectUri: 'https://yourapp.com/callback'
|
|
376
233
|
});
|
|
377
|
-
await authProvider.init();
|
|
378
234
|
|
|
379
|
-
|
|
380
|
-
const newTokens = await authProvider.refreshToken(refreshToken);
|
|
381
|
-
// Returns: { access_token, id_token, refresh_token }
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
**OAuth Configuration:**
|
|
385
|
-
- `clientId` - OAuth Client ID from Clerk
|
|
386
|
-
- `clientSecret` - OAuth Client Secret from Clerk
|
|
387
|
-
- `redirectUri` - OAuth redirect URI
|
|
388
|
-
|
|
389
|
-
**Token Requirements:**
|
|
390
|
-
- JWT token from Clerk (ID token or session token)
|
|
391
|
-
- Token must be valid and not expired
|
|
392
|
-
- Token must be issued by your Clerk instance
|
|
393
|
-
|
|
394
|
-
**User Data:**
|
|
395
|
-
```typescript
|
|
396
|
-
const user = await authProvider.getUser(idToken);
|
|
397
|
-
// Returns: { sub, email, email_verified, first_name, last_name, attributes }
|
|
235
|
+
await authProvider.init();
|
|
398
236
|
```
|
|
399
237
|
|
|
400
238
|
### Auth0
|
|
@@ -403,55 +241,69 @@ const user = await authProvider.getUser(idToken);
|
|
|
403
241
|
const authProvider = new AuthProvider('auth0', {
|
|
404
242
|
domain: 'your-tenant.auth0.com',
|
|
405
243
|
clientId: 'your-client-id',
|
|
406
|
-
clientSecret: 'your-client-secret',
|
|
407
|
-
audience: 'https://your-api-identifier'
|
|
408
|
-
scopes: 'openid profile email offline_access' // Optional, defaults shown
|
|
244
|
+
clientSecret: 'your-client-secret',
|
|
245
|
+
audience: 'https://your-api-identifier'
|
|
409
246
|
});
|
|
410
247
|
await authProvider.init();
|
|
411
|
-
|
|
412
|
-
// Refresh tokens when needed
|
|
413
|
-
const newTokens = await authProvider.refreshToken(refreshToken);
|
|
414
|
-
// Returns: { access_token, id_token, refresh_token, expires_in }
|
|
415
248
|
```
|
|
416
249
|
|
|
417
|
-
|
|
418
|
-
- `domain` - Your Auth0 tenant domain (e.g., `your-tenant.auth0.com`)
|
|
419
|
-
- `clientId` - Your Auth0 Application Client ID
|
|
420
|
-
- `clientSecret` - Your Auth0 Application Client Secret (optional for public clients)
|
|
421
|
-
- `audience` - Your API identifier (required for API access)
|
|
422
|
-
- `scopes` - OAuth scopes (default: `openid profile email offline_access`)
|
|
250
|
+
### LeanMCP
|
|
423
251
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
252
|
+
For LeanMCP platform deployments with user secrets support:
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
const authProvider = new AuthProvider('leanmcp', {
|
|
256
|
+
apiKey: 'your-leanmcp-api-key'
|
|
257
|
+
});
|
|
258
|
+
await authProvider.init();
|
|
430
259
|
```
|
|
431
260
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Client Usage
|
|
264
|
+
|
|
265
|
+
Authentication tokens are passed via the `_meta` field following MCP protocol standards:
|
|
437
266
|
|
|
438
|
-
**User Data:**
|
|
439
267
|
```typescript
|
|
440
|
-
|
|
441
|
-
|
|
268
|
+
await mcpClient.callTool({
|
|
269
|
+
name: "analyzeSentiment",
|
|
270
|
+
arguments: { text: "Hello world" },
|
|
271
|
+
_meta: {
|
|
272
|
+
authorization: {
|
|
273
|
+
type: "bearer",
|
|
274
|
+
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
});
|
|
442
278
|
```
|
|
443
279
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Error Handling
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
import { AuthenticationError } from "@leanmcp/auth";
|
|
450
286
|
|
|
451
|
-
|
|
287
|
+
try {
|
|
288
|
+
await service.protectedMethod({ text: "test" });
|
|
289
|
+
} catch (error) {
|
|
290
|
+
if (error instanceof AuthenticationError) {
|
|
291
|
+
switch (error.code) {
|
|
292
|
+
case 'MISSING_TOKEN':
|
|
293
|
+
console.log('No token provided');
|
|
294
|
+
break;
|
|
295
|
+
case 'INVALID_TOKEN':
|
|
296
|
+
console.log('Token is invalid or expired');
|
|
297
|
+
break;
|
|
298
|
+
case 'VERIFICATION_FAILED':
|
|
299
|
+
console.log('Verification failed:', error.message);
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
452
305
|
|
|
453
|
-
|
|
454
|
-
- Custom JWT providers
|
|
306
|
+
---
|
|
455
307
|
|
|
456
308
|
## API Reference
|
|
457
309
|
|
|
@@ -462,7 +314,7 @@ class AuthProvider {
|
|
|
462
314
|
constructor(provider: string, config: any);
|
|
463
315
|
async init(config?: any): Promise<void>;
|
|
464
316
|
async verifyToken(token: string): Promise<boolean>;
|
|
465
|
-
async refreshToken(refreshToken: string
|
|
317
|
+
async refreshToken(refreshToken: string): Promise<any>;
|
|
466
318
|
async getUser(token: string): Promise<any>;
|
|
467
319
|
getProviderType(): string;
|
|
468
320
|
}
|
|
@@ -477,18 +329,11 @@ function Authenticated(
|
|
|
477
329
|
): ClassDecorator | MethodDecorator;
|
|
478
330
|
|
|
479
331
|
interface AuthenticatedOptions {
|
|
480
|
-
getUser?: boolean;
|
|
332
|
+
getUser?: boolean; // Default: true
|
|
333
|
+
projectId?: string; // For LeanMCP user secrets
|
|
481
334
|
}
|
|
482
335
|
```
|
|
483
336
|
|
|
484
|
-
Can be applied to:
|
|
485
|
-
- **Classes** - Protects all methods in the class
|
|
486
|
-
- **Methods** - Protects individual methods
|
|
487
|
-
|
|
488
|
-
**Options:**
|
|
489
|
-
- `getUser: true` (default) - Fetches user info and injects `authUser` variable
|
|
490
|
-
- `getUser: false` - Only validates token, skips user info fetch (faster)
|
|
491
|
-
|
|
492
337
|
### AuthenticationError
|
|
493
338
|
|
|
494
339
|
```typescript
|
|
@@ -501,409 +346,51 @@ class AuthenticationError extends Error {
|
|
|
501
346
|
### Helper Functions
|
|
502
347
|
|
|
503
348
|
```typescript
|
|
504
|
-
// Check if
|
|
505
|
-
function isAuthenticationRequired(target: any
|
|
349
|
+
// Check if authentication is required
|
|
350
|
+
function isAuthenticationRequired(target: any): boolean;
|
|
506
351
|
|
|
507
352
|
// Get auth provider for method/class
|
|
508
|
-
function getAuthProvider(target: any
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
## Environment Variables
|
|
512
|
-
|
|
513
|
-
### AWS Cognito
|
|
514
|
-
```bash
|
|
515
|
-
AWS_REGION=us-east-1
|
|
516
|
-
COGNITO_USER_POOL_ID=us-east-1_XXXXXXXXX
|
|
517
|
-
COGNITO_CLIENT_ID=your-client-id
|
|
518
|
-
```
|
|
519
|
-
|
|
520
|
-
### Clerk (Session Mode)
|
|
521
|
-
```bash
|
|
522
|
-
CLERK_FRONTEND_API=your-frontend-api.clerk.accounts.dev
|
|
523
|
-
CLERK_SECRET_KEY=sk_test_...
|
|
524
|
-
```
|
|
525
|
-
|
|
526
|
-
### Auth0
|
|
527
|
-
```bash
|
|
528
|
-
AUTH0_DOMAIN=your-tenant.auth0.com
|
|
529
|
-
AUTH0_CLIENT_ID=your-client-id
|
|
530
|
-
AUTH0_CLIENT_SECRET=your-client-secret
|
|
531
|
-
AUTH0_AUDIENCE=https://your-api-identifier
|
|
532
|
-
```
|
|
533
|
-
|
|
534
|
-
## Complete Examples
|
|
535
|
-
|
|
536
|
-
### AWS Cognito Example
|
|
537
|
-
|
|
538
|
-
See [examples/slack-with-auth](../../examples/slack-with-auth) for a complete working example.
|
|
539
|
-
|
|
540
|
-
```typescript
|
|
541
|
-
import { createHTTPServer, MCPServer } from "@leanmcp/core";
|
|
542
|
-
import { AuthProvider, Authenticated } from "@leanmcp/auth";
|
|
543
|
-
|
|
544
|
-
// Initialize auth
|
|
545
|
-
const authProvider = new AuthProvider('cognito', {
|
|
546
|
-
region: process.env.AWS_REGION,
|
|
547
|
-
userPoolId: process.env.COGNITO_USER_POOL_ID,
|
|
548
|
-
clientId: process.env.COGNITO_CLIENT_ID
|
|
549
|
-
});
|
|
550
|
-
await authProvider.init();
|
|
551
|
-
|
|
552
|
-
// Create service with protected methods
|
|
553
|
-
@Authenticated(authProvider)
|
|
554
|
-
class MyService {
|
|
555
|
-
@Tool({ description: 'Process protected data' })
|
|
556
|
-
async protectedTool(input: { data: string }) {
|
|
557
|
-
// authUser is automatically available
|
|
558
|
-
console.log('User:', authUser.sub);
|
|
559
|
-
console.log('Email:', authUser.email);
|
|
560
|
-
|
|
561
|
-
return {
|
|
562
|
-
result: "Protected data",
|
|
563
|
-
processedBy: authUser.sub,
|
|
564
|
-
userEmail: authUser.email
|
|
565
|
-
};
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
// Start server
|
|
570
|
-
const serverFactory = () => {
|
|
571
|
-
const server = new MCPServer({ name: "auth-server", version: "1.0.0" });
|
|
572
|
-
server.registerService(new MyService());
|
|
573
|
-
return server.getServer();
|
|
574
|
-
};
|
|
353
|
+
function getAuthProvider(target: any): AuthProviderBase | undefined;
|
|
575
354
|
|
|
576
|
-
|
|
355
|
+
// Get current authenticated user
|
|
356
|
+
function getAuthUser(): any;
|
|
577
357
|
```
|
|
578
358
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
```typescript
|
|
582
|
-
import { createHTTPServer, MCPServer } from "@leanmcp/core";
|
|
583
|
-
import { AuthProvider, Authenticated } from "@leanmcp/auth";
|
|
584
|
-
|
|
585
|
-
// Initialize Clerk in Session Mode
|
|
586
|
-
const authProvider = new AuthProvider('clerk', {
|
|
587
|
-
frontendApi: process.env.CLERK_FRONTEND_API,
|
|
588
|
-
secretKey: process.env.CLERK_SECRET_KEY
|
|
589
|
-
});
|
|
590
|
-
await authProvider.init();
|
|
591
|
-
|
|
592
|
-
// Or initialize in OAuth Mode (with refresh tokens)
|
|
593
|
-
const authProviderOAuth = new AuthProvider('clerk', {
|
|
594
|
-
frontendApi: process.env.CLERK_FRONTEND_API,
|
|
595
|
-
secretKey: process.env.CLERK_SECRET_KEY,
|
|
596
|
-
clientId: process.env.CLERK_CLIENT_ID,
|
|
597
|
-
clientSecret: process.env.CLERK_CLIENT_SECRET,
|
|
598
|
-
redirectUri: process.env.CLERK_REDIRECT_URI
|
|
599
|
-
});
|
|
600
|
-
await authProviderOAuth.init();
|
|
601
|
-
|
|
602
|
-
@Authenticated(authProvider)
|
|
603
|
-
class UserService {
|
|
604
|
-
@Tool({ description: 'Get user profile' })
|
|
605
|
-
async getProfile() {
|
|
606
|
-
// authUser is automatically available with Clerk user data
|
|
607
|
-
return {
|
|
608
|
-
userId: authUser.userId || authUser.sub,
|
|
609
|
-
email: authUser.email,
|
|
610
|
-
firstName: authUser.firstName,
|
|
611
|
-
lastName: authUser.lastName,
|
|
612
|
-
imageUrl: authUser.imageUrl
|
|
613
|
-
};
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
@Tool({ description: 'Create user post' })
|
|
617
|
-
async createPost(input: { title: string, content: string }) {
|
|
618
|
-
// Access authUser in any protected method
|
|
619
|
-
return {
|
|
620
|
-
id: generateId(),
|
|
621
|
-
title: input.title,
|
|
622
|
-
content: input.content,
|
|
623
|
-
authorId: authUser.userId,
|
|
624
|
-
authorEmail: authUser.email
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
const serverFactory = () => {
|
|
630
|
-
const server = new MCPServer({ name: "clerk-server", version: "1.0.0" });
|
|
631
|
-
server.registerService(new UserService());
|
|
632
|
-
return server.getServer();
|
|
633
|
-
};
|
|
634
|
-
|
|
635
|
-
await createHTTPServer(serverFactory, { port: 3000 });
|
|
636
|
-
```
|
|
637
|
-
|
|
638
|
-
### Auth0 Example
|
|
639
|
-
|
|
640
|
-
```typescript
|
|
641
|
-
import { createHTTPServer, MCPServer } from "@leanmcp/core";
|
|
642
|
-
import { AuthProvider, Authenticated } from "@leanmcp/auth";
|
|
643
|
-
|
|
644
|
-
// Initialize Auth0
|
|
645
|
-
const authProvider = new AuthProvider('auth0', {
|
|
646
|
-
domain: process.env.AUTH0_DOMAIN,
|
|
647
|
-
clientId: process.env.AUTH0_CLIENT_ID,
|
|
648
|
-
clientSecret: process.env.AUTH0_CLIENT_SECRET,
|
|
649
|
-
audience: process.env.AUTH0_AUDIENCE,
|
|
650
|
-
scopes: 'openid profile email offline_access'
|
|
651
|
-
});
|
|
652
|
-
await authProvider.init();
|
|
653
|
-
|
|
654
|
-
@Authenticated(authProvider)
|
|
655
|
-
class SecureAPIService {
|
|
656
|
-
@Tool({ description: 'Get sensitive data' })
|
|
657
|
-
async getSensitiveData(input: { dataId: string }) {
|
|
658
|
-
// authUser is automatically available with Auth0 user data
|
|
659
|
-
console.log('User:', authUser.sub);
|
|
660
|
-
console.log('Email:', authUser.email);
|
|
661
|
-
|
|
662
|
-
return {
|
|
663
|
-
dataId: input.dataId,
|
|
664
|
-
data: "Sensitive information",
|
|
665
|
-
accessedBy: authUser.sub,
|
|
666
|
-
userName: authUser.name
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
@Tool({ description: 'Update user settings' })
|
|
671
|
-
async updateSettings(input: { settings: Record<string, any> }) {
|
|
672
|
-
// Access authUser in any protected method
|
|
673
|
-
return {
|
|
674
|
-
success: true,
|
|
675
|
-
settings: input.settings,
|
|
676
|
-
userId: authUser.sub,
|
|
677
|
-
updatedBy: authUser.email
|
|
678
|
-
};
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
const serverFactory = () => {
|
|
683
|
-
const server = new MCPServer({ name: "auth0-server", version: "1.0.0" });
|
|
684
|
-
server.registerService(new SecureAPIService());
|
|
685
|
-
return server.getServer();
|
|
686
|
-
};
|
|
687
|
-
|
|
688
|
-
await createHTTPServer(serverFactory, { port: 3000 });
|
|
689
|
-
```
|
|
690
|
-
|
|
691
|
-
### Multi-Provider Example
|
|
692
|
-
|
|
693
|
-
```typescript
|
|
694
|
-
import { AuthProvider, Authenticated } from "@leanmcp/auth";
|
|
695
|
-
|
|
696
|
-
// Initialize multiple providers
|
|
697
|
-
const clerkAuth = new AuthProvider('clerk', {
|
|
698
|
-
frontendApi: process.env.CLERK_FRONTEND_API,
|
|
699
|
-
secretKey: process.env.CLERK_SECRET_KEY
|
|
700
|
-
});
|
|
701
|
-
await clerkAuth.init();
|
|
702
|
-
|
|
703
|
-
const auth0Auth = new AuthProvider('auth0', {
|
|
704
|
-
domain: process.env.AUTH0_DOMAIN,
|
|
705
|
-
clientId: process.env.AUTH0_CLIENT_ID,
|
|
706
|
-
audience: process.env.AUTH0_AUDIENCE
|
|
707
|
-
});
|
|
708
|
-
await auth0Auth.init();
|
|
709
|
-
|
|
710
|
-
// Use different providers for different services
|
|
711
|
-
@Authenticated(clerkAuth)
|
|
712
|
-
class UserService {
|
|
713
|
-
@Tool({ description: 'Get user data' })
|
|
714
|
-
async getUserData() {
|
|
715
|
-
// authUser contains Clerk user data
|
|
716
|
-
return {
|
|
717
|
-
userId: authUser.userId,
|
|
718
|
-
email: authUser.email,
|
|
719
|
-
firstName: authUser.firstName
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
@Authenticated(auth0Auth)
|
|
725
|
-
class AdminService {
|
|
726
|
-
@Tool({ description: 'Get admin data' })
|
|
727
|
-
async getAdminData() {
|
|
728
|
-
// authUser contains Auth0 user data
|
|
729
|
-
return {
|
|
730
|
-
adminId: authUser.sub,
|
|
731
|
-
email: authUser.email,
|
|
732
|
-
name: authUser.name
|
|
733
|
-
};
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
```
|
|
737
|
-
|
|
738
|
-
## How It Works
|
|
739
|
-
|
|
740
|
-
1. **Request arrives** with `_meta.authorization.token`
|
|
741
|
-
2. **Decorator intercepts** the method call before execution
|
|
742
|
-
3. **Token is extracted** from `_meta.authorization.token`
|
|
743
|
-
4. **Token is validated** using the configured auth provider
|
|
744
|
-
5. **User info is fetched** (if `getUser: true`) and decoded from JWT
|
|
745
|
-
6. **authUser is injected** as a global variable in method scope
|
|
746
|
-
7. **Method executes** with clean business arguments and access to `authUser`
|
|
747
|
-
8. **authUser is cleaned up** after method execution
|
|
748
|
-
9. **Response returns** to client
|
|
749
|
-
|
|
750
|
-
**Key Benefits:**
|
|
751
|
-
- **Clean separation** - Authentication metadata separate from business data
|
|
752
|
-
- **MCP compliant** - Follows standard `_meta` pattern
|
|
753
|
-
- **Type-safe** - Input classes don't need token fields
|
|
754
|
-
- **Automatic user injection** - Access user data via `authUser` without manual extraction
|
|
755
|
-
- **Reusable** - Same input classes work for authenticated and public methods
|
|
756
|
-
- **Secure** - `authUser` is scoped to method execution and cleaned up after
|
|
359
|
+
---
|
|
757
360
|
|
|
758
361
|
## Best Practices
|
|
759
362
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
6. **Use environment variables** - Never hardcode credentials
|
|
766
|
-
7. **Use _meta for auth** - Don't include tokens in business arguments
|
|
767
|
-
8. **Choose the right mode** - Use Session mode for simpler setups, OAuth mode for refresh tokens
|
|
768
|
-
9. **Test token expiration** - Ensure your app handles expired tokens gracefully
|
|
769
|
-
10. **Monitor JWKS cache** - Providers cache JWKS keys for performance
|
|
770
|
-
11. **Use authUser for user context** - Access user data via `authUser` instead of parsing tokens manually
|
|
771
|
-
12. **Type authUser when needed** - Cast `authUser` to a typed interface for better type safety
|
|
772
|
-
13. **Use getUser: false for performance** - Skip user fetch when you only need token validation
|
|
773
|
-
|
|
774
|
-
## Quick Reference
|
|
775
|
-
|
|
776
|
-
### Initialization Patterns
|
|
777
|
-
|
|
778
|
-
```typescript
|
|
779
|
-
// AWS Cognito
|
|
780
|
-
const cognito = new AuthProvider('cognito', {
|
|
781
|
-
region: 'us-east-1',
|
|
782
|
-
userPoolId: 'us-east-1_XXX',
|
|
783
|
-
clientId: 'xxx'
|
|
784
|
-
});
|
|
785
|
-
|
|
786
|
-
// Clerk (Session Mode)
|
|
787
|
-
const clerk = new AuthProvider('clerk', {
|
|
788
|
-
frontendApi: 'xxx.clerk.accounts.dev',
|
|
789
|
-
secretKey: 'sk_test_xxx'
|
|
790
|
-
});
|
|
363
|
+
### Security
|
|
364
|
+
- Always use HTTPS in production
|
|
365
|
+
- Store tokens securely (keychain, encrypted storage)
|
|
366
|
+
- Implement token refresh before expiration
|
|
367
|
+
- Add rate limiting to protect against brute force
|
|
791
368
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
clientId: 'xxx',
|
|
797
|
-
clientSecret: 'xxx',
|
|
798
|
-
redirectUri: 'https://app.com/callback'
|
|
799
|
-
});
|
|
369
|
+
### Configuration
|
|
370
|
+
- Use environment variables for credentials
|
|
371
|
+
- Never hardcode secrets in code
|
|
372
|
+
- Use `_meta` for auth, not business arguments
|
|
800
373
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
clientId: 'xxx',
|
|
805
|
-
clientSecret: 'xxx',
|
|
806
|
-
audience: 'https://api-identifier'
|
|
807
|
-
});
|
|
808
|
-
```
|
|
809
|
-
|
|
810
|
-
### Common Operations
|
|
811
|
-
|
|
812
|
-
```typescript
|
|
813
|
-
// Initialize
|
|
814
|
-
await authProvider.init();
|
|
374
|
+
### Performance
|
|
375
|
+
- Use `getUser: false` when you only need token validation
|
|
376
|
+
- JWKS keys are cached automatically for performance
|
|
815
377
|
|
|
816
|
-
|
|
817
|
-
const isValid = await authProvider.verifyToken(token);
|
|
378
|
+
---
|
|
818
379
|
|
|
819
|
-
|
|
820
|
-
const newTokens = await authProvider.refreshToken(refreshToken);
|
|
380
|
+
## Documentation
|
|
821
381
|
|
|
822
|
-
|
|
823
|
-
const user = await authProvider.getUser(idToken);
|
|
382
|
+
- [Full Documentation](https://docs.leanmcp.com/sdk/auth)
|
|
824
383
|
|
|
825
|
-
|
|
826
|
-
const type = authProvider.getProviderType(); // 'cognito' | 'clerk' | 'auth0'
|
|
827
|
-
```
|
|
828
|
-
|
|
829
|
-
### Decorator Usage
|
|
830
|
-
|
|
831
|
-
```typescript
|
|
832
|
-
// Protect single method with authUser (default)
|
|
833
|
-
@Authenticated(authProvider)
|
|
834
|
-
async myMethod(input: { data: string }) {
|
|
835
|
-
console.log(authUser.sub); // User ID available
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
// Protect method without fetching user (faster)
|
|
839
|
-
@Authenticated(authProvider, { getUser: false })
|
|
840
|
-
async fastMethod(input: { data: string }) {
|
|
841
|
-
// Only token validation, no authUser
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
// Protect entire class
|
|
845
|
-
@Authenticated(authProvider)
|
|
846
|
-
class MyService {
|
|
847
|
-
@Tool() async method1() {
|
|
848
|
-
// authUser available in all methods
|
|
849
|
-
return { userId: authUser.sub };
|
|
850
|
-
}
|
|
851
|
-
@Tool() async method2() {
|
|
852
|
-
return { email: authUser.email };
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
// Check if authentication required
|
|
857
|
-
const required = isAuthenticationRequired(target, 'methodName');
|
|
858
|
-
|
|
859
|
-
// Get auth provider for method
|
|
860
|
-
const provider = getAuthProvider(target, 'methodName');
|
|
861
|
-
```
|
|
862
|
-
|
|
863
|
-
### authUser Quick Reference
|
|
384
|
+
## Related Packages
|
|
864
385
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
@Authenticated(authProvider)
|
|
868
|
-
async createPost(input: { title: string }) {
|
|
869
|
-
// authUser is automatically available
|
|
870
|
-
const userId = authUser.sub; // User ID (all providers)
|
|
871
|
-
const email = authUser.email; // Email (all providers)
|
|
872
|
-
|
|
873
|
-
// Provider-specific fields
|
|
874
|
-
const clerkId = authUser.userId; // Clerk only
|
|
875
|
-
const firstName = authUser.firstName; // Clerk only
|
|
876
|
-
const cognitoGroups = authUser['cognito:groups']; // Cognito only
|
|
877
|
-
const auth0Name = authUser.name; // Auth0 only
|
|
878
|
-
|
|
879
|
-
return { authorId: userId, authorEmail: email };
|
|
880
|
-
}
|
|
386
|
+
- [@leanmcp/core](https://www.npmjs.com/package/@leanmcp/core) — Core decorators and server functionality
|
|
387
|
+
- [@leanmcp/env-injection](https://www.npmjs.com/package/@leanmcp/env-injection) — Environment variable injection for user secrets
|
|
881
388
|
|
|
882
|
-
|
|
883
|
-
interface MyUser {
|
|
884
|
-
sub: string;
|
|
885
|
-
email: string;
|
|
886
|
-
name?: string;
|
|
887
|
-
}
|
|
389
|
+
## Links
|
|
888
390
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
const user = authUser as MyUser;
|
|
892
|
-
console.log(user.email); // Fully typed
|
|
893
|
-
}
|
|
894
|
-
```
|
|
391
|
+
- [GitHub Repository](https://github.com/LeanMCP/leanmcp-sdk)
|
|
392
|
+
- [NPM Package](https://www.npmjs.com/package/@leanmcp/auth)
|
|
895
393
|
|
|
896
394
|
## License
|
|
897
395
|
|
|
898
396
|
MIT
|
|
899
|
-
|
|
900
|
-
## Related Packages
|
|
901
|
-
|
|
902
|
-
- [@leanmcp/core](../core) - Core MCP server functionality
|
|
903
|
-
- [@leanmcp/cli](../cli) - CLI tool for creating new projects
|
|
904
|
-
- [@leanmcp/utils](../utils) - Utility functions
|
|
905
|
-
|
|
906
|
-
## Links
|
|
907
|
-
|
|
908
|
-
- [GitHub Repository](https://github.com/LeanMCP/leanmcp-sdk)
|
|
909
|
-
- [Documentation](https://github.com/LeanMCP/leanmcp-sdk#readme)
|