@leanmcp/auth 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,11 +5,13 @@ Authentication module for LeanMCP providing token-based authentication decorator
5
5
  ## Features
6
6
 
7
7
  - **@Authenticated decorator** - Protect MCP tools, prompts, and resources with token authentication
8
- - **Multi-provider support** - AWS Cognito (more providers coming soon)
8
+ - **Automatic authUser injection** - Access decoded user info via global `authUser` variable in protected methods
9
+ - **Multi-provider support** - AWS Cognito, Clerk, Auth0
9
10
  - **Method or class-level protection** - Apply to individual methods or entire services
10
11
  - **Automatic token validation** - Validates tokens before method execution
11
12
  - **Custom error handling** - Detailed error codes for different auth failures
12
- - **Type-safe** - Full TypeScript support with type inference
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
13
15
 
14
16
  ## Installation
15
17
 
@@ -24,6 +26,16 @@ For AWS Cognito:
24
26
  npm install @aws-sdk/client-cognito-identity-provider axios jsonwebtoken jwk-to-pem
25
27
  ```
26
28
 
29
+ For Clerk:
30
+ ```bash
31
+ npm install axios jsonwebtoken jwk-to-pem
32
+ ```
33
+
34
+ For Auth0:
35
+ ```bash
36
+ npm install axios jsonwebtoken jwk-to-pem
37
+ ```
38
+
27
39
  ## Quick Start
28
40
 
29
41
  ### 1. Initialize Auth Provider
@@ -48,13 +60,20 @@ import { Tool } from "@leanmcp/core";
48
60
  import { Authenticated } from "@leanmcp/auth";
49
61
 
50
62
  export class SentimentService {
51
- // This method requires authentication
63
+ // This method requires authentication with automatic user info
52
64
  @Tool({ description: 'Analyze sentiment (requires auth)' })
53
- @Authenticated(authProvider)
65
+ @Authenticated(authProvider) // getUser: true by default
54
66
  async analyzeSentiment(input: { text: string }) {
55
67
  // Token is automatically validated from _meta.authorization.token
56
- // Only business arguments are passed to the method
57
- return { sentiment: 'positive', score: 0.8 };
68
+ // authUser is automatically available with decoded JWT payload
69
+ console.log('User ID:', authUser.sub);
70
+ console.log('Email:', authUser.email);
71
+
72
+ return {
73
+ sentiment: 'positive',
74
+ score: 0.8,
75
+ analyzedBy: authUser.sub
76
+ };
58
77
  }
59
78
 
60
79
  // This method is public
@@ -75,16 +94,134 @@ import { Authenticated } from "@leanmcp/auth";
75
94
  export class SecureService {
76
95
  @Tool({ description: 'Protected tool 1' })
77
96
  async tool1(input: { data: string }) {
78
- // All methods require authentication via _meta
97
+ // authUser is automatically available in all methods
98
+ console.log('Authenticated user:', authUser.email);
99
+ return { data: input.data, userId: authUser.sub };
79
100
  }
80
101
 
81
102
  @Tool({ description: 'Protected tool 2' })
82
103
  async tool2(input: { data: string }) {
83
- // All methods require authentication via _meta
104
+ // authUser is available here too
105
+ return { data: input.data, userId: authUser.sub };
84
106
  }
85
107
  }
86
108
  ```
87
109
 
110
+ ## authUser Variable
111
+
112
+ ### Automatic User Information Injection
113
+
114
+ When you use the `@Authenticated` decorator, a global `authUser` variable is automatically injected into your protected methods containing the decoded JWT token payload.
115
+
116
+ ```typescript
117
+ @Tool({ description: 'Create post' })
118
+ @Authenticated(authProvider)
119
+ 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
+ return {
125
+ id: generateId(),
126
+ title: input.title,
127
+ content: input.content,
128
+ authorId: authUser.sub,
129
+ authorEmail: authUser.email
130
+ };
131
+ }
132
+ ```
133
+
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
+ ### Provider-Specific User Data
155
+
156
+ The structure of `authUser` depends on your authentication provider:
157
+
158
+ **AWS Cognito:**
159
+ ```typescript
160
+ {
161
+ sub: 'user-uuid',
162
+ email: 'user@example.com',
163
+ email_verified: true,
164
+ 'cognito:username': 'username',
165
+ 'cognito:groups': ['admin', 'users'],
166
+ // ... other Cognito claims
167
+ }
168
+ ```
169
+
170
+ **Clerk:**
171
+ ```typescript
172
+ {
173
+ sub: 'user_2abc123xyz',
174
+ userId: 'user_2abc123xyz',
175
+ email: 'user@example.com',
176
+ firstName: 'John',
177
+ lastName: 'Doe',
178
+ imageUrl: 'https://img.clerk.com/...',
179
+ // ... other Clerk claims
180
+ }
181
+ ```
182
+
183
+ **Auth0:**
184
+ ```typescript
185
+ {
186
+ sub: 'auth0|507f1f77bcf86cd799439011',
187
+ email: 'user@example.com',
188
+ email_verified: true,
189
+ 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
222
+ }
223
+ ```
224
+
88
225
  ## Usage
89
226
 
90
227
  ### Client Side - Calling Protected Methods
@@ -169,6 +306,17 @@ try {
169
306
 
170
307
  ## Supported Auth Providers
171
308
 
309
+ ### Provider Comparison
310
+
311
+ | Feature | AWS Cognito | Clerk | Auth0 |
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 |
319
+
172
320
  ### AWS Cognito
173
321
 
174
322
  ```typescript
@@ -185,10 +333,123 @@ await authProvider.init();
185
333
  - Token must be valid and not expired
186
334
  - Token must be issued by the configured User Pool
187
335
 
336
+ **Environment Variables:**
337
+ ```bash
338
+ AWS_REGION=us-east-1
339
+ COGNITO_USER_POOL_ID=us-east-1_XXXXXXXXX
340
+ COGNITO_CLIENT_ID=your-client-id
341
+ ```
342
+
343
+ ### Clerk
344
+
345
+ Clerk supports both **Session Mode** (default) and **OAuth Mode** (with refresh tokens).
346
+
347
+ #### Session Mode (Default)
348
+
349
+ ```typescript
350
+ const authProvider = new AuthProvider('clerk', {
351
+ frontendApi: 'your-frontend-api.clerk.accounts.dev',
352
+ secretKey: 'sk_test_...'
353
+ });
354
+ await authProvider.init();
355
+ ```
356
+
357
+ **Configuration:**
358
+ - `frontendApi` - Your Clerk Frontend API domain
359
+ - `secretKey` - Your Clerk Secret Key
360
+
361
+ **Environment Variables:**
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
370
+ const authProvider = new AuthProvider('clerk', {
371
+ frontendApi: 'your-frontend-api.clerk.accounts.dev',
372
+ secretKey: 'sk_test_...',
373
+ clientId: 'your-oauth-client-id',
374
+ clientSecret: 'your-oauth-client-secret',
375
+ redirectUri: 'https://yourapp.com/callback'
376
+ });
377
+ await authProvider.init();
378
+
379
+ // Refresh tokens when needed
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 }
398
+ ```
399
+
400
+ ### Auth0
401
+
402
+ ```typescript
403
+ const authProvider = new AuthProvider('auth0', {
404
+ domain: 'your-tenant.auth0.com',
405
+ clientId: 'your-client-id',
406
+ clientSecret: 'your-client-secret', // Optional for public clients
407
+ audience: 'https://your-api-identifier',
408
+ scopes: 'openid profile email offline_access' // Optional, defaults shown
409
+ });
410
+ 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
+ ```
416
+
417
+ **Configuration:**
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`)
423
+
424
+ **Environment Variables:**
425
+ ```bash
426
+ AUTH0_DOMAIN=your-tenant.auth0.com
427
+ AUTH0_CLIENT_ID=your-client-id
428
+ AUTH0_CLIENT_SECRET=your-client-secret
429
+ AUTH0_AUDIENCE=https://your-api-identifier
430
+ ```
431
+
432
+ **Token Requirements:**
433
+ - JWT token from Auth0 (access token or ID token)
434
+ - Token must be valid and not expired
435
+ - Token must be issued by your Auth0 tenant
436
+ - Token must have the correct audience
437
+
438
+ **User Data:**
439
+ ```typescript
440
+ const user = await authProvider.getUser(idToken);
441
+ // Returns: { sub, email, email_verified, name, attributes }
442
+ ```
443
+
444
+ **Error Handling:**
445
+ Auth0 provider includes detailed error messages:
446
+ - `Token has expired` - Token is expired
447
+ - `Invalid token signature` - Token signature verification failed
448
+ - `Malformed token` - Token format is invalid
449
+ - `Invalid token issuer` - Token issuer doesn't match
450
+
188
451
  ### More Providers Coming Soon
189
452
 
190
- - Clerk
191
- - Auth0
192
453
  - Firebase Auth
193
454
  - Custom JWT providers
194
455
 
@@ -210,13 +471,24 @@ class AuthProvider {
210
471
  ### @Authenticated Decorator
211
472
 
212
473
  ```typescript
213
- function Authenticated(authProvider: AuthProvider): ClassDecorator | MethodDecorator;
474
+ function Authenticated(
475
+ authProvider: AuthProvider,
476
+ options?: AuthenticatedOptions
477
+ ): ClassDecorator | MethodDecorator;
478
+
479
+ interface AuthenticatedOptions {
480
+ getUser?: boolean; // Default: true
481
+ }
214
482
  ```
215
483
 
216
484
  Can be applied to:
217
485
  - **Classes** - Protects all methods in the class
218
486
  - **Methods** - Protects individual methods
219
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
+
220
492
  ### AuthenticationError
221
493
 
222
494
  ```typescript
@@ -238,16 +510,32 @@ function getAuthProvider(target: any, propertyKey?: string): AuthProvider | unde
238
510
 
239
511
  ## Environment Variables
240
512
 
241
- For AWS Cognito:
513
+ ### AWS Cognito
242
514
  ```bash
243
515
  AWS_REGION=us-east-1
244
516
  COGNITO_USER_POOL_ID=us-east-1_XXXXXXXXX
245
517
  COGNITO_CLIENT_ID=your-client-id
246
518
  ```
247
519
 
248
- ## Complete Example
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
+ ```
249
533
 
250
- See [examples/slack-with-auth](../../examples/slack-with-auth) for a complete working example with AWS Cognito.
534
+ ## Complete Examples
535
+
536
+ ### AWS Cognito Example
537
+
538
+ See [examples/slack-with-auth](../../examples/slack-with-auth) for a complete working example.
251
539
 
252
540
  ```typescript
253
541
  import { createHTTPServer, MCPServer } from "@leanmcp/core";
@@ -264,9 +552,17 @@ await authProvider.init();
264
552
  // Create service with protected methods
265
553
  @Authenticated(authProvider)
266
554
  class MyService {
267
- @Tool()
555
+ @Tool({ description: 'Process protected data' })
268
556
  async protectedTool(input: { data: string }) {
269
- return { result: "Protected data" };
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
+ };
270
566
  }
271
567
  }
272
568
 
@@ -280,20 +576,184 @@ const serverFactory = () => {
280
576
  await createHTTPServer(serverFactory, { port: 3000 });
281
577
  ```
282
578
 
579
+ ### Clerk Example
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
+
283
738
  ## How It Works
284
739
 
285
740
  1. **Request arrives** with `_meta.authorization.token`
286
741
  2. **Decorator intercepts** the method call before execution
287
742
  3. **Token is extracted** from `_meta.authorization.token`
288
743
  4. **Token is validated** using the configured auth provider
289
- 5. **Method executes** with clean business arguments (no token)
290
- 6. **Response returns** to client
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
291
749
 
292
750
  **Key Benefits:**
293
751
  - **Clean separation** - Authentication metadata separate from business data
294
752
  - **MCP compliant** - Follows standard `_meta` pattern
295
753
  - **Type-safe** - Input classes don't need token fields
754
+ - **Automatic user injection** - Access user data via `authUser` without manual extraction
296
755
  - **Reusable** - Same input classes work for authenticated and public methods
756
+ - **Secure** - `authUser` is scoped to method execution and cleaned up after
297
757
 
298
758
  ## Best Practices
299
759
 
@@ -304,6 +764,134 @@ await createHTTPServer(serverFactory, { port: 3000 });
304
764
  5. **Log authentication failures** - Monitor for suspicious activity
305
765
  6. **Use environment variables** - Never hardcode credentials
306
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
+ });
791
+
792
+ // Clerk (OAuth Mode)
793
+ const clerkOAuth = new AuthProvider('clerk', {
794
+ frontendApi: 'xxx.clerk.accounts.dev',
795
+ secretKey: 'sk_test_xxx',
796
+ clientId: 'xxx',
797
+ clientSecret: 'xxx',
798
+ redirectUri: 'https://app.com/callback'
799
+ });
800
+
801
+ // Auth0
802
+ const auth0 = new AuthProvider('auth0', {
803
+ domain: 'tenant.auth0.com',
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();
815
+
816
+ // Verify token
817
+ const isValid = await authProvider.verifyToken(token);
818
+
819
+ // Refresh token (OAuth/Auth0 only)
820
+ const newTokens = await authProvider.refreshToken(refreshToken);
821
+
822
+ // Get user data
823
+ const user = await authProvider.getUser(idToken);
824
+
825
+ // Get provider type
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
864
+
865
+ ```typescript
866
+ // Access user data in protected methods
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
+ }
881
+
882
+ // Type authUser for better type safety
883
+ interface MyUser {
884
+ sub: string;
885
+ email: string;
886
+ name?: string;
887
+ }
888
+
889
+ @Authenticated(authProvider)
890
+ async typedMethod(input: any) {
891
+ const user = authUser as MyUser;
892
+ console.log(user.email); // Fully typed
893
+ }
894
+ ```
307
895
 
308
896
  ## License
309
897