@mastra/auth-clerk 0.10.3 → 0.10.4-alpha.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/CHANGELOG.md +9 -0
- package/package.json +17 -4
- package/.turbo/turbo-build.log +0 -4
- package/eslint.config.js +0 -6
- package/src/index.test.ts +0 -126
- package/src/index.ts +0 -58
- package/tsconfig.build.json +0 -9
- package/tsconfig.json +0 -5
- package/tsup.config.ts +0 -17
- package/vitest.config.ts +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @mastra/auth-clerk
|
|
2
2
|
|
|
3
|
+
## 0.10.4-alpha.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#7343](https://github.com/mastra-ai/mastra/pull/7343) [`de3cbc6`](https://github.com/mastra-ai/mastra/commit/de3cbc61079211431bd30487982ea3653517278e) Thanks [@LekoArts](https://github.com/LekoArts)! - Update the `package.json` file to include additional fields like `repository`, `homepage` or `files`.
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`de3cbc6`](https://github.com/mastra-ai/mastra/commit/de3cbc61079211431bd30487982ea3653517278e)]:
|
|
10
|
+
- @mastra/auth@0.1.3-alpha.0
|
|
11
|
+
|
|
3
12
|
## 0.10.3
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/auth-clerk",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.4-alpha.0",
|
|
4
4
|
"description": "Mastra Clerk Auth integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"license": "Apache-2.0",
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@clerk/backend": "^1.34.0",
|
|
24
|
-
"@mastra/auth": "0.1.
|
|
24
|
+
"@mastra/auth": "0.1.3-alpha.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/node": "^20.19.0",
|
|
@@ -30,8 +30,21 @@
|
|
|
30
30
|
"typescript": "^5.8.3",
|
|
31
31
|
"vitest": "^3.2.4",
|
|
32
32
|
"@internal/lint": "0.0.34",
|
|
33
|
-
"@
|
|
34
|
-
"@
|
|
33
|
+
"@mastra/core": "0.15.3-alpha.5",
|
|
34
|
+
"@internal/types-builder": "0.0.9"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"CHANGELOG.md"
|
|
39
|
+
],
|
|
40
|
+
"homepage": "https://mastra.ai",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/mastra-ai/mastra.git",
|
|
44
|
+
"directory": "auth/clerk"
|
|
45
|
+
},
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/mastra-ai/mastra/issues"
|
|
35
48
|
},
|
|
36
49
|
"scripts": {
|
|
37
50
|
"build": "tsup --silent --config tsup.config.ts",
|
package/.turbo/turbo-build.log
DELETED
package/eslint.config.js
DELETED
package/src/index.test.ts
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { createClerkClient } from '@clerk/backend';
|
|
2
|
-
import { verifyJwks } from '@mastra/auth';
|
|
3
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
4
|
-
import { MastraAuthClerk } from './index';
|
|
5
|
-
|
|
6
|
-
// Mock the external dependencies
|
|
7
|
-
vi.mock('@clerk/backend', () => ({
|
|
8
|
-
createClerkClient: vi.fn(),
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
vi.mock('@mastra/auth', () => ({
|
|
12
|
-
verifyJwks: vi.fn(),
|
|
13
|
-
}));
|
|
14
|
-
|
|
15
|
-
describe('MastraAuthClerk', () => {
|
|
16
|
-
const mockOptions = {
|
|
17
|
-
jwksUri: 'https://clerk.jwks.uri',
|
|
18
|
-
secretKey: 'test-secret-key',
|
|
19
|
-
publishableKey: 'test-publishable-key',
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const mockClerkClient = {
|
|
23
|
-
users: {
|
|
24
|
-
getOrganizationMembershipList: vi.fn(),
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
beforeEach(() => {
|
|
29
|
-
vi.clearAllMocks();
|
|
30
|
-
(createClerkClient as any).mockReturnValue(mockClerkClient);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe('initialization', () => {
|
|
34
|
-
it('should initialize with provided options', () => {
|
|
35
|
-
const auth = new MastraAuthClerk(mockOptions);
|
|
36
|
-
expect(auth).toBeInstanceOf(MastraAuthClerk);
|
|
37
|
-
expect(createClerkClient).toHaveBeenCalledWith({
|
|
38
|
-
secretKey: mockOptions.secretKey,
|
|
39
|
-
publishableKey: mockOptions.publishableKey,
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('should throw error when required options are missing', () => {
|
|
44
|
-
expect(() => new MastraAuthClerk({})).toThrow('Clerk JWKS URI, secret key and publishable key are required');
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
describe('authenticateToken', () => {
|
|
49
|
-
it('should verify token and return user', async () => {
|
|
50
|
-
const mockUser = { sub: 'user123', email: 'test@example.com' };
|
|
51
|
-
(verifyJwks as any).mockResolvedValue(mockUser);
|
|
52
|
-
|
|
53
|
-
const auth = new MastraAuthClerk(mockOptions);
|
|
54
|
-
const result = await auth.authenticateToken('test-token');
|
|
55
|
-
|
|
56
|
-
expect(verifyJwks).toHaveBeenCalledWith('test-token', mockOptions.jwksUri);
|
|
57
|
-
expect(result).toEqual(mockUser);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('should return null when token verification fails', async () => {
|
|
61
|
-
(verifyJwks as any).mockResolvedValue(null);
|
|
62
|
-
|
|
63
|
-
const auth = new MastraAuthClerk(mockOptions);
|
|
64
|
-
const result = await auth.authenticateToken('invalid-token');
|
|
65
|
-
|
|
66
|
-
expect(result).toBeNull();
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe('authorizeUser', () => {
|
|
71
|
-
it('should return false when user has no sub', async () => {
|
|
72
|
-
const auth = new MastraAuthClerk(mockOptions);
|
|
73
|
-
const result = await auth.authorizeUser({ email: 'test@example.com' });
|
|
74
|
-
|
|
75
|
-
expect(result).toBe(false);
|
|
76
|
-
expect(mockClerkClient.users.getOrganizationMembershipList).not.toHaveBeenCalled();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('should return true when user has organization memberships', async () => {
|
|
80
|
-
mockClerkClient.users.getOrganizationMembershipList.mockResolvedValue({
|
|
81
|
-
data: [{ id: 'org1' }],
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
const auth = new MastraAuthClerk(mockOptions);
|
|
85
|
-
const result = await auth.authorizeUser({ sub: 'user123' });
|
|
86
|
-
|
|
87
|
-
expect(mockClerkClient.users.getOrganizationMembershipList).toHaveBeenCalledWith({
|
|
88
|
-
userId: 'user123',
|
|
89
|
-
});
|
|
90
|
-
expect(result).toBe(true);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('should return false when user has no organization memberships', async () => {
|
|
94
|
-
mockClerkClient.users.getOrganizationMembershipList.mockResolvedValue({
|
|
95
|
-
data: [],
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
const auth = new MastraAuthClerk(mockOptions);
|
|
99
|
-
const result = await auth.authorizeUser({ sub: 'user123' });
|
|
100
|
-
|
|
101
|
-
expect(result).toBe(false);
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('can be overridden with custom authorization logic', async () => {
|
|
106
|
-
const clerk = new MastraAuthClerk({
|
|
107
|
-
...mockOptions,
|
|
108
|
-
async authorizeUser(user: any): Promise<boolean> {
|
|
109
|
-
// Custom authorization logic that checks for specific permissions
|
|
110
|
-
return user?.permissions?.includes('admin') ?? false;
|
|
111
|
-
},
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
// Test with admin user
|
|
115
|
-
const adminUser = { sub: 'user123', permissions: ['admin'] };
|
|
116
|
-
expect(await clerk.authorizeUser(adminUser)).toBe(true);
|
|
117
|
-
|
|
118
|
-
// Test with non-admin user
|
|
119
|
-
const regularUser = { sub: 'user456', permissions: ['read'] };
|
|
120
|
-
expect(await clerk.authorizeUser(regularUser)).toBe(false);
|
|
121
|
-
|
|
122
|
-
// Test with user without permissions
|
|
123
|
-
const noPermissionsUser = { sub: 'user789' };
|
|
124
|
-
expect(await clerk.authorizeUser(noPermissionsUser)).toBe(false);
|
|
125
|
-
});
|
|
126
|
-
});
|
package/src/index.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { createClerkClient } from '@clerk/backend';
|
|
2
|
-
import type { ClerkClient } from '@clerk/backend';
|
|
3
|
-
import { verifyJwks } from '@mastra/auth';
|
|
4
|
-
import type { JwtPayload } from '@mastra/auth';
|
|
5
|
-
import type { MastraAuthProviderOptions } from '@mastra/core/server';
|
|
6
|
-
import { MastraAuthProvider } from '@mastra/core/server';
|
|
7
|
-
|
|
8
|
-
type ClerkUser = JwtPayload;
|
|
9
|
-
|
|
10
|
-
interface MastraAuthClerkOptions extends MastraAuthProviderOptions<ClerkUser> {
|
|
11
|
-
jwksUri?: string;
|
|
12
|
-
secretKey?: string;
|
|
13
|
-
publishableKey?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export class MastraAuthClerk extends MastraAuthProvider<ClerkUser> {
|
|
17
|
-
protected clerk: ClerkClient;
|
|
18
|
-
protected jwksUri: string;
|
|
19
|
-
|
|
20
|
-
constructor(options?: MastraAuthClerkOptions) {
|
|
21
|
-
super({ name: options?.name ?? 'clerk' });
|
|
22
|
-
|
|
23
|
-
const jwksUri = options?.jwksUri ?? process.env.CLERK_JWKS_URI;
|
|
24
|
-
const secretKey = options?.secretKey ?? process.env.CLERK_SECRET_KEY;
|
|
25
|
-
const publishableKey = options?.publishableKey ?? process.env.CLERK_PUBLISHABLE_KEY;
|
|
26
|
-
|
|
27
|
-
if (!jwksUri || !secretKey || !publishableKey) {
|
|
28
|
-
throw new Error(
|
|
29
|
-
'Clerk JWKS URI, secret key and publishable key are required, please provide them in the options or set the environment variables CLERK_JWKS_URI, CLERK_SECRET_KEY and CLERK_PUBLISHABLE_KEY',
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
this.jwksUri = jwksUri;
|
|
34
|
-
this.clerk = createClerkClient({
|
|
35
|
-
secretKey,
|
|
36
|
-
publishableKey,
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
this.registerOptions(options);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async authenticateToken(token: string): Promise<ClerkUser | null> {
|
|
43
|
-
const user = await verifyJwks(token, this.jwksUri);
|
|
44
|
-
return user;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async authorizeUser(user: ClerkUser) {
|
|
48
|
-
if (!user.sub) {
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const orgs = await this.clerk.users.getOrganizationMembershipList({
|
|
53
|
-
userId: user.sub,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
return orgs.data.length > 0;
|
|
57
|
-
}
|
|
58
|
-
}
|
package/tsconfig.build.json
DELETED
package/tsconfig.json
DELETED
package/tsup.config.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { generateTypes } from '@internal/types-builder';
|
|
2
|
-
import { defineConfig } from 'tsup';
|
|
3
|
-
|
|
4
|
-
export default defineConfig({
|
|
5
|
-
entry: ['src/index.ts'],
|
|
6
|
-
format: ['esm', 'cjs'],
|
|
7
|
-
clean: true,
|
|
8
|
-
dts: false,
|
|
9
|
-
splitting: true,
|
|
10
|
-
treeshake: {
|
|
11
|
-
preset: 'smallest',
|
|
12
|
-
},
|
|
13
|
-
sourcemap: true,
|
|
14
|
-
onSuccess: async () => {
|
|
15
|
-
await generateTypes(process.cwd());
|
|
16
|
-
},
|
|
17
|
-
});
|