@leanmcp/auth 0.1.0 → 0.2.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 +353 -6
- package/dist/auth0-A25F3NOH.mjs +102 -0
- package/dist/{chunk-NALGJYQB.mjs → chunk-KKO5VLFX.mjs} +13 -8
- package/dist/clerk-XNMGPYSN.mjs +115 -0
- package/dist/{cognito-GBSAAMZI.mjs → cognito-2RRSLFKI.mjs} +1 -1
- package/dist/index.js +237 -7
- package/dist/index.mjs +1 -1
- package/package.json +5 -3
- package/dist/chunk-YC7GFXAO.mjs +0 -193
- package/dist/cognito-VCVS77OX.mjs +0 -145
package/README.md
CHANGED
|
@@ -5,11 +5,12 @@ 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
|
|
8
|
+
- **Multi-provider support** - AWS Cognito, Clerk, Auth0
|
|
9
9
|
- **Method or class-level protection** - Apply to individual methods or entire services
|
|
10
10
|
- **Automatic token validation** - Validates tokens before method execution
|
|
11
11
|
- **Custom error handling** - Detailed error codes for different auth failures
|
|
12
12
|
- **Type-safe** - Full TypeScript support with type inference
|
|
13
|
+
- **OAuth & Session modes** - Support for both session-based and OAuth refresh token flows
|
|
13
14
|
|
|
14
15
|
## Installation
|
|
15
16
|
|
|
@@ -24,6 +25,16 @@ For AWS Cognito:
|
|
|
24
25
|
npm install @aws-sdk/client-cognito-identity-provider axios jsonwebtoken jwk-to-pem
|
|
25
26
|
```
|
|
26
27
|
|
|
28
|
+
For Clerk:
|
|
29
|
+
```bash
|
|
30
|
+
npm install axios jsonwebtoken jwk-to-pem
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
For Auth0:
|
|
34
|
+
```bash
|
|
35
|
+
npm install axios jsonwebtoken jwk-to-pem
|
|
36
|
+
```
|
|
37
|
+
|
|
27
38
|
## Quick Start
|
|
28
39
|
|
|
29
40
|
### 1. Initialize Auth Provider
|
|
@@ -169,6 +180,17 @@ try {
|
|
|
169
180
|
|
|
170
181
|
## Supported Auth Providers
|
|
171
182
|
|
|
183
|
+
### Provider Comparison
|
|
184
|
+
|
|
185
|
+
| Feature | AWS Cognito | Clerk | Auth0 |
|
|
186
|
+
|---------|-------------|-------|-------|
|
|
187
|
+
| **JWT Verification** | ✅ JWKS | ✅ JWKS | ✅ JWKS |
|
|
188
|
+
| **Refresh Tokens** | ✅ Yes | ✅ Yes (OAuth mode) | ✅ Yes |
|
|
189
|
+
| **Session Mode** | ❌ No | ✅ Yes (default) | ❌ No |
|
|
190
|
+
| **OAuth Mode** | ✅ Yes | ✅ Yes | ✅ Yes |
|
|
191
|
+
| **User Data** | ✅ Yes | ✅ Yes | ✅ Yes |
|
|
192
|
+
| **Setup Complexity** | Low | Low | Low |
|
|
193
|
+
|
|
172
194
|
### AWS Cognito
|
|
173
195
|
|
|
174
196
|
```typescript
|
|
@@ -185,10 +207,123 @@ await authProvider.init();
|
|
|
185
207
|
- Token must be valid and not expired
|
|
186
208
|
- Token must be issued by the configured User Pool
|
|
187
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
|
+
Clerk supports both **Session Mode** (default) and **OAuth Mode** (with refresh tokens).
|
|
220
|
+
|
|
221
|
+
#### Session Mode (Default)
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
const authProvider = new AuthProvider('clerk', {
|
|
225
|
+
frontendApi: 'your-frontend-api.clerk.accounts.dev',
|
|
226
|
+
secretKey: 'sk_test_...'
|
|
227
|
+
});
|
|
228
|
+
await authProvider.init();
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Configuration:**
|
|
232
|
+
- `frontendApi` - Your Clerk Frontend API domain
|
|
233
|
+
- `secretKey` - Your Clerk Secret Key
|
|
234
|
+
|
|
235
|
+
**Environment Variables:**
|
|
236
|
+
```bash
|
|
237
|
+
CLERK_FRONTEND_API=your-frontend-api.clerk.accounts.dev
|
|
238
|
+
CLERK_SECRET_KEY=sk_test_...
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
#### OAuth Mode (Refresh Tokens)
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
const authProvider = new AuthProvider('clerk', {
|
|
245
|
+
frontendApi: 'your-frontend-api.clerk.accounts.dev',
|
|
246
|
+
secretKey: 'sk_test_...',
|
|
247
|
+
clientId: 'your-oauth-client-id',
|
|
248
|
+
clientSecret: 'your-oauth-client-secret',
|
|
249
|
+
redirectUri: 'https://yourapp.com/callback'
|
|
250
|
+
});
|
|
251
|
+
await authProvider.init();
|
|
252
|
+
|
|
253
|
+
// Refresh tokens when needed
|
|
254
|
+
const newTokens = await authProvider.refreshToken(refreshToken);
|
|
255
|
+
// Returns: { access_token, id_token, refresh_token }
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**OAuth Configuration:**
|
|
259
|
+
- `clientId` - OAuth Client ID from Clerk
|
|
260
|
+
- `clientSecret` - OAuth Client Secret from Clerk
|
|
261
|
+
- `redirectUri` - OAuth redirect URI
|
|
262
|
+
|
|
263
|
+
**Token Requirements:**
|
|
264
|
+
- JWT token from Clerk (ID token or session token)
|
|
265
|
+
- Token must be valid and not expired
|
|
266
|
+
- Token must be issued by your Clerk instance
|
|
267
|
+
|
|
268
|
+
**User Data:**
|
|
269
|
+
```typescript
|
|
270
|
+
const user = await authProvider.getUser(idToken);
|
|
271
|
+
// Returns: { sub, email, email_verified, first_name, last_name, attributes }
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Auth0
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
const authProvider = new AuthProvider('auth0', {
|
|
278
|
+
domain: 'your-tenant.auth0.com',
|
|
279
|
+
clientId: 'your-client-id',
|
|
280
|
+
clientSecret: 'your-client-secret', // Optional for public clients
|
|
281
|
+
audience: 'https://your-api-identifier',
|
|
282
|
+
scopes: 'openid profile email offline_access' // Optional, defaults shown
|
|
283
|
+
});
|
|
284
|
+
await authProvider.init();
|
|
285
|
+
|
|
286
|
+
// Refresh tokens when needed
|
|
287
|
+
const newTokens = await authProvider.refreshToken(refreshToken);
|
|
288
|
+
// Returns: { access_token, id_token, refresh_token, expires_in }
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
**Configuration:**
|
|
292
|
+
- `domain` - Your Auth0 tenant domain (e.g., `your-tenant.auth0.com`)
|
|
293
|
+
- `clientId` - Your Auth0 Application Client ID
|
|
294
|
+
- `clientSecret` - Your Auth0 Application Client Secret (optional for public clients)
|
|
295
|
+
- `audience` - Your API identifier (required for API access)
|
|
296
|
+
- `scopes` - OAuth scopes (default: `openid profile email offline_access`)
|
|
297
|
+
|
|
298
|
+
**Environment Variables:**
|
|
299
|
+
```bash
|
|
300
|
+
AUTH0_DOMAIN=your-tenant.auth0.com
|
|
301
|
+
AUTH0_CLIENT_ID=your-client-id
|
|
302
|
+
AUTH0_CLIENT_SECRET=your-client-secret
|
|
303
|
+
AUTH0_AUDIENCE=https://your-api-identifier
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
**Token Requirements:**
|
|
307
|
+
- JWT token from Auth0 (access token or ID token)
|
|
308
|
+
- Token must be valid and not expired
|
|
309
|
+
- Token must be issued by your Auth0 tenant
|
|
310
|
+
- Token must have the correct audience
|
|
311
|
+
|
|
312
|
+
**User Data:**
|
|
313
|
+
```typescript
|
|
314
|
+
const user = await authProvider.getUser(idToken);
|
|
315
|
+
// Returns: { sub, email, email_verified, name, attributes }
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Error Handling:**
|
|
319
|
+
Auth0 provider includes detailed error messages:
|
|
320
|
+
- `Token has expired` - Token is expired
|
|
321
|
+
- `Invalid token signature` - Token signature verification failed
|
|
322
|
+
- `Malformed token` - Token format is invalid
|
|
323
|
+
- `Invalid token issuer` - Token issuer doesn't match
|
|
324
|
+
|
|
188
325
|
### More Providers Coming Soon
|
|
189
326
|
|
|
190
|
-
- Clerk
|
|
191
|
-
- Auth0
|
|
192
327
|
- Firebase Auth
|
|
193
328
|
- Custom JWT providers
|
|
194
329
|
|
|
@@ -238,16 +373,32 @@ function getAuthProvider(target: any, propertyKey?: string): AuthProvider | unde
|
|
|
238
373
|
|
|
239
374
|
## Environment Variables
|
|
240
375
|
|
|
241
|
-
|
|
376
|
+
### AWS Cognito
|
|
242
377
|
```bash
|
|
243
378
|
AWS_REGION=us-east-1
|
|
244
379
|
COGNITO_USER_POOL_ID=us-east-1_XXXXXXXXX
|
|
245
380
|
COGNITO_CLIENT_ID=your-client-id
|
|
246
381
|
```
|
|
247
382
|
|
|
248
|
-
|
|
383
|
+
### Clerk (Session Mode)
|
|
384
|
+
```bash
|
|
385
|
+
CLERK_FRONTEND_API=your-frontend-api.clerk.accounts.dev
|
|
386
|
+
CLERK_SECRET_KEY=sk_test_...
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Auth0
|
|
390
|
+
```bash
|
|
391
|
+
AUTH0_DOMAIN=your-tenant.auth0.com
|
|
392
|
+
AUTH0_CLIENT_ID=your-client-id
|
|
393
|
+
AUTH0_CLIENT_SECRET=your-client-secret
|
|
394
|
+
AUTH0_AUDIENCE=https://your-api-identifier
|
|
395
|
+
```
|
|
249
396
|
|
|
250
|
-
|
|
397
|
+
## Complete Examples
|
|
398
|
+
|
|
399
|
+
### AWS Cognito Example
|
|
400
|
+
|
|
401
|
+
See [examples/slack-with-auth](../../examples/slack-with-auth) for a complete working example.
|
|
251
402
|
|
|
252
403
|
```typescript
|
|
253
404
|
import { createHTTPServer, MCPServer } from "@leanmcp/core";
|
|
@@ -280,6 +431,123 @@ const serverFactory = () => {
|
|
|
280
431
|
await createHTTPServer(serverFactory, { port: 3000 });
|
|
281
432
|
```
|
|
282
433
|
|
|
434
|
+
### Clerk Example
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
import { createHTTPServer, MCPServer } from "@leanmcp/core";
|
|
438
|
+
import { AuthProvider, Authenticated } from "@leanmcp/auth";
|
|
439
|
+
|
|
440
|
+
// Initialize Clerk in Session Mode
|
|
441
|
+
const authProvider = new AuthProvider('clerk', {
|
|
442
|
+
frontendApi: process.env.CLERK_FRONTEND_API,
|
|
443
|
+
secretKey: process.env.CLERK_SECRET_KEY
|
|
444
|
+
});
|
|
445
|
+
await authProvider.init();
|
|
446
|
+
|
|
447
|
+
// Or initialize in OAuth Mode (with refresh tokens)
|
|
448
|
+
const authProviderOAuth = new AuthProvider('clerk', {
|
|
449
|
+
frontendApi: process.env.CLERK_FRONTEND_API,
|
|
450
|
+
secretKey: process.env.CLERK_SECRET_KEY,
|
|
451
|
+
clientId: process.env.CLERK_CLIENT_ID,
|
|
452
|
+
clientSecret: process.env.CLERK_CLIENT_SECRET,
|
|
453
|
+
redirectUri: process.env.CLERK_REDIRECT_URI
|
|
454
|
+
});
|
|
455
|
+
await authProviderOAuth.init();
|
|
456
|
+
|
|
457
|
+
@Authenticated(authProvider)
|
|
458
|
+
class UserService {
|
|
459
|
+
@Tool({ description: 'Get user profile' })
|
|
460
|
+
async getProfile(input: { userId: string }) {
|
|
461
|
+
// Token is automatically validated
|
|
462
|
+
return { userId: input.userId, name: "John Doe" };
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const serverFactory = () => {
|
|
467
|
+
const server = new MCPServer({ name: "clerk-server", version: "1.0.0" });
|
|
468
|
+
server.registerService(new UserService());
|
|
469
|
+
return server.getServer();
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
await createHTTPServer(serverFactory, { port: 3000 });
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### Auth0 Example
|
|
476
|
+
|
|
477
|
+
```typescript
|
|
478
|
+
import { createHTTPServer, MCPServer } from "@leanmcp/core";
|
|
479
|
+
import { AuthProvider, Authenticated } from "@leanmcp/auth";
|
|
480
|
+
|
|
481
|
+
// Initialize Auth0
|
|
482
|
+
const authProvider = new AuthProvider('auth0', {
|
|
483
|
+
domain: process.env.AUTH0_DOMAIN,
|
|
484
|
+
clientId: process.env.AUTH0_CLIENT_ID,
|
|
485
|
+
clientSecret: process.env.AUTH0_CLIENT_SECRET,
|
|
486
|
+
audience: process.env.AUTH0_AUDIENCE,
|
|
487
|
+
scopes: 'openid profile email offline_access'
|
|
488
|
+
});
|
|
489
|
+
await authProvider.init();
|
|
490
|
+
|
|
491
|
+
@Authenticated(authProvider)
|
|
492
|
+
class SecureAPIService {
|
|
493
|
+
@Tool({ description: 'Get sensitive data' })
|
|
494
|
+
async getSensitiveData(input: { dataId: string }) {
|
|
495
|
+
// Token is automatically validated
|
|
496
|
+
return { dataId: input.dataId, data: "Sensitive information" };
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
@Tool({ description: 'Update user settings' })
|
|
500
|
+
async updateSettings(input: { settings: Record<string, any> }) {
|
|
501
|
+
return { success: true, settings: input.settings };
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const serverFactory = () => {
|
|
506
|
+
const server = new MCPServer({ name: "auth0-server", version: "1.0.0" });
|
|
507
|
+
server.registerService(new SecureAPIService());
|
|
508
|
+
return server.getServer();
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
await createHTTPServer(serverFactory, { port: 3000 });
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### Multi-Provider Example
|
|
515
|
+
|
|
516
|
+
```typescript
|
|
517
|
+
import { AuthProvider, Authenticated } from "@leanmcp/auth";
|
|
518
|
+
|
|
519
|
+
// Initialize multiple providers
|
|
520
|
+
const clerkAuth = new AuthProvider('clerk', {
|
|
521
|
+
frontendApi: process.env.CLERK_FRONTEND_API,
|
|
522
|
+
secretKey: process.env.CLERK_SECRET_KEY
|
|
523
|
+
});
|
|
524
|
+
await clerkAuth.init();
|
|
525
|
+
|
|
526
|
+
const auth0Auth = new AuthProvider('auth0', {
|
|
527
|
+
domain: process.env.AUTH0_DOMAIN,
|
|
528
|
+
clientId: process.env.AUTH0_CLIENT_ID,
|
|
529
|
+
audience: process.env.AUTH0_AUDIENCE
|
|
530
|
+
});
|
|
531
|
+
await auth0Auth.init();
|
|
532
|
+
|
|
533
|
+
// Use different providers for different services
|
|
534
|
+
@Authenticated(clerkAuth)
|
|
535
|
+
class UserService {
|
|
536
|
+
@Tool()
|
|
537
|
+
async getUserData(input: { userId: string }) {
|
|
538
|
+
return { userId: input.userId };
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
@Authenticated(auth0Auth)
|
|
543
|
+
class AdminService {
|
|
544
|
+
@Tool()
|
|
545
|
+
async getAdminData(input: { adminId: string }) {
|
|
546
|
+
return { adminId: input.adminId };
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
```
|
|
550
|
+
|
|
283
551
|
## How It Works
|
|
284
552
|
|
|
285
553
|
1. **Request arrives** with `_meta.authorization.token`
|
|
@@ -304,6 +572,85 @@ await createHTTPServer(serverFactory, { port: 3000 });
|
|
|
304
572
|
5. **Log authentication failures** - Monitor for suspicious activity
|
|
305
573
|
6. **Use environment variables** - Never hardcode credentials
|
|
306
574
|
7. **Use _meta for auth** - Don't include tokens in business arguments
|
|
575
|
+
8. **Choose the right mode** - Use Session mode for simpler setups, OAuth mode for refresh tokens
|
|
576
|
+
9. **Test token expiration** - Ensure your app handles expired tokens gracefully
|
|
577
|
+
10. **Monitor JWKS cache** - Providers cache JWKS keys for performance
|
|
578
|
+
|
|
579
|
+
## Quick Reference
|
|
580
|
+
|
|
581
|
+
### Initialization Patterns
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
// AWS Cognito
|
|
585
|
+
const cognito = new AuthProvider('cognito', {
|
|
586
|
+
region: 'us-east-1',
|
|
587
|
+
userPoolId: 'us-east-1_XXX',
|
|
588
|
+
clientId: 'xxx'
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
// Clerk (Session Mode)
|
|
592
|
+
const clerk = new AuthProvider('clerk', {
|
|
593
|
+
frontendApi: 'xxx.clerk.accounts.dev',
|
|
594
|
+
secretKey: 'sk_test_xxx'
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
// Clerk (OAuth Mode)
|
|
598
|
+
const clerkOAuth = new AuthProvider('clerk', {
|
|
599
|
+
frontendApi: 'xxx.clerk.accounts.dev',
|
|
600
|
+
secretKey: 'sk_test_xxx',
|
|
601
|
+
clientId: 'xxx',
|
|
602
|
+
clientSecret: 'xxx',
|
|
603
|
+
redirectUri: 'https://app.com/callback'
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
// Auth0
|
|
607
|
+
const auth0 = new AuthProvider('auth0', {
|
|
608
|
+
domain: 'tenant.auth0.com',
|
|
609
|
+
clientId: 'xxx',
|
|
610
|
+
clientSecret: 'xxx',
|
|
611
|
+
audience: 'https://api-identifier'
|
|
612
|
+
});
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
### Common Operations
|
|
616
|
+
|
|
617
|
+
```typescript
|
|
618
|
+
// Initialize
|
|
619
|
+
await authProvider.init();
|
|
620
|
+
|
|
621
|
+
// Verify token
|
|
622
|
+
const isValid = await authProvider.verifyToken(token);
|
|
623
|
+
|
|
624
|
+
// Refresh token (OAuth/Auth0 only)
|
|
625
|
+
const newTokens = await authProvider.refreshToken(refreshToken);
|
|
626
|
+
|
|
627
|
+
// Get user data
|
|
628
|
+
const user = await authProvider.getUser(idToken);
|
|
629
|
+
|
|
630
|
+
// Get provider type
|
|
631
|
+
const type = authProvider.getProviderType(); // 'cognito' | 'clerk' | 'auth0'
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
### Decorator Usage
|
|
635
|
+
|
|
636
|
+
```typescript
|
|
637
|
+
// Protect single method
|
|
638
|
+
@Authenticated(authProvider)
|
|
639
|
+
async myMethod(input: { data: string }) { }
|
|
640
|
+
|
|
641
|
+
// Protect entire class
|
|
642
|
+
@Authenticated(authProvider)
|
|
643
|
+
class MyService {
|
|
644
|
+
@Tool() async method1() { }
|
|
645
|
+
@Tool() async method2() { }
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// Check if authentication required
|
|
649
|
+
const required = isAuthenticationRequired(target, 'methodName');
|
|
650
|
+
|
|
651
|
+
// Get auth provider for method
|
|
652
|
+
const provider = getAuthProvider(target, 'methodName');
|
|
653
|
+
```
|
|
307
654
|
|
|
308
655
|
## License
|
|
309
656
|
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuthProviderBase,
|
|
3
|
+
__name
|
|
4
|
+
} from "./chunk-KKO5VLFX.mjs";
|
|
5
|
+
|
|
6
|
+
// src/providers/auth0.ts
|
|
7
|
+
import axios from "axios";
|
|
8
|
+
import jwt from "jsonwebtoken";
|
|
9
|
+
import jwkToPem from "jwk-to-pem";
|
|
10
|
+
var AuthAuth0 = class extends AuthProviderBase {
|
|
11
|
+
static {
|
|
12
|
+
__name(this, "AuthAuth0");
|
|
13
|
+
}
|
|
14
|
+
domain = "";
|
|
15
|
+
clientId = "";
|
|
16
|
+
clientSecret = "";
|
|
17
|
+
audience = "";
|
|
18
|
+
scopes = "openid profile email offline_access";
|
|
19
|
+
jwksCache = null;
|
|
20
|
+
async init(config) {
|
|
21
|
+
this.domain = config?.domain || process.env.AUTH0_DOMAIN || "";
|
|
22
|
+
this.clientId = config?.clientId || process.env.AUTH0_CLIENT_ID || "";
|
|
23
|
+
this.clientSecret = config?.clientSecret || process.env.AUTH0_CLIENT_SECRET || "";
|
|
24
|
+
this.audience = config?.audience || process.env.AUTH0_AUDIENCE || "";
|
|
25
|
+
this.scopes = config?.scopes || this.scopes;
|
|
26
|
+
if (!this.domain || !this.clientId || !this.audience) {
|
|
27
|
+
throw new Error("Auth0 config missing: domain, clientId, and audience are required");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async refreshToken(refreshToken) {
|
|
31
|
+
const url = `https://${this.domain}/oauth/token`;
|
|
32
|
+
const payload = {
|
|
33
|
+
grant_type: "refresh_token",
|
|
34
|
+
client_id: this.clientId,
|
|
35
|
+
refresh_token: refreshToken,
|
|
36
|
+
audience: this.audience,
|
|
37
|
+
scope: this.scopes
|
|
38
|
+
};
|
|
39
|
+
if (this.clientSecret) {
|
|
40
|
+
payload.client_secret = this.clientSecret;
|
|
41
|
+
}
|
|
42
|
+
const { data } = await axios.post(url, payload, {
|
|
43
|
+
headers: {
|
|
44
|
+
"Content-Type": "application/json"
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
return data;
|
|
48
|
+
}
|
|
49
|
+
async verifyToken(token) {
|
|
50
|
+
try {
|
|
51
|
+
await this.verifyJwt(token);
|
|
52
|
+
return true;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
if (error instanceof Error) {
|
|
55
|
+
if (error.message.includes("jwt expired")) throw new Error("Token has expired");
|
|
56
|
+
if (error.message.includes("invalid signature")) throw new Error("Invalid token signature");
|
|
57
|
+
if (error.message.includes("jwt malformed")) throw new Error("Malformed token");
|
|
58
|
+
if (error.message.includes("invalid issuer")) throw new Error("Invalid token issuer");
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async getUser(idToken) {
|
|
65
|
+
const decoded = jwt.decode(idToken);
|
|
66
|
+
if (!decoded) throw new Error("Invalid ID token");
|
|
67
|
+
return {
|
|
68
|
+
sub: decoded.sub,
|
|
69
|
+
email: decoded.email,
|
|
70
|
+
email_verified: decoded.email_verified,
|
|
71
|
+
name: decoded.name,
|
|
72
|
+
attributes: decoded
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async fetchJWKS() {
|
|
76
|
+
if (!this.jwksCache) {
|
|
77
|
+
const jwksUri = `https://${this.domain}/.well-known/jwks.json`;
|
|
78
|
+
const { data } = await axios.get(jwksUri);
|
|
79
|
+
this.jwksCache = data.keys;
|
|
80
|
+
}
|
|
81
|
+
return this.jwksCache;
|
|
82
|
+
}
|
|
83
|
+
async verifyJwt(token) {
|
|
84
|
+
const decoded = jwt.decode(token, {
|
|
85
|
+
complete: true
|
|
86
|
+
});
|
|
87
|
+
if (!decoded) throw new Error("Invalid token");
|
|
88
|
+
const jwks = await this.fetchJWKS();
|
|
89
|
+
const key = jwks.find((k) => k.kid === decoded.header.kid);
|
|
90
|
+
if (!key) throw new Error("Signing key not found in JWKS");
|
|
91
|
+
const pem = jwkToPem(key);
|
|
92
|
+
return jwt.verify(token, pem, {
|
|
93
|
+
algorithms: [
|
|
94
|
+
"RS256"
|
|
95
|
+
],
|
|
96
|
+
issuer: `https://${this.domain}/`
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
export {
|
|
101
|
+
AuthAuth0
|
|
102
|
+
};
|
|
@@ -123,18 +123,23 @@ var AuthProvider = class extends AuthProviderBase {
|
|
|
123
123
|
const finalConfig = config || this.config;
|
|
124
124
|
switch (this.providerType) {
|
|
125
125
|
case "cognito": {
|
|
126
|
-
const { AuthCognito } = await import("./cognito-
|
|
126
|
+
const { AuthCognito } = await import("./cognito-2RRSLFKI.mjs");
|
|
127
127
|
this.providerInstance = new AuthCognito();
|
|
128
128
|
await this.providerInstance.init(finalConfig);
|
|
129
129
|
break;
|
|
130
130
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
131
|
+
case "auth0": {
|
|
132
|
+
const { AuthAuth0 } = await import("./auth0-A25F3NOH.mjs");
|
|
133
|
+
this.providerInstance = new AuthAuth0();
|
|
134
|
+
await this.providerInstance.init(finalConfig);
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
case "clerk": {
|
|
138
|
+
const { AuthClerk } = await import("./clerk-XNMGPYSN.mjs");
|
|
139
|
+
this.providerInstance = new AuthClerk();
|
|
140
|
+
await this.providerInstance.init(finalConfig);
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
138
143
|
default:
|
|
139
144
|
throw new Error(`Unsupported auth provider: ${this.providerType}. Supported providers: cognito`);
|
|
140
145
|
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuthProviderBase,
|
|
3
|
+
__name
|
|
4
|
+
} from "./chunk-KKO5VLFX.mjs";
|
|
5
|
+
|
|
6
|
+
// src/providers/clerk.ts
|
|
7
|
+
import axios from "axios";
|
|
8
|
+
import jwt from "jsonwebtoken";
|
|
9
|
+
import jwkToPem from "jwk-to-pem";
|
|
10
|
+
var AuthClerk = class extends AuthProviderBase {
|
|
11
|
+
static {
|
|
12
|
+
__name(this, "AuthClerk");
|
|
13
|
+
}
|
|
14
|
+
clerkFrontendApi = "";
|
|
15
|
+
clerkSecretKey = "";
|
|
16
|
+
clerkJWKSUrl = "";
|
|
17
|
+
clerkIssuer = "";
|
|
18
|
+
jwksCache = null;
|
|
19
|
+
mode = "session";
|
|
20
|
+
oauthTokenUrl = "";
|
|
21
|
+
clientId;
|
|
22
|
+
clientSecret;
|
|
23
|
+
redirectUri;
|
|
24
|
+
/**
|
|
25
|
+
* Initialize Clerk Auth Provider
|
|
26
|
+
*/
|
|
27
|
+
async init(config) {
|
|
28
|
+
this.clerkFrontendApi = config?.frontendApi || process.env.CLERK_FRONTEND_API || "";
|
|
29
|
+
this.clerkSecretKey = config?.secretKey || process.env.CLERK_SECRET_KEY || "";
|
|
30
|
+
if (!this.clerkFrontendApi || !this.clerkSecretKey) {
|
|
31
|
+
throw new Error("Missing Clerk configuration: frontendApi and secretKey are required");
|
|
32
|
+
}
|
|
33
|
+
this.clerkIssuer = `https://${this.clerkFrontendApi}`;
|
|
34
|
+
this.clerkJWKSUrl = `${this.clerkIssuer}/.well-known/jwks.json`;
|
|
35
|
+
if (config?.clientId && config?.clientSecret && config?.redirectUri) {
|
|
36
|
+
this.mode = "oauth";
|
|
37
|
+
this.clientId = config.clientId;
|
|
38
|
+
this.clientSecret = config.clientSecret;
|
|
39
|
+
this.redirectUri = config.redirectUri;
|
|
40
|
+
this.oauthTokenUrl = `${this.clerkIssuer}/oauth/token`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Refresh tokens (OAuth mode only)
|
|
45
|
+
*/
|
|
46
|
+
async refreshToken(refreshToken) {
|
|
47
|
+
if (this.mode !== "oauth") {
|
|
48
|
+
throw new Error("Clerk is in Session Mode: refresh tokens are not supported. Enable OAuth mode.");
|
|
49
|
+
}
|
|
50
|
+
const payload = {
|
|
51
|
+
grant_type: "refresh_token",
|
|
52
|
+
refresh_token: refreshToken,
|
|
53
|
+
client_id: this.clientId,
|
|
54
|
+
client_secret: this.clientSecret,
|
|
55
|
+
redirect_uri: this.redirectUri
|
|
56
|
+
};
|
|
57
|
+
const { data } = await axios.post(this.oauthTokenUrl, payload, {
|
|
58
|
+
headers: {
|
|
59
|
+
"Content-Type": "application/json"
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return data;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Verify JWT using JWKS
|
|
66
|
+
*/
|
|
67
|
+
async verifyToken(token) {
|
|
68
|
+
await this.verifyJwt(token);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Extract user data from ID token
|
|
73
|
+
*/
|
|
74
|
+
async getUser(idToken) {
|
|
75
|
+
const decoded = jwt.decode(idToken);
|
|
76
|
+
if (!decoded) throw new Error("Invalid ID token");
|
|
77
|
+
return {
|
|
78
|
+
sub: decoded.sub,
|
|
79
|
+
email: decoded.email,
|
|
80
|
+
email_verified: decoded.email_verified,
|
|
81
|
+
first_name: decoded.given_name,
|
|
82
|
+
last_name: decoded.family_name,
|
|
83
|
+
attributes: decoded
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* JWT verification using JWKS
|
|
88
|
+
*/
|
|
89
|
+
async verifyJwt(token) {
|
|
90
|
+
const decoded = jwt.decode(token, {
|
|
91
|
+
complete: true
|
|
92
|
+
});
|
|
93
|
+
if (!decoded) throw new Error("Invalid token");
|
|
94
|
+
const jwks = await this.fetchJWKS();
|
|
95
|
+
const key = jwks.find((k) => k.kid === decoded.header.kid);
|
|
96
|
+
if (!key) throw new Error("Signing key not found in JWKS");
|
|
97
|
+
const pem = jwkToPem(key);
|
|
98
|
+
return jwt.verify(token, pem, {
|
|
99
|
+
algorithms: [
|
|
100
|
+
"RS256"
|
|
101
|
+
],
|
|
102
|
+
issuer: this.clerkIssuer
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
async fetchJWKS() {
|
|
106
|
+
if (!this.jwksCache) {
|
|
107
|
+
const { data } = await axios.get(this.clerkJWKSUrl);
|
|
108
|
+
this.jwksCache = data.keys;
|
|
109
|
+
}
|
|
110
|
+
return this.jwksCache;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
export {
|
|
114
|
+
AuthClerk
|
|
115
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -280,6 +280,231 @@ var init_cognito = __esm({
|
|
|
280
280
|
}
|
|
281
281
|
});
|
|
282
282
|
|
|
283
|
+
// src/providers/auth0.ts
|
|
284
|
+
var auth0_exports = {};
|
|
285
|
+
__export(auth0_exports, {
|
|
286
|
+
AuthAuth0: () => AuthAuth0
|
|
287
|
+
});
|
|
288
|
+
var import_axios2, import_jsonwebtoken2, import_jwk_to_pem2, AuthAuth0;
|
|
289
|
+
var init_auth0 = __esm({
|
|
290
|
+
"src/providers/auth0.ts"() {
|
|
291
|
+
"use strict";
|
|
292
|
+
import_axios2 = __toESM(require("axios"));
|
|
293
|
+
import_jsonwebtoken2 = __toESM(require("jsonwebtoken"));
|
|
294
|
+
import_jwk_to_pem2 = __toESM(require("jwk-to-pem"));
|
|
295
|
+
init_index();
|
|
296
|
+
AuthAuth0 = class extends AuthProviderBase {
|
|
297
|
+
static {
|
|
298
|
+
__name(this, "AuthAuth0");
|
|
299
|
+
}
|
|
300
|
+
domain = "";
|
|
301
|
+
clientId = "";
|
|
302
|
+
clientSecret = "";
|
|
303
|
+
audience = "";
|
|
304
|
+
scopes = "openid profile email offline_access";
|
|
305
|
+
jwksCache = null;
|
|
306
|
+
async init(config) {
|
|
307
|
+
this.domain = config?.domain || process.env.AUTH0_DOMAIN || "";
|
|
308
|
+
this.clientId = config?.clientId || process.env.AUTH0_CLIENT_ID || "";
|
|
309
|
+
this.clientSecret = config?.clientSecret || process.env.AUTH0_CLIENT_SECRET || "";
|
|
310
|
+
this.audience = config?.audience || process.env.AUTH0_AUDIENCE || "";
|
|
311
|
+
this.scopes = config?.scopes || this.scopes;
|
|
312
|
+
if (!this.domain || !this.clientId || !this.audience) {
|
|
313
|
+
throw new Error("Auth0 config missing: domain, clientId, and audience are required");
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
async refreshToken(refreshToken) {
|
|
317
|
+
const url = `https://${this.domain}/oauth/token`;
|
|
318
|
+
const payload = {
|
|
319
|
+
grant_type: "refresh_token",
|
|
320
|
+
client_id: this.clientId,
|
|
321
|
+
refresh_token: refreshToken,
|
|
322
|
+
audience: this.audience,
|
|
323
|
+
scope: this.scopes
|
|
324
|
+
};
|
|
325
|
+
if (this.clientSecret) {
|
|
326
|
+
payload.client_secret = this.clientSecret;
|
|
327
|
+
}
|
|
328
|
+
const { data } = await import_axios2.default.post(url, payload, {
|
|
329
|
+
headers: {
|
|
330
|
+
"Content-Type": "application/json"
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
return data;
|
|
334
|
+
}
|
|
335
|
+
async verifyToken(token) {
|
|
336
|
+
try {
|
|
337
|
+
await this.verifyJwt(token);
|
|
338
|
+
return true;
|
|
339
|
+
} catch (error) {
|
|
340
|
+
if (error instanceof Error) {
|
|
341
|
+
if (error.message.includes("jwt expired")) throw new Error("Token has expired");
|
|
342
|
+
if (error.message.includes("invalid signature")) throw new Error("Invalid token signature");
|
|
343
|
+
if (error.message.includes("jwt malformed")) throw new Error("Malformed token");
|
|
344
|
+
if (error.message.includes("invalid issuer")) throw new Error("Invalid token issuer");
|
|
345
|
+
throw error;
|
|
346
|
+
}
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
async getUser(idToken) {
|
|
351
|
+
const decoded = import_jsonwebtoken2.default.decode(idToken);
|
|
352
|
+
if (!decoded) throw new Error("Invalid ID token");
|
|
353
|
+
return {
|
|
354
|
+
sub: decoded.sub,
|
|
355
|
+
email: decoded.email,
|
|
356
|
+
email_verified: decoded.email_verified,
|
|
357
|
+
name: decoded.name,
|
|
358
|
+
attributes: decoded
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
async fetchJWKS() {
|
|
362
|
+
if (!this.jwksCache) {
|
|
363
|
+
const jwksUri = `https://${this.domain}/.well-known/jwks.json`;
|
|
364
|
+
const { data } = await import_axios2.default.get(jwksUri);
|
|
365
|
+
this.jwksCache = data.keys;
|
|
366
|
+
}
|
|
367
|
+
return this.jwksCache;
|
|
368
|
+
}
|
|
369
|
+
async verifyJwt(token) {
|
|
370
|
+
const decoded = import_jsonwebtoken2.default.decode(token, {
|
|
371
|
+
complete: true
|
|
372
|
+
});
|
|
373
|
+
if (!decoded) throw new Error("Invalid token");
|
|
374
|
+
const jwks = await this.fetchJWKS();
|
|
375
|
+
const key = jwks.find((k) => k.kid === decoded.header.kid);
|
|
376
|
+
if (!key) throw new Error("Signing key not found in JWKS");
|
|
377
|
+
const pem = (0, import_jwk_to_pem2.default)(key);
|
|
378
|
+
return import_jsonwebtoken2.default.verify(token, pem, {
|
|
379
|
+
algorithms: [
|
|
380
|
+
"RS256"
|
|
381
|
+
],
|
|
382
|
+
issuer: `https://${this.domain}/`
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// src/providers/clerk.ts
|
|
390
|
+
var clerk_exports = {};
|
|
391
|
+
__export(clerk_exports, {
|
|
392
|
+
AuthClerk: () => AuthClerk
|
|
393
|
+
});
|
|
394
|
+
var import_axios3, import_jsonwebtoken3, import_jwk_to_pem3, AuthClerk;
|
|
395
|
+
var init_clerk = __esm({
|
|
396
|
+
"src/providers/clerk.ts"() {
|
|
397
|
+
"use strict";
|
|
398
|
+
import_axios3 = __toESM(require("axios"));
|
|
399
|
+
import_jsonwebtoken3 = __toESM(require("jsonwebtoken"));
|
|
400
|
+
import_jwk_to_pem3 = __toESM(require("jwk-to-pem"));
|
|
401
|
+
init_index();
|
|
402
|
+
AuthClerk = class extends AuthProviderBase {
|
|
403
|
+
static {
|
|
404
|
+
__name(this, "AuthClerk");
|
|
405
|
+
}
|
|
406
|
+
clerkFrontendApi = "";
|
|
407
|
+
clerkSecretKey = "";
|
|
408
|
+
clerkJWKSUrl = "";
|
|
409
|
+
clerkIssuer = "";
|
|
410
|
+
jwksCache = null;
|
|
411
|
+
mode = "session";
|
|
412
|
+
oauthTokenUrl = "";
|
|
413
|
+
clientId;
|
|
414
|
+
clientSecret;
|
|
415
|
+
redirectUri;
|
|
416
|
+
/**
|
|
417
|
+
* Initialize Clerk Auth Provider
|
|
418
|
+
*/
|
|
419
|
+
async init(config) {
|
|
420
|
+
this.clerkFrontendApi = config?.frontendApi || process.env.CLERK_FRONTEND_API || "";
|
|
421
|
+
this.clerkSecretKey = config?.secretKey || process.env.CLERK_SECRET_KEY || "";
|
|
422
|
+
if (!this.clerkFrontendApi || !this.clerkSecretKey) {
|
|
423
|
+
throw new Error("Missing Clerk configuration: frontendApi and secretKey are required");
|
|
424
|
+
}
|
|
425
|
+
this.clerkIssuer = `https://${this.clerkFrontendApi}`;
|
|
426
|
+
this.clerkJWKSUrl = `${this.clerkIssuer}/.well-known/jwks.json`;
|
|
427
|
+
if (config?.clientId && config?.clientSecret && config?.redirectUri) {
|
|
428
|
+
this.mode = "oauth";
|
|
429
|
+
this.clientId = config.clientId;
|
|
430
|
+
this.clientSecret = config.clientSecret;
|
|
431
|
+
this.redirectUri = config.redirectUri;
|
|
432
|
+
this.oauthTokenUrl = `${this.clerkIssuer}/oauth/token`;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Refresh tokens (OAuth mode only)
|
|
437
|
+
*/
|
|
438
|
+
async refreshToken(refreshToken) {
|
|
439
|
+
if (this.mode !== "oauth") {
|
|
440
|
+
throw new Error("Clerk is in Session Mode: refresh tokens are not supported. Enable OAuth mode.");
|
|
441
|
+
}
|
|
442
|
+
const payload = {
|
|
443
|
+
grant_type: "refresh_token",
|
|
444
|
+
refresh_token: refreshToken,
|
|
445
|
+
client_id: this.clientId,
|
|
446
|
+
client_secret: this.clientSecret,
|
|
447
|
+
redirect_uri: this.redirectUri
|
|
448
|
+
};
|
|
449
|
+
const { data } = await import_axios3.default.post(this.oauthTokenUrl, payload, {
|
|
450
|
+
headers: {
|
|
451
|
+
"Content-Type": "application/json"
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
return data;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Verify JWT using JWKS
|
|
458
|
+
*/
|
|
459
|
+
async verifyToken(token) {
|
|
460
|
+
await this.verifyJwt(token);
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Extract user data from ID token
|
|
465
|
+
*/
|
|
466
|
+
async getUser(idToken) {
|
|
467
|
+
const decoded = import_jsonwebtoken3.default.decode(idToken);
|
|
468
|
+
if (!decoded) throw new Error("Invalid ID token");
|
|
469
|
+
return {
|
|
470
|
+
sub: decoded.sub,
|
|
471
|
+
email: decoded.email,
|
|
472
|
+
email_verified: decoded.email_verified,
|
|
473
|
+
first_name: decoded.given_name,
|
|
474
|
+
last_name: decoded.family_name,
|
|
475
|
+
attributes: decoded
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* JWT verification using JWKS
|
|
480
|
+
*/
|
|
481
|
+
async verifyJwt(token) {
|
|
482
|
+
const decoded = import_jsonwebtoken3.default.decode(token, {
|
|
483
|
+
complete: true
|
|
484
|
+
});
|
|
485
|
+
if (!decoded) throw new Error("Invalid token");
|
|
486
|
+
const jwks = await this.fetchJWKS();
|
|
487
|
+
const key = jwks.find((k) => k.kid === decoded.header.kid);
|
|
488
|
+
if (!key) throw new Error("Signing key not found in JWKS");
|
|
489
|
+
const pem = (0, import_jwk_to_pem3.default)(key);
|
|
490
|
+
return import_jsonwebtoken3.default.verify(token, pem, {
|
|
491
|
+
algorithms: [
|
|
492
|
+
"RS256"
|
|
493
|
+
],
|
|
494
|
+
issuer: this.clerkIssuer
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
async fetchJWKS() {
|
|
498
|
+
if (!this.jwksCache) {
|
|
499
|
+
const { data } = await import_axios3.default.get(this.clerkJWKSUrl);
|
|
500
|
+
this.jwksCache = data.keys;
|
|
501
|
+
}
|
|
502
|
+
return this.jwksCache;
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
|
|
283
508
|
// src/index.ts
|
|
284
509
|
var index_exports = {};
|
|
285
510
|
__export(index_exports, {
|
|
@@ -325,13 +550,18 @@ var init_index = __esm({
|
|
|
325
550
|
await this.providerInstance.init(finalConfig);
|
|
326
551
|
break;
|
|
327
552
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
553
|
+
case "auth0": {
|
|
554
|
+
const { AuthAuth0: AuthAuth02 } = await Promise.resolve().then(() => (init_auth0(), auth0_exports));
|
|
555
|
+
this.providerInstance = new AuthAuth02();
|
|
556
|
+
await this.providerInstance.init(finalConfig);
|
|
557
|
+
break;
|
|
558
|
+
}
|
|
559
|
+
case "clerk": {
|
|
560
|
+
const { AuthClerk: AuthClerk2 } = await Promise.resolve().then(() => (init_clerk(), clerk_exports));
|
|
561
|
+
this.providerInstance = new AuthClerk2();
|
|
562
|
+
await this.providerInstance.init(finalConfig);
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
335
565
|
default:
|
|
336
566
|
throw new Error(`Unsupported auth provider: ${this.providerType}. Supported providers: cognito`);
|
|
337
567
|
}
|
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leanmcp/auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Authentication and identity module supporting multiple providers",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"test:watch": "jest --watch"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@leanmcp/core": "^0.
|
|
27
|
+
"@leanmcp/core": "^0.2.0",
|
|
28
28
|
"reflect-metadata": "^0.2.1"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
@@ -32,7 +32,9 @@
|
|
|
32
32
|
"@types/jsonwebtoken": "^9.0.10",
|
|
33
33
|
"@types/jwk-to-pem": "^2.0.3",
|
|
34
34
|
"@types/node": "^20.0.0",
|
|
35
|
-
"dotenv": "^17.2.3"
|
|
35
|
+
"dotenv": "^17.2.3",
|
|
36
|
+
"jest": "^29.7.0",
|
|
37
|
+
"ts-jest": "^29.1.0"
|
|
36
38
|
},
|
|
37
39
|
"peerDependencies": {
|
|
38
40
|
"@aws-sdk/client-cognito-identity-provider": "^3.0.0",
|
package/dist/chunk-YC7GFXAO.mjs
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
-
|
|
4
|
-
// src/index.ts
|
|
5
|
-
import "reflect-metadata";
|
|
6
|
-
|
|
7
|
-
// src/decorators.ts
|
|
8
|
-
import "reflect-metadata";
|
|
9
|
-
var AuthenticationError = class extends Error {
|
|
10
|
-
static {
|
|
11
|
-
__name(this, "AuthenticationError");
|
|
12
|
-
}
|
|
13
|
-
code;
|
|
14
|
-
constructor(message, code) {
|
|
15
|
-
super(message), this.code = code;
|
|
16
|
-
this.name = "AuthenticationError";
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
function Authenticated(authProvider) {
|
|
20
|
-
return function(target, propertyKey, descriptor) {
|
|
21
|
-
if (!propertyKey && !descriptor) {
|
|
22
|
-
Reflect.defineMetadata("auth:provider", authProvider, target);
|
|
23
|
-
Reflect.defineMetadata("auth:required", true, target);
|
|
24
|
-
const prototype = target.prototype;
|
|
25
|
-
const methodNames = Object.getOwnPropertyNames(prototype).filter((name) => name !== "constructor" && typeof prototype[name] === "function");
|
|
26
|
-
for (const methodName of methodNames) {
|
|
27
|
-
const originalDescriptor = Object.getOwnPropertyDescriptor(prototype, methodName);
|
|
28
|
-
if (originalDescriptor && typeof originalDescriptor.value === "function") {
|
|
29
|
-
const originalMethod = originalDescriptor.value;
|
|
30
|
-
Reflect.defineMetadata("auth:provider", authProvider, originalMethod);
|
|
31
|
-
Reflect.defineMetadata("auth:required", true, originalMethod);
|
|
32
|
-
prototype[methodName] = createAuthenticatedMethod(originalMethod, authProvider);
|
|
33
|
-
copyMetadata(originalMethod, prototype[methodName]);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return target;
|
|
37
|
-
}
|
|
38
|
-
if (descriptor && typeof descriptor.value === "function") {
|
|
39
|
-
const originalMethod = descriptor.value;
|
|
40
|
-
Reflect.defineMetadata("auth:provider", authProvider, originalMethod);
|
|
41
|
-
Reflect.defineMetadata("auth:required", true, originalMethod);
|
|
42
|
-
descriptor.value = createAuthenticatedMethod(originalMethod, authProvider);
|
|
43
|
-
copyMetadata(originalMethod, descriptor.value);
|
|
44
|
-
return descriptor;
|
|
45
|
-
}
|
|
46
|
-
throw new Error("@Authenticated can only be applied to classes or methods");
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
__name(Authenticated, "Authenticated");
|
|
50
|
-
function createAuthenticatedMethod(originalMethod, authProvider) {
|
|
51
|
-
return async function(...args) {
|
|
52
|
-
const firstArg = args[0];
|
|
53
|
-
let token;
|
|
54
|
-
let cleanedArgs = args;
|
|
55
|
-
if (firstArg && typeof firstArg === "object") {
|
|
56
|
-
token = firstArg.token;
|
|
57
|
-
const { token: _, ...restArgs } = firstArg;
|
|
58
|
-
cleanedArgs = [
|
|
59
|
-
restArgs,
|
|
60
|
-
...args.slice(1)
|
|
61
|
-
];
|
|
62
|
-
}
|
|
63
|
-
if (!token) {
|
|
64
|
-
throw new AuthenticationError("Authentication required. Please provide a valid token in the request.", "MISSING_TOKEN");
|
|
65
|
-
}
|
|
66
|
-
try {
|
|
67
|
-
const isValid = await authProvider.verifyToken(token);
|
|
68
|
-
if (!isValid) {
|
|
69
|
-
throw new AuthenticationError("Invalid or expired token. Please authenticate again.", "INVALID_TOKEN");
|
|
70
|
-
}
|
|
71
|
-
} catch (error) {
|
|
72
|
-
if (error instanceof AuthenticationError) {
|
|
73
|
-
throw error;
|
|
74
|
-
}
|
|
75
|
-
throw new AuthenticationError(`Token verification failed: ${error instanceof Error ? error.message : String(error)}`, "VERIFICATION_FAILED");
|
|
76
|
-
}
|
|
77
|
-
return originalMethod.apply(this, cleanedArgs);
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
__name(createAuthenticatedMethod, "createAuthenticatedMethod");
|
|
81
|
-
function copyMetadata(source, target) {
|
|
82
|
-
const metadataKeys = Reflect.getMetadataKeys(source);
|
|
83
|
-
for (const key of metadataKeys) {
|
|
84
|
-
const value = Reflect.getMetadata(key, source);
|
|
85
|
-
Reflect.defineMetadata(key, value, target);
|
|
86
|
-
}
|
|
87
|
-
const designKeys = [
|
|
88
|
-
"design:type",
|
|
89
|
-
"design:paramtypes",
|
|
90
|
-
"design:returntype"
|
|
91
|
-
];
|
|
92
|
-
for (const key of designKeys) {
|
|
93
|
-
const value = Reflect.getMetadata(key, source);
|
|
94
|
-
if (value !== void 0) {
|
|
95
|
-
Reflect.defineMetadata(key, value, target);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
__name(copyMetadata, "copyMetadata");
|
|
100
|
-
function isAuthenticationRequired(target) {
|
|
101
|
-
return Reflect.getMetadata("auth:required", target) === true;
|
|
102
|
-
}
|
|
103
|
-
__name(isAuthenticationRequired, "isAuthenticationRequired");
|
|
104
|
-
function getAuthProvider(target) {
|
|
105
|
-
return Reflect.getMetadata("auth:provider", target);
|
|
106
|
-
}
|
|
107
|
-
__name(getAuthProvider, "getAuthProvider");
|
|
108
|
-
|
|
109
|
-
// src/index.ts
|
|
110
|
-
var AuthProviderBase = class {
|
|
111
|
-
static {
|
|
112
|
-
__name(this, "AuthProviderBase");
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
var AuthProvider = class extends AuthProviderBase {
|
|
116
|
-
static {
|
|
117
|
-
__name(this, "AuthProvider");
|
|
118
|
-
}
|
|
119
|
-
providerInstance = null;
|
|
120
|
-
providerType;
|
|
121
|
-
config;
|
|
122
|
-
constructor(provider, config) {
|
|
123
|
-
super();
|
|
124
|
-
this.providerType = provider.toLowerCase();
|
|
125
|
-
this.config = config;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Initialize the selected auth provider
|
|
129
|
-
*/
|
|
130
|
-
async init(config) {
|
|
131
|
-
const finalConfig = config || this.config;
|
|
132
|
-
switch (this.providerType) {
|
|
133
|
-
case "cognito": {
|
|
134
|
-
const { AuthCognito } = await import("./cognito-GBSAAMZI.mjs");
|
|
135
|
-
this.providerInstance = new AuthCognito();
|
|
136
|
-
await this.providerInstance.init(finalConfig);
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
// Add more providers here in the future
|
|
140
|
-
// case 'clerk': {
|
|
141
|
-
// const { AuthClerk } = await import('./providers/clerk');
|
|
142
|
-
// this.providerInstance = new AuthClerk();
|
|
143
|
-
// await this.providerInstance.init(finalConfig);
|
|
144
|
-
// break;
|
|
145
|
-
// }
|
|
146
|
-
default:
|
|
147
|
-
throw new Error(`Unsupported auth provider: ${this.providerType}. Supported providers: cognito`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Refresh an authentication token
|
|
152
|
-
*/
|
|
153
|
-
async refreshToken(refreshToken, username) {
|
|
154
|
-
if (!this.providerInstance) {
|
|
155
|
-
throw new Error("AuthProvider not initialized. Call init() first.");
|
|
156
|
-
}
|
|
157
|
-
return this.providerInstance.refreshToken(refreshToken, username);
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Verify if a token is valid
|
|
161
|
-
*/
|
|
162
|
-
async verifyToken(token) {
|
|
163
|
-
if (!this.providerInstance) {
|
|
164
|
-
throw new Error("AuthProvider not initialized. Call init() first.");
|
|
165
|
-
}
|
|
166
|
-
return this.providerInstance.verifyToken(token);
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Get user information from a token
|
|
170
|
-
*/
|
|
171
|
-
async getUser(token) {
|
|
172
|
-
if (!this.providerInstance) {
|
|
173
|
-
throw new Error("AuthProvider not initialized. Call init() first.");
|
|
174
|
-
}
|
|
175
|
-
return this.providerInstance.getUser(token);
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Get the provider type
|
|
179
|
-
*/
|
|
180
|
-
getProviderType() {
|
|
181
|
-
return this.providerType;
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
export {
|
|
186
|
-
__name,
|
|
187
|
-
AuthenticationError,
|
|
188
|
-
Authenticated,
|
|
189
|
-
isAuthenticationRequired,
|
|
190
|
-
getAuthProvider,
|
|
191
|
-
AuthProviderBase,
|
|
192
|
-
AuthProvider
|
|
193
|
-
};
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AuthProviderBase,
|
|
3
|
-
__name
|
|
4
|
-
} from "./chunk-NALGJYQB.mjs";
|
|
5
|
-
|
|
6
|
-
// src/providers/cognito.ts
|
|
7
|
-
import { CognitoIdentityProviderClient, InitiateAuthCommand } from "@aws-sdk/client-cognito-identity-provider";
|
|
8
|
-
import { createHmac } from "crypto";
|
|
9
|
-
import axios from "axios";
|
|
10
|
-
import jwt from "jsonwebtoken";
|
|
11
|
-
import jwkToPem from "jwk-to-pem";
|
|
12
|
-
var AuthCognito = class extends AuthProviderBase {
|
|
13
|
-
static {
|
|
14
|
-
__name(this, "AuthCognito");
|
|
15
|
-
}
|
|
16
|
-
cognito = null;
|
|
17
|
-
region = "";
|
|
18
|
-
userPoolId = "";
|
|
19
|
-
clientId = "";
|
|
20
|
-
clientSecret = "";
|
|
21
|
-
jwksCache = null;
|
|
22
|
-
/**
|
|
23
|
-
* Initialize the Cognito client with configuration
|
|
24
|
-
*/
|
|
25
|
-
async init(config) {
|
|
26
|
-
this.region = config?.region || process.env.AWS_REGION || "";
|
|
27
|
-
this.userPoolId = config?.userPoolId || process.env.COGNITO_USER_POOL_ID || "";
|
|
28
|
-
this.clientId = config?.clientId || process.env.COGNITO_CLIENT_ID || "";
|
|
29
|
-
this.clientSecret = config?.clientSecret || process.env.COGNITO_CLIENT_SECRET || "";
|
|
30
|
-
if (!this.region || !this.userPoolId || !this.clientId) {
|
|
31
|
-
throw new Error("Missing required Cognito configuration: region, userPoolId, and clientId are required");
|
|
32
|
-
}
|
|
33
|
-
this.cognito = new CognitoIdentityProviderClient({
|
|
34
|
-
region: this.region
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Refresh access tokens using a refresh token
|
|
39
|
-
*/
|
|
40
|
-
async refreshToken(refreshToken, username) {
|
|
41
|
-
if (!this.cognito) {
|
|
42
|
-
throw new Error("CognitoAuth not initialized. Call init() first.");
|
|
43
|
-
}
|
|
44
|
-
const authParameters = {
|
|
45
|
-
REFRESH_TOKEN: refreshToken
|
|
46
|
-
};
|
|
47
|
-
if (this.clientSecret) {
|
|
48
|
-
const usernameForHash = username;
|
|
49
|
-
const secretHash = this.calculateSecretHash(usernameForHash);
|
|
50
|
-
authParameters.SECRET_HASH = secretHash;
|
|
51
|
-
}
|
|
52
|
-
const command = new InitiateAuthCommand({
|
|
53
|
-
AuthFlow: "REFRESH_TOKEN_AUTH",
|
|
54
|
-
ClientId: this.clientId,
|
|
55
|
-
AuthParameters: authParameters
|
|
56
|
-
});
|
|
57
|
-
return await this.cognito.send(command);
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Verify a Cognito JWT token using JWKS
|
|
61
|
-
*/
|
|
62
|
-
async verifyToken(token) {
|
|
63
|
-
try {
|
|
64
|
-
await this.verifyJwt(token);
|
|
65
|
-
return true;
|
|
66
|
-
} catch (error) {
|
|
67
|
-
if (error instanceof Error) {
|
|
68
|
-
if (error.message.includes("jwt expired")) {
|
|
69
|
-
throw new Error("Token has expired");
|
|
70
|
-
} else if (error.message.includes("invalid signature")) {
|
|
71
|
-
throw new Error("Invalid token signature");
|
|
72
|
-
} else if (error.message.includes("jwt malformed")) {
|
|
73
|
-
throw new Error("Malformed token");
|
|
74
|
-
} else if (error.message.includes("invalid issuer")) {
|
|
75
|
-
throw new Error("Invalid token issuer");
|
|
76
|
-
}
|
|
77
|
-
throw error;
|
|
78
|
-
}
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Get user information from an ID token
|
|
84
|
-
*/
|
|
85
|
-
async getUser(idToken) {
|
|
86
|
-
const decoded = jwt.decode(idToken);
|
|
87
|
-
if (!decoded) {
|
|
88
|
-
throw new Error("Invalid ID token");
|
|
89
|
-
}
|
|
90
|
-
return {
|
|
91
|
-
username: decoded["cognito:username"],
|
|
92
|
-
email: decoded.email,
|
|
93
|
-
email_verified: decoded.email_verified,
|
|
94
|
-
sub: decoded.sub,
|
|
95
|
-
attributes: decoded
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Fetch JWKS from Cognito (cached)
|
|
100
|
-
*/
|
|
101
|
-
async fetchJWKS() {
|
|
102
|
-
if (!this.jwksCache) {
|
|
103
|
-
const jwksUri = `https://cognito-idp.${this.region}.amazonaws.com/${this.userPoolId}/.well-known/jwks.json`;
|
|
104
|
-
const { data } = await axios.get(jwksUri);
|
|
105
|
-
this.jwksCache = data.keys;
|
|
106
|
-
}
|
|
107
|
-
return this.jwksCache;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Verify JWT token using JWKS
|
|
111
|
-
*/
|
|
112
|
-
async verifyJwt(token) {
|
|
113
|
-
const decoded = jwt.decode(token, {
|
|
114
|
-
complete: true
|
|
115
|
-
});
|
|
116
|
-
if (!decoded) {
|
|
117
|
-
throw new Error("Invalid token");
|
|
118
|
-
}
|
|
119
|
-
const jwks = await this.fetchJWKS();
|
|
120
|
-
const key = jwks.find((k) => k.kid === decoded.header.kid);
|
|
121
|
-
if (!key) {
|
|
122
|
-
throw new Error("Signing key not found in JWKS");
|
|
123
|
-
}
|
|
124
|
-
const pem = jwkToPem(key);
|
|
125
|
-
return jwt.verify(token, pem, {
|
|
126
|
-
algorithms: [
|
|
127
|
-
"RS256"
|
|
128
|
-
],
|
|
129
|
-
issuer: `https://cognito-idp.${this.region}.amazonaws.com/${this.userPoolId}`
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Calculate SECRET_HASH for Cognito authentication
|
|
134
|
-
* SECRET_HASH = Base64(HMAC_SHA256(username + clientId, clientSecret))
|
|
135
|
-
*/
|
|
136
|
-
calculateSecretHash(username) {
|
|
137
|
-
const message = username + this.clientId;
|
|
138
|
-
const hmac = createHmac("sha256", this.clientSecret);
|
|
139
|
-
hmac.update(message);
|
|
140
|
-
return hmac.digest("base64");
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
export {
|
|
144
|
-
AuthCognito
|
|
145
|
-
};
|