@leanmcp/auth 0.4.0 → 0.4.2

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
@@ -1,396 +1,396 @@
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>
31
-
32
- ## Features
33
-
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
40
-
41
- ## Installation
42
-
43
- ```bash
44
- npm install @leanmcp/auth @leanmcp/core
45
- ```
46
-
47
- ### Provider Dependencies
48
-
49
- **AWS Cognito:**
50
- ```bash
51
- npm install @aws-sdk/client-cognito-identity-provider axios jsonwebtoken jwk-to-pem
52
- ```
53
-
54
- **Clerk:**
55
- ```bash
56
- npm install axios jsonwebtoken jwk-to-pem
57
- ```
58
-
59
- **Auth0:**
60
- ```bash
61
- npm install axios jsonwebtoken jwk-to-pem
62
- ```
63
-
64
- ## Quick Start
65
-
66
- ### 1. Initialize Auth Provider
67
-
68
- ```typescript
69
- import { AuthProvider } from "@leanmcp/auth";
70
-
71
- const authProvider = new AuthProvider('cognito', {
72
- region: 'us-east-1',
73
- userPoolId: 'us-east-1_XXXXXXXXX',
74
- clientId: 'your-client-id'
75
- });
76
-
77
- await authProvider.init();
78
- ```
79
-
80
- ### 2. Protect Methods with @Authenticated
81
-
82
- ```typescript
83
- import { Tool } from "@leanmcp/core";
84
- import { Authenticated } from "@leanmcp/auth";
85
-
86
- export class SentimentService {
87
- @Tool({ description: 'Analyze sentiment (requires auth)' })
88
- @Authenticated(authProvider)
89
- async analyzeSentiment(input: { text: string }) {
90
- // authUser is automatically available with user info
91
- console.log('User ID:', authUser.sub);
92
- console.log('Email:', authUser.email);
93
-
94
- return {
95
- sentiment: 'positive',
96
- score: 0.8,
97
- analyzedBy: authUser.sub
98
- };
99
- }
100
-
101
- // Public method - no authentication
102
- @Tool({ description: 'Get categories (public)' })
103
- async getCategories() {
104
- return { categories: ['positive', 'negative', 'neutral'] };
105
- }
106
- }
107
- ```
108
-
109
- ### 3. Protect Entire Service
110
-
111
- ```typescript
112
- // All methods in this class require authentication
113
- @Authenticated(authProvider)
114
- export class SecureService {
115
- @Tool({ description: 'Protected tool' })
116
- async protectedTool(input: { data: string }) {
117
- // authUser is available in all methods
118
- return { data: input.data, userId: authUser.sub };
119
- }
120
- }
121
- ```
122
-
123
- ---
124
-
125
- ## The authUser Variable
126
-
127
- When using `@Authenticated`, a global `authUser` variable is automatically injected containing the decoded JWT payload:
128
-
129
- ```typescript
130
- @Tool({ description: 'Create post' })
131
- @Authenticated(authProvider)
132
- async createPost(input: { title: string, content: string }) {
133
- return {
134
- id: generateId(),
135
- title: input.title,
136
- content: input.content,
137
- authorId: authUser.sub,
138
- authorEmail: authUser.email
139
- };
140
- }
141
- ```
142
-
143
- ### Provider-Specific User Data
144
-
145
- **AWS Cognito:**
146
- ```typescript
147
- {
148
- sub: 'user-uuid',
149
- email: 'user@example.com',
150
- email_verified: true,
151
- 'cognito:username': 'username',
152
- 'cognito:groups': ['admin', 'users']
153
- }
154
- ```
155
-
156
- **Clerk:**
157
- ```typescript
158
- {
159
- sub: 'user_2abc123xyz',
160
- userId: 'user_2abc123xyz',
161
- email: 'user@example.com',
162
- firstName: 'John',
163
- lastName: 'Doe',
164
- imageUrl: 'https://img.clerk.com/...'
165
- }
166
- ```
167
-
168
- **Auth0:**
169
- ```typescript
170
- {
171
- sub: 'auth0|507f1f77bcf86cd799439011',
172
- email: 'user@example.com',
173
- email_verified: true,
174
- name: 'John Doe',
175
- picture: 'https://s.gravatar.com/avatar/...'
176
- }
177
- ```
178
-
179
- ### Controlling User Fetch
180
-
181
- ```typescript
182
- // Fetch user info (default)
183
- @Authenticated(authProvider, { getUser: true })
184
- async withUserInfo(input: any) {
185
- console.log(authUser); // User data available
186
- }
187
-
188
- // Only verify token, skip user fetch (faster)
189
- @Authenticated(authProvider, { getUser: false })
190
- async tokenOnlyValidation(input: any) {
191
- // authUser is undefined
192
- }
193
- ```
194
-
195
- ---
196
-
197
- ## Supported Providers
198
-
199
- ### AWS Cognito
200
-
201
- ```typescript
202
- const authProvider = new AuthProvider('cognito', {
203
- region: 'us-east-1',
204
- userPoolId: 'us-east-1_XXXXXXXXX',
205
- clientId: 'your-client-id'
206
- });
207
- await authProvider.init();
208
- ```
209
-
210
- **Environment Variables:**
211
- ```bash
212
- AWS_REGION=us-east-1
213
- COGNITO_USER_POOL_ID=us-east-1_XXXXXXXXX
214
- COGNITO_CLIENT_ID=your-client-id
215
- ```
216
-
217
- ### Clerk
218
-
219
- ```typescript
220
- // Session Mode (default)
221
- const authProvider = new AuthProvider('clerk', {
222
- frontendApi: 'your-frontend-api.clerk.accounts.dev',
223
- secretKey: 'sk_test_...'
224
- });
225
-
226
- // OAuth Mode (with refresh tokens)
227
- const authProvider = new AuthProvider('clerk', {
228
- frontendApi: 'your-frontend-api.clerk.accounts.dev',
229
- secretKey: 'sk_test_...',
230
- clientId: 'your-oauth-client-id',
231
- clientSecret: 'your-oauth-client-secret',
232
- redirectUri: 'https://yourapp.com/callback'
233
- });
234
-
235
- await authProvider.init();
236
- ```
237
-
238
- ### Auth0
239
-
240
- ```typescript
241
- const authProvider = new AuthProvider('auth0', {
242
- domain: 'your-tenant.auth0.com',
243
- clientId: 'your-client-id',
244
- clientSecret: 'your-client-secret',
245
- audience: 'https://your-api-identifier'
246
- });
247
- await authProvider.init();
248
- ```
249
-
250
- ### LeanMCP
251
-
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();
259
- ```
260
-
261
- ---
262
-
263
- ## Client Usage
264
-
265
- Authentication tokens are passed via the `_meta` field following MCP protocol standards:
266
-
267
- ```typescript
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
- });
278
- ```
279
-
280
- ---
281
-
282
- ## Error Handling
283
-
284
- ```typescript
285
- import { AuthenticationError } from "@leanmcp/auth";
286
-
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
- ```
305
-
306
- ---
307
-
308
- ## API Reference
309
-
310
- ### AuthProvider
311
-
312
- ```typescript
313
- class AuthProvider {
314
- constructor(provider: string, config: any);
315
- async init(config?: any): Promise<void>;
316
- async verifyToken(token: string): Promise<boolean>;
317
- async refreshToken(refreshToken: string): Promise<any>;
318
- async getUser(token: string): Promise<any>;
319
- getProviderType(): string;
320
- }
321
- ```
322
-
323
- ### @Authenticated Decorator
324
-
325
- ```typescript
326
- function Authenticated(
327
- authProvider: AuthProvider,
328
- options?: AuthenticatedOptions
329
- ): ClassDecorator | MethodDecorator;
330
-
331
- interface AuthenticatedOptions {
332
- getUser?: boolean; // Default: true
333
- projectId?: string; // For LeanMCP user secrets
334
- }
335
- ```
336
-
337
- ### AuthenticationError
338
-
339
- ```typescript
340
- class AuthenticationError extends Error {
341
- code: 'MISSING_TOKEN' | 'INVALID_TOKEN' | 'VERIFICATION_FAILED';
342
- constructor(message: string, code: string);
343
- }
344
- ```
345
-
346
- ### Helper Functions
347
-
348
- ```typescript
349
- // Check if authentication is required
350
- function isAuthenticationRequired(target: any): boolean;
351
-
352
- // Get auth provider for method/class
353
- function getAuthProvider(target: any): AuthProviderBase | undefined;
354
-
355
- // Get current authenticated user
356
- function getAuthUser(): any;
357
- ```
358
-
359
- ---
360
-
361
- ## Best Practices
362
-
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
368
-
369
- ### Configuration
370
- - Use environment variables for credentials
371
- - Never hardcode secrets in code
372
- - Use `_meta` for auth, not business arguments
373
-
374
- ### Performance
375
- - Use `getUser: false` when you only need token validation
376
- - JWKS keys are cached automatically for performance
377
-
378
- ---
379
-
380
- ## Documentation
381
-
382
- - [Full Documentation](https://docs.leanmcp.com/sdk/auth)
383
-
384
- ## Related Packages
385
-
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
388
-
389
- ## Links
390
-
391
- - [GitHub Repository](https://github.com/LeanMCP/leanmcp-sdk)
392
- - [NPM Package](https://www.npmjs.com/package/@leanmcp/auth)
393
-
394
- ## License
395
-
396
- MIT
1
+ <p align="center">
2
+ <img
3
+ src="https://raw.githubusercontent.com/LeanMCP/leanmcp-sdk/refs/heads/main/assets/logo.png"
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>
31
+
32
+ ## Features
33
+
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
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ npm install @leanmcp/auth @leanmcp/core
45
+ ```
46
+
47
+ ### Provider Dependencies
48
+
49
+ **AWS Cognito:**
50
+ ```bash
51
+ npm install @aws-sdk/client-cognito-identity-provider axios jsonwebtoken jwk-to-pem
52
+ ```
53
+
54
+ **Clerk:**
55
+ ```bash
56
+ npm install axios jsonwebtoken jwk-to-pem
57
+ ```
58
+
59
+ **Auth0:**
60
+ ```bash
61
+ npm install axios jsonwebtoken jwk-to-pem
62
+ ```
63
+
64
+ ## Quick Start
65
+
66
+ ### 1. Initialize Auth Provider
67
+
68
+ ```typescript
69
+ import { AuthProvider } from "@leanmcp/auth";
70
+
71
+ const authProvider = new AuthProvider('cognito', {
72
+ region: 'us-east-1',
73
+ userPoolId: 'us-east-1_XXXXXXXXX',
74
+ clientId: 'your-client-id'
75
+ });
76
+
77
+ await authProvider.init();
78
+ ```
79
+
80
+ ### 2. Protect Methods with @Authenticated
81
+
82
+ ```typescript
83
+ import { Tool } from "@leanmcp/core";
84
+ import { Authenticated } from "@leanmcp/auth";
85
+
86
+ export class SentimentService {
87
+ @Tool({ description: 'Analyze sentiment (requires auth)' })
88
+ @Authenticated(authProvider)
89
+ async analyzeSentiment(input: { text: string }) {
90
+ // authUser is automatically available with user info
91
+ console.log('User ID:', authUser.sub);
92
+ console.log('Email:', authUser.email);
93
+
94
+ return {
95
+ sentiment: 'positive',
96
+ score: 0.8,
97
+ analyzedBy: authUser.sub
98
+ };
99
+ }
100
+
101
+ // Public method - no authentication
102
+ @Tool({ description: 'Get categories (public)' })
103
+ async getCategories() {
104
+ return { categories: ['positive', 'negative', 'neutral'] };
105
+ }
106
+ }
107
+ ```
108
+
109
+ ### 3. Protect Entire Service
110
+
111
+ ```typescript
112
+ // All methods in this class require authentication
113
+ @Authenticated(authProvider)
114
+ export class SecureService {
115
+ @Tool({ description: 'Protected tool' })
116
+ async protectedTool(input: { data: string }) {
117
+ // authUser is available in all methods
118
+ return { data: input.data, userId: authUser.sub };
119
+ }
120
+ }
121
+ ```
122
+
123
+ ---
124
+
125
+ ## The authUser Variable
126
+
127
+ When using `@Authenticated`, a global `authUser` variable is automatically injected containing the decoded JWT payload:
128
+
129
+ ```typescript
130
+ @Tool({ description: 'Create post' })
131
+ @Authenticated(authProvider)
132
+ async createPost(input: { title: string, content: string }) {
133
+ return {
134
+ id: generateId(),
135
+ title: input.title,
136
+ content: input.content,
137
+ authorId: authUser.sub,
138
+ authorEmail: authUser.email
139
+ };
140
+ }
141
+ ```
142
+
143
+ ### Provider-Specific User Data
144
+
145
+ **AWS Cognito:**
146
+ ```typescript
147
+ {
148
+ sub: 'user-uuid',
149
+ email: 'user@example.com',
150
+ email_verified: true,
151
+ 'cognito:username': 'username',
152
+ 'cognito:groups': ['admin', 'users']
153
+ }
154
+ ```
155
+
156
+ **Clerk:**
157
+ ```typescript
158
+ {
159
+ sub: 'user_2abc123xyz',
160
+ userId: 'user_2abc123xyz',
161
+ email: 'user@example.com',
162
+ firstName: 'John',
163
+ lastName: 'Doe',
164
+ imageUrl: 'https://img.clerk.com/...'
165
+ }
166
+ ```
167
+
168
+ **Auth0:**
169
+ ```typescript
170
+ {
171
+ sub: 'auth0|507f1f77bcf86cd799439011',
172
+ email: 'user@example.com',
173
+ email_verified: true,
174
+ name: 'John Doe',
175
+ picture: 'https://s.gravatar.com/avatar/...'
176
+ }
177
+ ```
178
+
179
+ ### Controlling User Fetch
180
+
181
+ ```typescript
182
+ // Fetch user info (default)
183
+ @Authenticated(authProvider, { getUser: true })
184
+ async withUserInfo(input: any) {
185
+ console.log(authUser); // User data available
186
+ }
187
+
188
+ // Only verify token, skip user fetch (faster)
189
+ @Authenticated(authProvider, { getUser: false })
190
+ async tokenOnlyValidation(input: any) {
191
+ // authUser is undefined
192
+ }
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Supported Providers
198
+
199
+ ### AWS Cognito
200
+
201
+ ```typescript
202
+ const authProvider = new AuthProvider('cognito', {
203
+ region: 'us-east-1',
204
+ userPoolId: 'us-east-1_XXXXXXXXX',
205
+ clientId: 'your-client-id'
206
+ });
207
+ await authProvider.init();
208
+ ```
209
+
210
+ **Environment Variables:**
211
+ ```bash
212
+ AWS_REGION=us-east-1
213
+ COGNITO_USER_POOL_ID=us-east-1_XXXXXXXXX
214
+ COGNITO_CLIENT_ID=your-client-id
215
+ ```
216
+
217
+ ### Clerk
218
+
219
+ ```typescript
220
+ // Session Mode (default)
221
+ const authProvider = new AuthProvider('clerk', {
222
+ frontendApi: 'your-frontend-api.clerk.accounts.dev',
223
+ secretKey: 'sk_test_...'
224
+ });
225
+
226
+ // OAuth Mode (with refresh tokens)
227
+ const authProvider = new AuthProvider('clerk', {
228
+ frontendApi: 'your-frontend-api.clerk.accounts.dev',
229
+ secretKey: 'sk_test_...',
230
+ clientId: 'your-oauth-client-id',
231
+ clientSecret: 'your-oauth-client-secret',
232
+ redirectUri: 'https://yourapp.com/callback'
233
+ });
234
+
235
+ await authProvider.init();
236
+ ```
237
+
238
+ ### Auth0
239
+
240
+ ```typescript
241
+ const authProvider = new AuthProvider('auth0', {
242
+ domain: 'your-tenant.auth0.com',
243
+ clientId: 'your-client-id',
244
+ clientSecret: 'your-client-secret',
245
+ audience: 'https://your-api-identifier'
246
+ });
247
+ await authProvider.init();
248
+ ```
249
+
250
+ ### LeanMCP
251
+
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();
259
+ ```
260
+
261
+ ---
262
+
263
+ ## Client Usage
264
+
265
+ Authentication tokens are passed via the `_meta` field following MCP protocol standards:
266
+
267
+ ```typescript
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
+ });
278
+ ```
279
+
280
+ ---
281
+
282
+ ## Error Handling
283
+
284
+ ```typescript
285
+ import { AuthenticationError } from "@leanmcp/auth";
286
+
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
+ ```
305
+
306
+ ---
307
+
308
+ ## API Reference
309
+
310
+ ### AuthProvider
311
+
312
+ ```typescript
313
+ class AuthProvider {
314
+ constructor(provider: string, config: any);
315
+ async init(config?: any): Promise<void>;
316
+ async verifyToken(token: string): Promise<boolean>;
317
+ async refreshToken(refreshToken: string): Promise<any>;
318
+ async getUser(token: string): Promise<any>;
319
+ getProviderType(): string;
320
+ }
321
+ ```
322
+
323
+ ### @Authenticated Decorator
324
+
325
+ ```typescript
326
+ function Authenticated(
327
+ authProvider: AuthProvider,
328
+ options?: AuthenticatedOptions
329
+ ): ClassDecorator | MethodDecorator;
330
+
331
+ interface AuthenticatedOptions {
332
+ getUser?: boolean; // Default: true
333
+ projectId?: string; // For LeanMCP user secrets
334
+ }
335
+ ```
336
+
337
+ ### AuthenticationError
338
+
339
+ ```typescript
340
+ class AuthenticationError extends Error {
341
+ code: 'MISSING_TOKEN' | 'INVALID_TOKEN' | 'VERIFICATION_FAILED';
342
+ constructor(message: string, code: string);
343
+ }
344
+ ```
345
+
346
+ ### Helper Functions
347
+
348
+ ```typescript
349
+ // Check if authentication is required
350
+ function isAuthenticationRequired(target: any): boolean;
351
+
352
+ // Get auth provider for method/class
353
+ function getAuthProvider(target: any): AuthProviderBase | undefined;
354
+
355
+ // Get current authenticated user
356
+ function getAuthUser(): any;
357
+ ```
358
+
359
+ ---
360
+
361
+ ## Best Practices
362
+
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
368
+
369
+ ### Configuration
370
+ - Use environment variables for credentials
371
+ - Never hardcode secrets in code
372
+ - Use `_meta` for auth, not business arguments
373
+
374
+ ### Performance
375
+ - Use `getUser: false` when you only need token validation
376
+ - JWKS keys are cached automatically for performance
377
+
378
+ ---
379
+
380
+ ## Documentation
381
+
382
+ - [Full Documentation](https://docs.leanmcp.com/sdk/auth)
383
+
384
+ ## Related Packages
385
+
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
388
+
389
+ ## Links
390
+
391
+ - [GitHub Repository](https://github.com/LeanMCP/leanmcp-sdk)
392
+ - [NPM Package](https://www.npmjs.com/package/@leanmcp/auth)
393
+
394
+ ## License
395
+
396
+ MIT