canopycms-auth-clerk 0.0.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 ADDED
@@ -0,0 +1,81 @@
1
+ # canopycms-auth-clerk
2
+
3
+ Clerk authentication provider for CanopyCMS.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install canopycms canopycms-auth-clerk @clerk/nextjs
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Basic Setup
14
+
15
+ ```typescript
16
+ import { createClerkAuthPlugin } from 'canopycms-auth-clerk'
17
+ import { createCanopyHandler } from 'canopycms/next'
18
+
19
+ const authPlugin = createClerkAuthPlugin({
20
+ secretKey: process.env.CLERK_SECRET_KEY,
21
+ useOrganizationsAsGroups: true, // Map Clerk organizations to CMS groups
22
+ })
23
+
24
+ const handler = createCanopyHandler({
25
+ config,
26
+ authPlugin,
27
+ // ...
28
+ })
29
+ ```
30
+
31
+ ### Configuration Options
32
+
33
+ ```typescript
34
+ interface ClerkAuthConfig {
35
+ /**
36
+ * Clerk secret key (defaults to process.env.CLERK_SECRET_KEY)
37
+ */
38
+ secretKey?: string
39
+
40
+ /**
41
+ * Use organizations as groups
42
+ * @default true
43
+ */
44
+ useOrganizationsAsGroups?: boolean
45
+ }
46
+ ```
47
+
48
+ ### Groups-Only Permission Model
49
+
50
+ CanopyCMS uses a groups-only permission model. The Clerk plugin extracts the user ID and group memberships from Clerk organizations:
51
+
52
+ - **User ID**: Clerk's `userId` is used as the CMS user identifier
53
+ - **Groups**: When `useOrganizationsAsGroups` is enabled, Clerk organization memberships are returned as group names
54
+
55
+ ### Reserved Groups in CanopyCMS
56
+
57
+ CanopyCMS uses reserved groups for permissions:
58
+
59
+ - **Admins**: Full access to all operations (manage groups, merge PRs, delete branches, etc.)
60
+ - **Reviewers**: Can review branches, request changes, approve PRs
61
+
62
+ To grant admin access to a user, add them to the "Admins" group in CanopyCMS's group management UI, or use the bootstrap admin mechanism:
63
+
64
+ ```bash
65
+ # Set bootstrap admins via environment variable
66
+ CANOPY_BOOTSTRAP_ADMIN_IDS=user_abc123,user_def456
67
+ ```
68
+
69
+ Bootstrap admins are automatically treated as members of the Admins group, even before the group system is configured.
70
+
71
+ ### Using Organizations as Groups
72
+
73
+ When `useOrganizationsAsGroups` is enabled (default), Clerk organizations are automatically mapped to CanopyCMS groups for permission management. Create an organization named "Admins" or "Reviewers" in Clerk and add users to grant them those permissions.
74
+
75
+ ## Example App Integration
76
+
77
+ See the [example app](../../apps/example1) for a complete integration example.
78
+
79
+ ## License
80
+
81
+ MIT
@@ -0,0 +1,26 @@
1
+ export interface RefreshClerkCacheOptions {
2
+ /** Clerk Secret Key (CLERK_SECRET_KEY) */
3
+ secretKey: string;
4
+ /** Directory to write cache files to (e.g., /mnt/efs/workspace/.cache) */
5
+ cachePath: string;
6
+ /** Whether to treat Clerk organizations as groups (default: true) */
7
+ useOrganizationsAsGroups?: boolean;
8
+ }
9
+ export interface RefreshClerkCacheResult {
10
+ userCount: number;
11
+ groupCount: number;
12
+ membershipCount: number;
13
+ }
14
+ /**
15
+ * Fetches all user/org metadata from Clerk API and writes to JSON cache files.
16
+ *
17
+ * Used by the EC2 worker to populate the cache that FileBasedAuthCache reads.
18
+ * Writes atomically (write to temp file, then rename) to avoid partial reads.
19
+ *
20
+ * Output files:
21
+ * - {cachePath}/users.json — { users: UserSearchResult[] }
22
+ * - {cachePath}/orgs.json — { groups: GroupMetadata[] }
23
+ * - {cachePath}/memberships.json — { memberships: { [userId]: groupId[] } }
24
+ */
25
+ export declare function refreshClerkCache(options: RefreshClerkCacheOptions): Promise<RefreshClerkCacheResult>;
26
+ //# sourceMappingURL=cache-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-writer.d.ts","sourceRoot":"","sources":["../src/cache-writer.ts"],"names":[],"mappings":"AAsCA,MAAM,WAAW,wBAAwB;IACvC,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAA;IACjB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAA;IACjB,qEAAqE;IACrE,wBAAwB,CAAC,EAAE,OAAO,CAAA;CACnC;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,eAAe,EAAE,MAAM,CAAA;CACxB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,uBAAuB,CAAC,CAqFlC"}
@@ -0,0 +1,94 @@
1
+ import { createClerkClient } from '@clerk/backend';
2
+ import { writeAuthCacheSnapshot } from 'canopycms/auth/cache';
3
+ function unwrapClerkResponse(response) {
4
+ return Array.isArray(response) ? response : response.data;
5
+ }
6
+ /**
7
+ * Fetches all user/org metadata from Clerk API and writes to JSON cache files.
8
+ *
9
+ * Used by the EC2 worker to populate the cache that FileBasedAuthCache reads.
10
+ * Writes atomically (write to temp file, then rename) to avoid partial reads.
11
+ *
12
+ * Output files:
13
+ * - {cachePath}/users.json — { users: UserSearchResult[] }
14
+ * - {cachePath}/orgs.json — { groups: GroupMetadata[] }
15
+ * - {cachePath}/memberships.json — { memberships: { [userId]: groupId[] } }
16
+ */
17
+ export async function refreshClerkCache(options) {
18
+ const { secretKey, cachePath, useOrganizationsAsGroups = true } = options;
19
+ const clerkClient = createClerkClient({ secretKey });
20
+ // Fetch all users (paginate to handle large organizations)
21
+ const clerkUsers = [];
22
+ const pageSize = 500;
23
+ let offset = 0;
24
+ // eslint-disable-next-line no-constant-condition
25
+ while (true) {
26
+ const usersResponse = (await clerkClient.users.getUserList({
27
+ limit: pageSize,
28
+ offset,
29
+ }));
30
+ const page = unwrapClerkResponse(usersResponse);
31
+ clerkUsers.push(...page);
32
+ if (page.length < pageSize)
33
+ break;
34
+ offset += pageSize;
35
+ }
36
+ const users = clerkUsers.map((u) => ({
37
+ id: u.id,
38
+ name: u.fullName ?? u.full_name ?? u.username ?? u.id,
39
+ email: u.primaryEmailAddress?.emailAddress ?? u.email_addresses?.[0]?.email_address ?? '',
40
+ avatarUrl: u.imageUrl ?? u.image_url ?? undefined,
41
+ }));
42
+ let groups = [];
43
+ const memberships = {};
44
+ if (useOrganizationsAsGroups) {
45
+ // Fetch all organizations (paginate)
46
+ const clerkOrgs = [];
47
+ let orgOffset = 0;
48
+ const orgPageSize = 100;
49
+ // eslint-disable-next-line no-constant-condition
50
+ while (true) {
51
+ const orgsResponse = (await clerkClient.organizations.getOrganizationList({
52
+ limit: orgPageSize,
53
+ offset: orgOffset,
54
+ }));
55
+ const page = unwrapClerkResponse(orgsResponse);
56
+ clerkOrgs.push(...page);
57
+ if (page.length < orgPageSize)
58
+ break;
59
+ orgOffset += orgPageSize;
60
+ }
61
+ groups = clerkOrgs.map((o) => ({
62
+ id: o.id,
63
+ name: o.name,
64
+ memberCount: o.membersCount ?? o.members_count,
65
+ }));
66
+ // Fetch memberships per user
67
+ for (const user of clerkUsers) {
68
+ try {
69
+ const membershipResponse = (await clerkClient.users.getOrganizationMembershipList({
70
+ userId: user.id,
71
+ }));
72
+ const userMemberships = unwrapClerkResponse(membershipResponse);
73
+ if (userMemberships.length > 0) {
74
+ memberships[user.id] = userMemberships.map((m) => m.organization.id);
75
+ }
76
+ }
77
+ catch (err) {
78
+ console.warn(`Failed to fetch memberships for user ${user.id}:`, err instanceof Error ? err.message : err);
79
+ }
80
+ }
81
+ }
82
+ // Write cache files atomically via snapshot directory + symlink swap
83
+ await writeAuthCacheSnapshot(cachePath, {
84
+ 'users.json': { users },
85
+ 'orgs.json': { groups },
86
+ 'memberships.json': { memberships },
87
+ });
88
+ return {
89
+ userCount: users.length,
90
+ groupCount: groups.length,
91
+ membershipCount: Object.keys(memberships).length,
92
+ };
93
+ }
94
+ //# sourceMappingURL=cache-writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-writer.js","sourceRoot":"","sources":["../src/cache-writer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { AuthPlugin, AuthPluginFactory } from 'canopycms/auth';
2
+ import type { UserSearchResult, GroupMetadata, AuthenticationResult } from 'canopycms/auth';
3
+ import { type HeadersLike } from 'canopycms/auth';
4
+ export interface ClerkAuthConfig {
5
+ /**
6
+ * Use organizations as groups
7
+ * @default true
8
+ */
9
+ useOrganizationsAsGroups?: boolean;
10
+ /**
11
+ * Clerk Secret Key. If not provided, will use CLERK_SECRET_KEY env var.
12
+ */
13
+ secretKey?: string;
14
+ /**
15
+ * PEM public key for networkless JWT verification.
16
+ * If not provided, will use CLERK_JWT_KEY env var.
17
+ */
18
+ jwtKey?: string;
19
+ /**
20
+ * List of authorized parties (domains) for CSRF protection.
21
+ * If not provided, will parse from CLERK_AUTHORIZED_PARTIES env var.
22
+ */
23
+ authorizedParties?: string[];
24
+ }
25
+ /**
26
+ * Extract token from headers.
27
+ * Looks for Bearer token in Authorization header or __session cookie.
28
+ */
29
+ export declare const extractToken: (headers: HeadersLike) => string | null;
30
+ /**
31
+ * Clerk authentication plugin implementation for CanopyCMS.
32
+ * Uses @clerk/backend for framework-agnostic JWT verification.
33
+ */
34
+ export declare class ClerkAuthPlugin implements AuthPlugin {
35
+ private config;
36
+ private clerkClient;
37
+ constructor(config?: ClerkAuthConfig);
38
+ authenticate(context: unknown): Promise<AuthenticationResult>;
39
+ searchUsers(query: string, limit?: number): Promise<UserSearchResult[]>;
40
+ getUserMetadata(userId: string): Promise<UserSearchResult | null>;
41
+ getGroupMetadata(groupId: string): Promise<GroupMetadata | null>;
42
+ listGroups(limit?: number): Promise<GroupMetadata[]>;
43
+ }
44
+ /**
45
+ * Factory function to create a Clerk auth plugin instance
46
+ */
47
+ export declare const createClerkAuthPlugin: AuthPluginFactory<ClerkAuthConfig>;
48
+ //# sourceMappingURL=clerk-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clerk-plugin.d.ts","sourceRoot":"","sources":["../src/clerk-plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAC3F,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjE,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAA;IAElC;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC7B;AAmED;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,SAAS,WAAW,KAAG,MAAM,GAAG,IAiB5D,CAAA;AAED;;;GAGG;AACH,qBAAa,eAAgB,YAAW,UAAU;IAChD,OAAO,CAAC,MAAM,CAIb;IACD,OAAO,CAAC,WAAW,CAAsC;gBAE7C,MAAM,GAAE,eAAoB;IAyBlC,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAuE7D,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAuBnE,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAgBjE,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAqBhE,UAAU,CAAC,KAAK,SAAK,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;CAqBvD;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,iBAAiB,CAAC,eAAe,CAEpE,CAAA"}
@@ -0,0 +1,214 @@
1
+ import { createClerkClient, verifyToken as clerkVerifyToken } from '@clerk/backend';
2
+ import { extractHeaders } from 'canopycms/auth';
3
+ /**
4
+ * Unwrap Clerk paginated response to array.
5
+ * Clerk SDK sometimes returns arrays directly, sometimes paginated objects.
6
+ */
7
+ function unwrapClerkResponse(response) {
8
+ return Array.isArray(response) ? response : response.data;
9
+ }
10
+ /**
11
+ * Map Clerk user data to Canopy user metadata.
12
+ * Handles both camelCase and snake_case property variants.
13
+ */
14
+ function mapClerkUserData(clerkUser) {
15
+ const avatarUrl = clerkUser.imageUrl ?? clerkUser.image_url;
16
+ return {
17
+ email: clerkUser.primaryEmailAddress?.emailAddress ?? clerkUser.email_addresses?.[0]?.email_address,
18
+ name: clerkUser.fullName ?? clerkUser.full_name ?? clerkUser.username ?? clerkUser.id,
19
+ avatarUrl: avatarUrl ?? undefined,
20
+ };
21
+ }
22
+ /**
23
+ * Get member count from organization, handling property name variants.
24
+ */
25
+ function getOrgMemberCount(org) {
26
+ return org.membersCount ?? org.members_count;
27
+ }
28
+ /**
29
+ * Extract token from headers.
30
+ * Looks for Bearer token in Authorization header or __session cookie.
31
+ */
32
+ export const extractToken = (headers) => {
33
+ // Try Authorization header first
34
+ const authHeader = headers.get('Authorization');
35
+ if (authHeader?.startsWith('Bearer ')) {
36
+ return authHeader.slice(7);
37
+ }
38
+ // Try __session cookie
39
+ const cookie = headers.get('Cookie');
40
+ if (cookie) {
41
+ const match = cookie.match(/__session=([^;]+)/);
42
+ if (match) {
43
+ return match[1];
44
+ }
45
+ }
46
+ return null;
47
+ };
48
+ /**
49
+ * Clerk authentication plugin implementation for CanopyCMS.
50
+ * Uses @clerk/backend for framework-agnostic JWT verification.
51
+ */
52
+ export class ClerkAuthPlugin {
53
+ constructor(config = {}) {
54
+ const secretKey = config.secretKey ?? process.env.CLERK_SECRET_KEY;
55
+ if (!secretKey) {
56
+ throw new Error('ClerkAuthPlugin: CLERK_SECRET_KEY environment variable or secretKey config is required');
57
+ }
58
+ const jwtKey = config.jwtKey ?? process.env.CLERK_JWT_KEY;
59
+ const authorizedParties = config.authorizedParties ??
60
+ process.env.CLERK_AUTHORIZED_PARTIES?.split(',')
61
+ .map((s) => s.trim())
62
+ .filter(Boolean);
63
+ this.config = {
64
+ useOrganizationsAsGroups: config.useOrganizationsAsGroups ?? true,
65
+ secretKey,
66
+ jwtKey,
67
+ authorizedParties,
68
+ };
69
+ this.clerkClient = createClerkClient({ secretKey });
70
+ }
71
+ async authenticate(context) {
72
+ try {
73
+ // Extract headers from context (supports CanopyRequest and Headers)
74
+ const headers = extractHeaders(context);
75
+ if (!headers) {
76
+ return {
77
+ success: false,
78
+ error: 'Invalid context: expected CanopyRequest or Headers object',
79
+ };
80
+ }
81
+ // Extract token from headers
82
+ const token = extractToken(headers);
83
+ if (!token) {
84
+ return { success: false, error: 'No authentication token found' };
85
+ }
86
+ // Verify the token
87
+ const verifyOptions = {
88
+ secretKey: this.config.secretKey,
89
+ };
90
+ if (this.config.jwtKey) {
91
+ verifyOptions.jwtKey = this.config.jwtKey;
92
+ }
93
+ if (this.config.authorizedParties) {
94
+ verifyOptions.authorizedParties = this.config.authorizedParties;
95
+ }
96
+ const payload = await clerkVerifyToken(token, verifyOptions);
97
+ if (!payload || !payload.sub) {
98
+ return { success: false, error: 'Invalid token payload' };
99
+ }
100
+ const userId = payload.sub;
101
+ // Get user details from Clerk
102
+ const clerkUser = (await this.clerkClient.users.getUser(userId));
103
+ // Get organizations as external groups
104
+ let externalGroups;
105
+ if (this.config.useOrganizationsAsGroups) {
106
+ const orgs = (await this.clerkClient.users.getOrganizationMembershipList({
107
+ userId: clerkUser.id,
108
+ }));
109
+ const memberships = unwrapClerkResponse(orgs);
110
+ externalGroups = memberships.map((m) => m.organization.id);
111
+ }
112
+ const userData = mapClerkUserData(clerkUser);
113
+ // Return identity only - core will apply bootstrap admins
114
+ return {
115
+ success: true,
116
+ user: {
117
+ userId: clerkUser.id,
118
+ ...userData,
119
+ externalGroups,
120
+ },
121
+ };
122
+ }
123
+ catch (error) {
124
+ return {
125
+ success: false,
126
+ error: error instanceof Error ? error.message : 'Authentication failed',
127
+ };
128
+ }
129
+ }
130
+ async searchUsers(query, limit = 10) {
131
+ try {
132
+ const response = (await this.clerkClient.users.getUserList({
133
+ query,
134
+ limit,
135
+ }));
136
+ const users = unwrapClerkResponse(response);
137
+ return users.map((u) => {
138
+ const mapped = mapClerkUserData(u);
139
+ return {
140
+ id: u.id,
141
+ name: mapped.name,
142
+ email: mapped.email ?? '',
143
+ avatarUrl: mapped.avatarUrl,
144
+ };
145
+ });
146
+ }
147
+ catch (error) {
148
+ console.error('ClerkAuthPlugin: searchUsers failed', error);
149
+ return [];
150
+ }
151
+ }
152
+ async getUserMetadata(userId) {
153
+ try {
154
+ const user = (await this.clerkClient.users.getUser(userId));
155
+ const mapped = mapClerkUserData(user);
156
+ return {
157
+ id: user.id,
158
+ name: mapped.name,
159
+ email: mapped.email ?? '',
160
+ avatarUrl: mapped.avatarUrl,
161
+ };
162
+ }
163
+ catch (error) {
164
+ console.error('ClerkAuthPlugin: getUserMetadata failed', error);
165
+ return null;
166
+ }
167
+ }
168
+ async getGroupMetadata(groupId) {
169
+ if (!this.config.useOrganizationsAsGroups) {
170
+ return null;
171
+ }
172
+ try {
173
+ const org = (await this.clerkClient.organizations.getOrganization({
174
+ organizationId: groupId,
175
+ }));
176
+ return {
177
+ id: org.id,
178
+ name: org.name,
179
+ memberCount: getOrgMemberCount(org),
180
+ };
181
+ }
182
+ catch (error) {
183
+ console.error('ClerkAuthPlugin: getGroupMetadata failed', error);
184
+ return null;
185
+ }
186
+ }
187
+ async listGroups(limit = 50) {
188
+ if (!this.config.useOrganizationsAsGroups) {
189
+ return [];
190
+ }
191
+ try {
192
+ const response = (await this.clerkClient.organizations.getOrganizationList({
193
+ limit,
194
+ }));
195
+ const orgs = unwrapClerkResponse(response);
196
+ return orgs.map((o) => ({
197
+ id: o.id,
198
+ name: o.name,
199
+ memberCount: getOrgMemberCount(o),
200
+ }));
201
+ }
202
+ catch (error) {
203
+ console.error('ClerkAuthPlugin: listGroups failed', error);
204
+ return [];
205
+ }
206
+ }
207
+ }
208
+ /**
209
+ * Factory function to create a Clerk auth plugin instance
210
+ */
211
+ export const createClerkAuthPlugin = (config) => {
212
+ return new ClerkAuthPlugin(config);
213
+ };
214
+ //# sourceMappingURL=clerk-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clerk-plugin.js","sourceRoot":"","sources":["../src/clerk-plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { CanopyClientConfig } from 'canopycms/client';
2
+ /**
3
+ * Hook that provides Clerk-specific auth handlers and components for CanopyCMS editor.
4
+ * Use this in your edit page to integrate Clerk authentication with CanopyCMS.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * import { useClerkAuthConfig } from 'canopycms-auth-clerk/client'
9
+ * import config from '../../canopycms.config'
10
+ *
11
+ * export default function EditPage() {
12
+ * const clerkAuth = useClerkAuthConfig()
13
+ * const editorConfig = config.client(clerkAuth)
14
+ * return <CanopyEditorPage config={editorConfig} />
15
+ * }
16
+ * ```
17
+ */
18
+ export declare function useClerkAuthConfig(): Pick<CanopyClientConfig, 'editor'>;
19
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAE1D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAevE"}
package/dist/client.js ADDED
@@ -0,0 +1,36 @@
1
+ 'use client';
2
+ import { useClerk } from '@clerk/nextjs';
3
+ import { UserButton } from '@clerk/nextjs';
4
+ /**
5
+ * Hook that provides Clerk-specific auth handlers and components for CanopyCMS editor.
6
+ * Use this in your edit page to integrate Clerk authentication with CanopyCMS.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * import { useClerkAuthConfig } from 'canopycms-auth-clerk/client'
11
+ * import config from '../../canopycms.config'
12
+ *
13
+ * export default function EditPage() {
14
+ * const clerkAuth = useClerkAuthConfig()
15
+ * const editorConfig = config.client(clerkAuth)
16
+ * return <CanopyEditorPage config={editorConfig} />
17
+ * }
18
+ * ```
19
+ */
20
+ export function useClerkAuthConfig() {
21
+ const { signOut } = useClerk();
22
+ return {
23
+ editor: {
24
+ AccountComponent: UserButton,
25
+ onLogoutClick: async () => {
26
+ try {
27
+ await signOut();
28
+ }
29
+ catch (error) {
30
+ console.error('Failed to sign out:', error);
31
+ }
32
+ },
33
+ },
34
+ };
35
+ }
36
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAG1C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,EAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAA;IAE9B,OAAO;QACL,MAAM,EAAE;YACN,gBAAgB,EAAE,UAAU;YAC5B,aAAa,EAAE,KAAK,IAAI,EAAE;gBACxB,IAAI,CAAC;oBACH,MAAM,OAAO,EAAE,CAAA;gBACjB,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAA;gBAC7C,CAAC;YACH,CAAC;SACF;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { ClerkAuthPlugin, createClerkAuthPlugin } from './clerk-plugin';
2
+ export type { ClerkAuthConfig } from './clerk-plugin';
3
+ export { createClerkJwtVerifier } from './jwt-verifier';
4
+ export type { ClerkJwtVerifierConfig } from './jwt-verifier';
5
+ export { refreshClerkCache } from './cache-writer';
6
+ export type { RefreshClerkCacheOptions, RefreshClerkCacheResult } from './cache-writer';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACvE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AACvD,YAAY,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAClD,YAAY,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { ClerkAuthPlugin, createClerkAuthPlugin } from './clerk-plugin';
2
+ export { createClerkJwtVerifier } from './jwt-verifier';
3
+ export { refreshClerkCache } from './cache-writer';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAEvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AAEvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA"}
@@ -0,0 +1,28 @@
1
+ import type { TokenVerifier } from 'canopycms/auth';
2
+ export interface ClerkJwtVerifierConfig {
3
+ /**
4
+ * PEM public key for networkless JWT verification.
5
+ * Required for operation without internet access.
6
+ */
7
+ jwtKey: string;
8
+ /**
9
+ * Clerk Secret Key as fallback (requires internet).
10
+ * If not provided, only jwtKey verification is attempted.
11
+ */
12
+ secretKey?: string;
13
+ /**
14
+ * Authorized parties for CSRF protection.
15
+ */
16
+ authorizedParties?: string[];
17
+ }
18
+ /**
19
+ * Creates a token verifier function that uses Clerk's JWT verification.
20
+ *
21
+ * When jwtKey (PEM public key) is provided, verification is **networkless** —
22
+ * no Clerk API calls are made. This is used in Lambda environments
23
+ * with no internet access.
24
+ *
25
+ * Returns a TokenVerifier compatible with CachingAuthPlugin.
26
+ */
27
+ export declare function createClerkJwtVerifier(config: ClerkJwtVerifierConfig): TokenVerifier;
28
+ //# sourceMappingURL=jwt-verifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt-verifier.d.ts","sourceRoot":"","sources":["../src/jwt-verifier.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAGnD,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB,GAAG,aAAa,CA8BpF"}
@@ -0,0 +1,42 @@
1
+ import { verifyToken as clerkVerifyToken } from '@clerk/backend';
2
+ import { extractHeaders } from 'canopycms/auth';
3
+ import { extractToken } from './clerk-plugin';
4
+ /**
5
+ * Creates a token verifier function that uses Clerk's JWT verification.
6
+ *
7
+ * When jwtKey (PEM public key) is provided, verification is **networkless** —
8
+ * no Clerk API calls are made. This is used in Lambda environments
9
+ * with no internet access.
10
+ *
11
+ * Returns a TokenVerifier compatible with CachingAuthPlugin.
12
+ */
13
+ export function createClerkJwtVerifier(config) {
14
+ return async (context) => {
15
+ const headers = extractHeaders(context);
16
+ if (!headers)
17
+ return null;
18
+ const token = extractToken(headers);
19
+ if (!token)
20
+ return null;
21
+ try {
22
+ const verifyOptions = {};
23
+ if (config.jwtKey) {
24
+ verifyOptions.jwtKey = config.jwtKey;
25
+ }
26
+ if (config.secretKey) {
27
+ verifyOptions.secretKey = config.secretKey;
28
+ }
29
+ if (config.authorizedParties) {
30
+ verifyOptions.authorizedParties = config.authorizedParties;
31
+ }
32
+ const payload = await clerkVerifyToken(token, verifyOptions);
33
+ if (!payload?.sub)
34
+ return null;
35
+ return { userId: payload.sub };
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ };
41
+ }
42
+ //# sourceMappingURL=jwt-verifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt-verifier.js","sourceRoot":"","sources":["../src/jwt-verifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,IAAI,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAE/C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAmB7C;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAA8B;IACnE,OAAO,KAAK,EAAE,OAAgB,EAAE,EAAE;QAChC,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAA;QACvC,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAA;QAEzB,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;QACnC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QAEvB,IAAI,CAAC;YACH,MAAM,aAAa,GAA2C,EAAE,CAAA;YAEhE,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClB,aAAa,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAA;YACtC,CAAC;YACD,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;gBACrB,aAAa,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAA;YAC5C,CAAC;YACD,IAAI,MAAM,CAAC,iBAAiB,EAAE,CAAC;gBAC7B,aAAa,CAAC,iBAAiB,GAAG,MAAM,CAAC,iBAAiB,CAAA;YAC5D,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,KAAK,EAAE,aAAa,CAAC,CAAA;YAE5D,IAAI,CAAC,OAAO,EAAE,GAAG;gBAAE,OAAO,IAAI,CAAA;YAE9B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,CAAA;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC,CAAA;AACH,CAAC"}