canopycms-auth-dev 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 +324 -0
- package/dist/UserSwitcherButton.d.ts +4 -0
- package/dist/UserSwitcherButton.js +23 -0
- package/dist/UserSwitcherButton.js.map +1 -0
- package/dist/UserSwitcherModal.d.ts +10 -0
- package/dist/UserSwitcherModal.js +19 -0
- package/dist/UserSwitcherModal.js.map +1 -0
- package/dist/cache-writer.d.ts +22 -0
- package/dist/cache-writer.js +42 -0
- package/dist/cache-writer.js.map +1 -0
- package/dist/client.d.ts +18 -0
- package/dist/client.js +32 -0
- package/dist/client.js.map +1 -0
- package/dist/cookie-utils.d.ts +20 -0
- package/dist/cookie-utils.js +39 -0
- package/dist/cookie-utils.js.map +1 -0
- package/dist/dev-plugin.d.ts +71 -0
- package/dist/dev-plugin.js +168 -0
- package/dist/dev-plugin.js.map +1 -0
- package/dist/dev-plugin.test.d.ts +1 -0
- package/dist/dev-plugin.test.js +382 -0
- package/dist/dev-plugin.test.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/jwt-verifier.d.ts +12 -0
- package/dist/jwt-verifier.js +41 -0
- package/dist/jwt-verifier.js.map +1 -0
- package/package.json +62 -0
- package/src/UserSwitcherButton.tsx +46 -0
- package/src/UserSwitcherModal.tsx +63 -0
- package/src/cache-writer.ts +61 -0
- package/src/client.ts +34 -0
- package/src/cookie-utils.ts +44 -0
- package/src/dev-plugin.test.ts +470 -0
- package/src/dev-plugin.ts +241 -0
- package/src/index.ts +11 -0
- package/src/jwt-verifier.ts +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# canopycms-auth-dev
|
|
2
|
+
|
|
3
|
+
Development-only authentication provider for CanopyCMS that allows testing the CMS without setting up a real auth provider like Clerk.
|
|
4
|
+
|
|
5
|
+
**⚠️ Development Only**: This package throws an error if `NODE_ENV === 'production'`. Never use it in production.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Zero configuration**: Works out of the box with 5 default users
|
|
10
|
+
- **UI-based user switching**: Click avatar to switch between users in the editor
|
|
11
|
+
- **Test compatibility**: Supports `X-Test-User` header for Playwright tests
|
|
12
|
+
- **Group-based permissions**: Test team-based access control with external groups
|
|
13
|
+
- **Internal groups**: Configure reserved groups (Admins, Reviewers) via `.canopycms/groups.json`
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install canopycms-auth-dev
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### 1. Server-side Setup
|
|
24
|
+
|
|
25
|
+
Replace your auth plugin in the API route:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
// app/lib/canopy.ts
|
|
29
|
+
import { createNextCanopyContext } from 'canopycms-next'
|
|
30
|
+
import { createDevAuthPlugin } from 'canopycms-auth-dev'
|
|
31
|
+
import configBundle from '../../canopycms.config'
|
|
32
|
+
|
|
33
|
+
const { handler } = createNextCanopyContext({
|
|
34
|
+
config: configBundle.server,
|
|
35
|
+
authPlugin: createDevAuthPlugin(),
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
export { handler }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 2. Client-side Setup
|
|
42
|
+
|
|
43
|
+
Use the dev auth hook in your edit page:
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
// app/edit/page.tsx
|
|
47
|
+
'use client'
|
|
48
|
+
|
|
49
|
+
import { useDevAuthConfig } from 'canopycms-auth-dev/client'
|
|
50
|
+
import { NextCanopyEditorPage } from 'canopycms-next/client'
|
|
51
|
+
import config from '../../canopycms.config'
|
|
52
|
+
|
|
53
|
+
export default function EditPage() {
|
|
54
|
+
const devAuth = useDevAuthConfig()
|
|
55
|
+
const clientConfig = config.client(devAuth)
|
|
56
|
+
const EditorPage = NextCanopyEditorPage(clientConfig)
|
|
57
|
+
return <EditorPage />
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 3. Configure Bootstrap Admins
|
|
62
|
+
|
|
63
|
+
Add admin1's user ID to your config:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
// canopycms.config.ts
|
|
67
|
+
export default defineCanopyConfig({
|
|
68
|
+
// ... other config
|
|
69
|
+
bootstrapAdminIds: ['dev_admin_3xY6zW1qR5'], // admin1
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 4. Create Internal Groups (Optional)
|
|
74
|
+
|
|
75
|
+
For Reviewers and other internal groups, create `.canopycms/groups.json`:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"version": 1,
|
|
80
|
+
"updatedAt": "2024-01-01T00:00:00.000Z",
|
|
81
|
+
"updatedBy": "canopycms-system",
|
|
82
|
+
"groups": [
|
|
83
|
+
{
|
|
84
|
+
"id": "Reviewers",
|
|
85
|
+
"name": "Reviewers",
|
|
86
|
+
"description": "Users who can review and approve branches",
|
|
87
|
+
"members": ["dev_reviewer_9aB4cD2eF7"]
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Default Users
|
|
94
|
+
|
|
95
|
+
The plugin comes with 5 pre-configured users:
|
|
96
|
+
|
|
97
|
+
| User | ID | Email | External Groups |
|
|
98
|
+
| ------------ | ------------------------- | ----------------------- | ---------------------- |
|
|
99
|
+
| User One | `dev_user1_2nK8mP4xL9` | user1@localhost.dev | team-a, team-b |
|
|
100
|
+
| User Two | `dev_user2_7qR3tY6wN2` | user2@localhost.dev | team-b |
|
|
101
|
+
| User Three | `dev_user3_5vS1pM8kJ4` | user3@localhost.dev | team-c |
|
|
102
|
+
| Reviewer One | `dev_reviewer_9aB4cD2eF7` | reviewer1@localhost.dev | team-a |
|
|
103
|
+
| Admin One | `dev_admin_3xY6zW1qR5` | admin1@localhost.dev | team-a, team-b, team-c |
|
|
104
|
+
|
|
105
|
+
**Note**: admin1 gets the 'Admins' group via `bootstrapAdminIds` config, not from external groups.
|
|
106
|
+
|
|
107
|
+
## User Switching
|
|
108
|
+
|
|
109
|
+
### In the UI
|
|
110
|
+
|
|
111
|
+
1. Open the editor (`/edit`)
|
|
112
|
+
2. Click the avatar button in the top-right
|
|
113
|
+
3. Select a user from the modal
|
|
114
|
+
4. Page reloads with the new user
|
|
115
|
+
|
|
116
|
+
### In Tests (Playwright)
|
|
117
|
+
|
|
118
|
+
Send the `X-Test-User` header with one of these values:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
// In your test
|
|
122
|
+
await page.setExtraHTTPHeaders({
|
|
123
|
+
'X-Test-User': 'admin', // Maps to admin1 (dev_admin_3xY6zW1qR5)
|
|
124
|
+
})
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Test user mappings:
|
|
128
|
+
|
|
129
|
+
- `admin` → admin1 (dev_admin_3xY6zW1qR5)
|
|
130
|
+
- `editor` → user1 (dev_user1_2nK8mP4xL9)
|
|
131
|
+
- `viewer` → user2 (dev_user2_7qR3tY6wN2)
|
|
132
|
+
- `reviewer` → reviewer1 (dev_reviewer_9aB4cD2eF7)
|
|
133
|
+
|
|
134
|
+
## Configuration
|
|
135
|
+
|
|
136
|
+
Customize users and groups:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { createDevAuthPlugin } from 'canopycms-auth-dev'
|
|
140
|
+
|
|
141
|
+
const authPlugin = createDevAuthPlugin({
|
|
142
|
+
defaultUserId: 'dev_user1_2nK8mP4xL9', // user1
|
|
143
|
+
users: [
|
|
144
|
+
{
|
|
145
|
+
userId: 'custom_user1',
|
|
146
|
+
name: 'Custom User',
|
|
147
|
+
email: 'custom@example.com',
|
|
148
|
+
externalGroups: ['team-x'],
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
groups: [{ id: 'team-x', name: 'Team X', description: 'Custom team' }],
|
|
152
|
+
})
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## How It Works
|
|
156
|
+
|
|
157
|
+
### Authentication Flow
|
|
158
|
+
|
|
159
|
+
1. **Request arrives** → Plugin checks for user identifier
|
|
160
|
+
2. **Priority order**:
|
|
161
|
+
- `X-Test-User` header (for tests)
|
|
162
|
+
- `x-dev-user-id` header (custom)
|
|
163
|
+
- `canopy-dev-user` cookie (from UI)
|
|
164
|
+
- Default user (user1)
|
|
165
|
+
3. **User lookup** → Find user in config
|
|
166
|
+
4. **Groups assigned**:
|
|
167
|
+
- External groups (from auth plugin)
|
|
168
|
+
- Bootstrap admin groups (from config)
|
|
169
|
+
- Internal groups (from `.canopycms/groups.json`)
|
|
170
|
+
|
|
171
|
+
### Group Types
|
|
172
|
+
|
|
173
|
+
- **External groups**: Returned by auth plugin (e.g., team-a, team-b, team-c)
|
|
174
|
+
- **Bootstrap admins**: Added automatically from `bootstrapAdminIds` config
|
|
175
|
+
- **Internal groups**: Loaded from `.canopycms/groups.json` (managed by admins via UI)
|
|
176
|
+
|
|
177
|
+
### Reserved Groups
|
|
178
|
+
|
|
179
|
+
- **`Admins`**: Full access to all CMS operations
|
|
180
|
+
- **`Reviewers`**: Can review branches, request changes, approve PRs
|
|
181
|
+
|
|
182
|
+
## API
|
|
183
|
+
|
|
184
|
+
### `createDevAuthPlugin(config?)`
|
|
185
|
+
|
|
186
|
+
Factory function that creates a dev auth plugin.
|
|
187
|
+
|
|
188
|
+
**Parameters:**
|
|
189
|
+
|
|
190
|
+
- `config.users?` - Custom user list
|
|
191
|
+
- `config.groups?` - Custom group list
|
|
192
|
+
- `config.defaultUserId?` - Default user when none specified
|
|
193
|
+
|
|
194
|
+
**Returns:** `AuthPlugin`
|
|
195
|
+
|
|
196
|
+
### `useDevAuthConfig()`
|
|
197
|
+
|
|
198
|
+
React hook that provides editor configuration with user switcher.
|
|
199
|
+
|
|
200
|
+
**Returns:** `Pick<CanopyClientConfig, 'editor'>`
|
|
201
|
+
|
|
202
|
+
### Exports
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
// Server-side
|
|
206
|
+
export { createDevAuthPlugin, DevAuthPlugin, DEFAULT_USERS, DEFAULT_GROUPS }
|
|
207
|
+
export type { DevAuthConfig, DevUser, DevGroup }
|
|
208
|
+
|
|
209
|
+
// Client-side (import from 'canopycms-auth-dev/client')
|
|
210
|
+
export { useDevAuthConfig }
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Switching Between Auth Providers
|
|
214
|
+
|
|
215
|
+
You can configure your app to switch between dev auth and production auth (like Clerk) using environment variables:
|
|
216
|
+
|
|
217
|
+
### Server-side (app/lib/canopy.ts)
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
import { createNextCanopyContext } from 'canopycms-next'
|
|
221
|
+
import { createClerkAuthPlugin } from 'canopycms-auth-clerk'
|
|
222
|
+
import { createDevAuthPlugin } from 'canopycms-auth-dev'
|
|
223
|
+
import type { AuthPlugin } from 'canopycms/auth'
|
|
224
|
+
import config from '../../canopycms.config'
|
|
225
|
+
|
|
226
|
+
function getAuthPlugin(): AuthPlugin {
|
|
227
|
+
const authMode = process.env.CANOPY_AUTH_MODE || 'dev'
|
|
228
|
+
|
|
229
|
+
if (authMode === 'dev') {
|
|
230
|
+
return createDevAuthPlugin()
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (authMode === 'clerk') {
|
|
234
|
+
return createClerkAuthPlugin({
|
|
235
|
+
useOrganizationsAsGroups: true,
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
throw new Error(`Invalid CANOPY_AUTH_MODE: "${authMode}". Must be "dev" or "clerk".`)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const canopyContext = createNextCanopyContext({
|
|
243
|
+
config: config.server,
|
|
244
|
+
authPlugin: getAuthPlugin(),
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
export const getCanopy = canopyContext.getCanopy
|
|
248
|
+
export const handler = canopyContext.handler
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Client-side (app/edit/page.tsx)
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
'use client'
|
|
255
|
+
|
|
256
|
+
import { useClerkAuthConfig } from 'canopycms-auth-clerk/client'
|
|
257
|
+
import { useDevAuthConfig } from 'canopycms-auth-dev/client'
|
|
258
|
+
import { NextCanopyEditorPage } from 'canopycms-next/client'
|
|
259
|
+
import config from '../../canopycms.config'
|
|
260
|
+
|
|
261
|
+
function useAuthConfig() {
|
|
262
|
+
const authMode = process.env.NEXT_PUBLIC_CANOPY_AUTH_MODE || 'dev'
|
|
263
|
+
|
|
264
|
+
if (authMode === 'dev') {
|
|
265
|
+
return useDevAuthConfig()
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (authMode === 'clerk') {
|
|
269
|
+
return useClerkAuthConfig()
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
throw new Error(`Invalid NEXT_PUBLIC_CANOPY_AUTH_MODE: "${authMode}". Must be "dev" or "clerk".`)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export default function EditPage() {
|
|
276
|
+
const authConfig = useAuthConfig()
|
|
277
|
+
const clientConfig = config.client(authConfig)
|
|
278
|
+
const EditorPage = NextCanopyEditorPage(clientConfig)
|
|
279
|
+
return <EditorPage />
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Environment Configuration
|
|
284
|
+
|
|
285
|
+
**Default: Dev auth is enabled by default** (no configuration needed)
|
|
286
|
+
|
|
287
|
+
To switch to Clerk, create `.env.local`:
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
# Use Clerk authentication
|
|
291
|
+
CANOPY_AUTH_MODE=clerk
|
|
292
|
+
NEXT_PUBLIC_CANOPY_AUTH_MODE=clerk
|
|
293
|
+
|
|
294
|
+
# Clerk configuration
|
|
295
|
+
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_key
|
|
296
|
+
CLERK_SECRET_KEY=your_secret
|
|
297
|
+
|
|
298
|
+
# Bootstrap admin (use your Clerk user ID)
|
|
299
|
+
CANOPY_BOOTSTRAP_ADMIN_IDS=user_xxxxxxxxxxxxx
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
To switch back to dev auth, just remove `.env.local` or set the mode to `dev`.
|
|
303
|
+
|
|
304
|
+
**Optional dev auth configuration** (`.env.local`):
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
# Bootstrap admin for dev mode
|
|
308
|
+
CANOPY_BOOTSTRAP_ADMIN_IDS=dev_admin_3xY6zW1qR5
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Benefits
|
|
312
|
+
|
|
313
|
+
- **No code changes**: Switch auth modes by changing environment variables
|
|
314
|
+
- **Team flexibility**: Developers can use dev auth locally while staging/production uses Clerk
|
|
315
|
+
- **Easy testing**: Quickly test features without auth provider setup
|
|
316
|
+
- **Clean separation**: Same codebase works with multiple auth providers
|
|
317
|
+
|
|
318
|
+
## Production Safety
|
|
319
|
+
|
|
320
|
+
⚠️ **WARNING**: This plugin is for development and testing only. Do not use in production environments.
|
|
321
|
+
|
|
322
|
+
## License
|
|
323
|
+
|
|
324
|
+
MIT
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect } from 'react';
|
|
4
|
+
import { ActionIcon, Avatar } from '@mantine/core';
|
|
5
|
+
import { UserSwitcherModal } from './UserSwitcherModal';
|
|
6
|
+
import { DEFAULT_USERS } from './dev-plugin';
|
|
7
|
+
import { getDevUserCookie, DEFAULT_USER_ID } from './cookie-utils';
|
|
8
|
+
/**
|
|
9
|
+
* User switcher button component that shows current user avatar and opens modal
|
|
10
|
+
*/
|
|
11
|
+
export function UserSwitcherButton() {
|
|
12
|
+
const [opened, setOpened] = useState(false);
|
|
13
|
+
const [mounted, setMounted] = useState(false);
|
|
14
|
+
// Only read cookie after mount to avoid hydration mismatch
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
setMounted(true);
|
|
17
|
+
}, []);
|
|
18
|
+
// Read current user from cookie (only on client)
|
|
19
|
+
const currentUserId = mounted ? (getDevUserCookie() ?? DEFAULT_USER_ID) : DEFAULT_USER_ID;
|
|
20
|
+
const currentUser = DEFAULT_USERS.find((u) => u.userId === currentUserId);
|
|
21
|
+
return (_jsxs(_Fragment, { children: [_jsx(ActionIcon, { variant: "subtle", size: "lg", radius: "md", onClick: () => setOpened(true), "aria-label": "Switch user", children: _jsx(Avatar, { size: "sm", color: "blue", children: currentUser?.name[0] ?? 'U' }) }), _jsx(UserSwitcherModal, { opened: opened, onClose: () => setOpened(false), currentUserId: currentUserId })] }));
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=UserSwitcherButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserSwitcherButton.js","sourceRoot":"","sources":["../src/UserSwitcherButton.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAElE;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC3C,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAE7C,2DAA2D;IAC3D,SAAS,CAAC,GAAG,EAAE;QACb,UAAU,CAAC,IAAI,CAAC,CAAA;IAClB,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,iDAAiD;IACjD,MAAM,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,gBAAgB,EAAE,IAAI,eAAe,CAAC,CAAC,CAAC,CAAC,eAAe,CAAA;IACzF,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,CAAA;IAEzE,OAAO,CACL,8BACE,KAAC,UAAU,IACT,OAAO,EAAC,QAAQ,EAChB,IAAI,EAAC,IAAI,EACT,MAAM,EAAC,IAAI,EACX,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,gBACnB,aAAa,YAExB,KAAC,MAAM,IAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC,MAAM,YAC3B,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,GACrB,GACE,EAEb,KAAC,iBAAiB,IAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,EAC/B,aAAa,EAAE,aAAa,GAC5B,IACD,CACJ,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
opened: boolean;
|
|
3
|
+
onClose: () => void;
|
|
4
|
+
currentUserId: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* User switcher modal component that displays all available dev users
|
|
8
|
+
*/
|
|
9
|
+
export declare function UserSwitcherModal({ opened, onClose, currentUserId }: Props): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Modal, Stack, Paper, Group, Avatar, Text, Badge } from '@mantine/core';
|
|
4
|
+
import { MdCheck } from 'react-icons/md';
|
|
5
|
+
import { DEFAULT_USERS } from './dev-plugin';
|
|
6
|
+
import { setDevUserCookie } from './cookie-utils';
|
|
7
|
+
/**
|
|
8
|
+
* User switcher modal component that displays all available dev users
|
|
9
|
+
*/
|
|
10
|
+
export function UserSwitcherModal({ opened, onClose, currentUserId }) {
|
|
11
|
+
const switchUser = (userId) => {
|
|
12
|
+
// Set cookie for 7 days
|
|
13
|
+
setDevUserCookie(userId);
|
|
14
|
+
// Reload to apply new user
|
|
15
|
+
window.location.reload();
|
|
16
|
+
};
|
|
17
|
+
return (_jsx(Modal, { opened: opened, onClose: onClose, title: "Switch Development User", children: _jsx(Stack, { gap: "sm", children: DEFAULT_USERS.map((user) => (_jsxs(Paper, { p: "md", withBorder: true, style: { cursor: 'pointer' }, onClick: () => switchUser(user.userId), children: [_jsxs(Group, { justify: "space-between", mb: "xs", children: [_jsxs(Group, { children: [_jsx(Avatar, { color: "blue", children: user.name[0] }), _jsxs("div", { children: [_jsx(Text, { fw: 500, children: user.name }), _jsx(Text, { size: "sm", c: "dimmed", children: user.email })] })] }), user.userId === currentUserId && _jsx(MdCheck, { size: 20 })] }), user.externalGroups.length > 0 && (_jsx(Group, { gap: "xs", children: user.externalGroups.map((g) => (_jsx(Badge, { variant: "outline", size: "sm", children: g }, g))) }))] }, user.userId))) }) }));
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=UserSwitcherModal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserSwitcherModal.js","sourceRoot":"","sources":["../src/UserSwitcherModal.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,eAAe,CAAA;AAC/E,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAQjD;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAS;IACzE,MAAM,UAAU,GAAG,CAAC,MAAc,EAAE,EAAE;QACpC,wBAAwB;QACxB,gBAAgB,CAAC,MAAM,CAAC,CAAA;QACxB,2BAA2B;QAC3B,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAA;IAC1B,CAAC,CAAA;IAED,OAAO,CACL,KAAC,KAAK,IAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAC,yBAAyB,YACtE,KAAC,KAAK,IAAC,GAAG,EAAC,IAAI,YACZ,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAC3B,MAAC,KAAK,IAEJ,CAAC,EAAC,IAAI,EACN,UAAU,QACV,KAAK,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,EAC5B,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,aAEtC,MAAC,KAAK,IAAC,OAAO,EAAC,eAAe,EAAC,EAAE,EAAC,IAAI,aACpC,MAAC,KAAK,eACJ,KAAC,MAAM,IAAC,KAAK,EAAC,MAAM,YAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAU,EAC5C,0BACE,KAAC,IAAI,IAAC,EAAE,EAAE,GAAG,YAAG,IAAI,CAAC,IAAI,GAAQ,EACjC,KAAC,IAAI,IAAC,IAAI,EAAC,IAAI,EAAC,CAAC,EAAC,QAAQ,YACvB,IAAI,CAAC,KAAK,GACN,IACH,IACA,EACP,IAAI,CAAC,MAAM,KAAK,aAAa,IAAI,KAAC,OAAO,IAAC,IAAI,EAAE,EAAE,GAAI,IACjD,EAEP,IAAI,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,IAAI,CACjC,KAAC,KAAK,IAAC,GAAG,EAAC,IAAI,YACZ,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAC9B,KAAC,KAAK,IAAS,OAAO,EAAC,SAAS,EAAC,IAAI,EAAC,IAAI,YACvC,CAAC,IADQ,CAAC,CAEL,CACT,CAAC,GACI,CACT,KA3BI,IAAI,CAAC,MAAM,CA4BV,CACT,CAAC,GACI,GACF,CACT,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { DevUser, DevGroup } from './dev-plugin';
|
|
2
|
+
export interface RefreshDevCacheOptions {
|
|
3
|
+
/** Directory to write cache files to (e.g., .canopy-prod-sim/.cache) */
|
|
4
|
+
cachePath: string;
|
|
5
|
+
/** Custom users (defaults to DEFAULT_USERS) */
|
|
6
|
+
users?: DevUser[];
|
|
7
|
+
/** Custom groups (defaults to DEFAULT_GROUPS) */
|
|
8
|
+
groups?: DevGroup[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Write dev users/groups to cache files for FileBasedAuthCache.
|
|
12
|
+
*
|
|
13
|
+
* This is the dev-auth equivalent of refreshClerkCache() — it populates
|
|
14
|
+
* the same JSON files that CachingAuthPlugin reads. Since dev users are
|
|
15
|
+
* hardcoded, no API calls are needed.
|
|
16
|
+
*
|
|
17
|
+
* Used by the worker's `run-once` command in prod-sim mode with dev auth.
|
|
18
|
+
*/
|
|
19
|
+
export declare function refreshDevCache(options: RefreshDevCacheOptions): Promise<{
|
|
20
|
+
userCount: number;
|
|
21
|
+
groupCount: number;
|
|
22
|
+
}>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { writeAuthCacheSnapshot } from 'canopycms/auth/cache';
|
|
2
|
+
import { DEFAULT_USERS, DEFAULT_GROUPS } from './dev-plugin';
|
|
3
|
+
/**
|
|
4
|
+
* Write dev users/groups to cache files for FileBasedAuthCache.
|
|
5
|
+
*
|
|
6
|
+
* This is the dev-auth equivalent of refreshClerkCache() — it populates
|
|
7
|
+
* the same JSON files that CachingAuthPlugin reads. Since dev users are
|
|
8
|
+
* hardcoded, no API calls are needed.
|
|
9
|
+
*
|
|
10
|
+
* Used by the worker's `run-once` command in prod-sim mode with dev auth.
|
|
11
|
+
*/
|
|
12
|
+
export async function refreshDevCache(options) {
|
|
13
|
+
const { cachePath } = options;
|
|
14
|
+
const users = options.users ?? DEFAULT_USERS;
|
|
15
|
+
const groups = options.groups ?? DEFAULT_GROUPS;
|
|
16
|
+
const usersData = {
|
|
17
|
+
users: users.map((u) => ({
|
|
18
|
+
id: u.userId,
|
|
19
|
+
name: u.name,
|
|
20
|
+
email: u.email,
|
|
21
|
+
avatarUrl: u.avatarUrl,
|
|
22
|
+
})),
|
|
23
|
+
};
|
|
24
|
+
const groupsData = {
|
|
25
|
+
groups: groups.map((g) => ({
|
|
26
|
+
id: g.id,
|
|
27
|
+
name: g.name,
|
|
28
|
+
description: g.description,
|
|
29
|
+
})),
|
|
30
|
+
};
|
|
31
|
+
const membershipsData = {
|
|
32
|
+
memberships: Object.fromEntries(users.filter((u) => u.externalGroups.length > 0).map((u) => [u.userId, u.externalGroups])),
|
|
33
|
+
};
|
|
34
|
+
// Write cache files atomically via snapshot directory + symlink swap
|
|
35
|
+
await writeAuthCacheSnapshot(cachePath, {
|
|
36
|
+
'users.json': usersData,
|
|
37
|
+
'orgs.json': groupsData,
|
|
38
|
+
'memberships.json': membershipsData,
|
|
39
|
+
});
|
|
40
|
+
return { userCount: users.length, groupCount: groups.length };
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=cache-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-writer.js","sourceRoot":"","sources":["../src/cache-writer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAC7D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAY5D;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAA+B;IAE/B,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAA;IAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,aAAa,CAAA;IAC5C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,cAAc,CAAA;IAE/C,MAAM,SAAS,GAAG;QAChB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACvB,EAAE,EAAE,CAAC,CAAC,MAAM;YACZ,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,SAAS,EAAE,CAAC,CAAC,SAAS;SACvB,CAAC,CAAC;KACJ,CAAA;IAED,MAAM,UAAU,GAAG;QACjB,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACzB,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;SAC3B,CAAC,CAAC;KACJ,CAAA;IAED,MAAM,eAAe,GAAG;QACtB,WAAW,EAAE,MAAM,CAAC,WAAW,CAC7B,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,cAAc,CAAC,CAAC,CAC1F;KACF,CAAA;IAED,qEAAqE;IACrE,MAAM,sBAAsB,CAAC,SAAS,EAAE;QACtC,YAAY,EAAE,SAAS;QACvB,WAAW,EAAE,UAAU;QACvB,kBAAkB,EAAE,eAAe;KACpC,CAAC,CAAA;IAEF,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAA;AAC/D,CAAC"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CanopyClientConfig } from 'canopycms/client';
|
|
2
|
+
/**
|
|
3
|
+
* Hook that provides dev auth handlers and components for CanopyCMS editor.
|
|
4
|
+
* Model after: packages/canopycms-auth-clerk/src/client.ts
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* import { useDevAuthConfig } from 'canopycms-auth-dev/client'
|
|
9
|
+
* import config from '../../canopycms.config'
|
|
10
|
+
*
|
|
11
|
+
* export default function EditPage() {
|
|
12
|
+
* const devAuth = useDevAuthConfig()
|
|
13
|
+
* const editorConfig = config.client(devAuth)
|
|
14
|
+
* return <CanopyEditorPage config={editorConfig} />
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function useDevAuthConfig(): Pick<CanopyClientConfig, 'editor'>;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { UserSwitcherButton } from './UserSwitcherButton';
|
|
3
|
+
import { clearDevUserCookie } from './cookie-utils';
|
|
4
|
+
/**
|
|
5
|
+
* Hook that provides dev auth handlers and components for CanopyCMS editor.
|
|
6
|
+
* Model after: packages/canopycms-auth-clerk/src/client.ts
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* import { useDevAuthConfig } from 'canopycms-auth-dev/client'
|
|
11
|
+
* import config from '../../canopycms.config'
|
|
12
|
+
*
|
|
13
|
+
* export default function EditPage() {
|
|
14
|
+
* const devAuth = useDevAuthConfig()
|
|
15
|
+
* const editorConfig = config.client(devAuth)
|
|
16
|
+
* return <CanopyEditorPage config={editorConfig} />
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function useDevAuthConfig() {
|
|
21
|
+
return {
|
|
22
|
+
editor: {
|
|
23
|
+
AccountComponent: UserSwitcherButton,
|
|
24
|
+
onLogoutClick: () => {
|
|
25
|
+
// Reset to default user
|
|
26
|
+
clearDevUserCookie();
|
|
27
|
+
window.location.reload();
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAGZ,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAEnD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO;QACL,MAAM,EAAE;YACN,gBAAgB,EAAE,kBAAkB;YACpC,aAAa,EAAE,GAAG,EAAE;gBAClB,wBAAwB;gBACxB,kBAAkB,EAAE,CAAA;gBACpB,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAA;YAC1B,CAAC;SACF;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { HeadersLike } from 'canopycms/auth';
|
|
2
|
+
export declare const DEV_USER_COOKIE_NAME = "canopy-dev-user";
|
|
3
|
+
export declare const DEV_USER_COOKIE_MAX_AGE: number;
|
|
4
|
+
export declare const DEFAULT_USER_ID = "dev_user1_2nK8mP4xL9";
|
|
5
|
+
/**
|
|
6
|
+
* Server-side: Extract cookie value from HTTP headers
|
|
7
|
+
*/
|
|
8
|
+
export declare function getDevUserCookieFromHeaders(headers: HeadersLike): string | null;
|
|
9
|
+
/**
|
|
10
|
+
* Client-side: Read cookie from document.cookie
|
|
11
|
+
*/
|
|
12
|
+
export declare function getDevUserCookie(): string | null;
|
|
13
|
+
/**
|
|
14
|
+
* Client-side: Set dev user cookie
|
|
15
|
+
*/
|
|
16
|
+
export declare function setDevUserCookie(userId: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* Client-side: Clear dev user cookie (logout)
|
|
19
|
+
*/
|
|
20
|
+
export declare function clearDevUserCookie(): void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const DEV_USER_COOKIE_NAME = 'canopy-dev-user';
|
|
2
|
+
export const DEV_USER_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; // 7 days
|
|
3
|
+
export const DEFAULT_USER_ID = 'dev_user1_2nK8mP4xL9';
|
|
4
|
+
/**
|
|
5
|
+
* Server-side: Extract cookie value from HTTP headers
|
|
6
|
+
*/
|
|
7
|
+
export function getDevUserCookieFromHeaders(headers) {
|
|
8
|
+
const cookie = headers.get('Cookie');
|
|
9
|
+
if (!cookie)
|
|
10
|
+
return null;
|
|
11
|
+
const match = cookie.match(new RegExp(`${DEV_USER_COOKIE_NAME}=([^;]+)`));
|
|
12
|
+
return match?.[1] ?? null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Client-side: Read cookie from document.cookie
|
|
16
|
+
*/
|
|
17
|
+
export function getDevUserCookie() {
|
|
18
|
+
if (typeof document === 'undefined')
|
|
19
|
+
return null;
|
|
20
|
+
const match = document.cookie.match(new RegExp(`${DEV_USER_COOKIE_NAME}=([^;]+)`));
|
|
21
|
+
return match?.[1] ?? null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Client-side: Set dev user cookie
|
|
25
|
+
*/
|
|
26
|
+
export function setDevUserCookie(userId) {
|
|
27
|
+
if (typeof document === 'undefined')
|
|
28
|
+
return;
|
|
29
|
+
document.cookie = `${DEV_USER_COOKIE_NAME}=${userId}; path=/; max-age=${DEV_USER_COOKIE_MAX_AGE}; SameSite=Lax`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Client-side: Clear dev user cookie (logout)
|
|
33
|
+
*/
|
|
34
|
+
export function clearDevUserCookie() {
|
|
35
|
+
if (typeof document === 'undefined')
|
|
36
|
+
return;
|
|
37
|
+
document.cookie = `${DEV_USER_COOKIE_NAME}=; path=/; max-age=0`;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=cookie-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookie-utils.js","sourceRoot":"","sources":["../src/cookie-utils.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,oBAAoB,GAAG,iBAAiB,CAAA;AACrD,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA,CAAC,SAAS;AACjE,MAAM,CAAC,MAAM,eAAe,GAAG,sBAAsB,CAAA;AAErD;;GAEG;AACH,MAAM,UAAU,2BAA2B,CAAC,OAAoB;IAC9D,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACpC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAExB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,GAAG,oBAAoB,UAAU,CAAC,CAAC,CAAA;IACzE,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;AAC3B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO,IAAI,CAAA;IAEhD,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,GAAG,oBAAoB,UAAU,CAAC,CAAC,CAAA;IAClF,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;AAC3B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAM;IAE3C,QAAQ,CAAC,MAAM,GAAG,GAAG,oBAAoB,IAAI,MAAM,qBAAqB,uBAAuB,gBAAgB,CAAA;AACjH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAM;IAE3C,QAAQ,CAAC,MAAM,GAAG,GAAG,oBAAoB,sBAAsB,CAAA;AACjE,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { AuthPlugin } from 'canopycms/auth';
|
|
2
|
+
import type { UserSearchResult, GroupMetadata, AuthenticationResult } from 'canopycms/auth';
|
|
3
|
+
import type { CanopyUserId, CanopyGroupId } from 'canopycms';
|
|
4
|
+
/**
|
|
5
|
+
* WARNING: This plugin is for development and testing only!
|
|
6
|
+
* Do not use in production environments.
|
|
7
|
+
*/
|
|
8
|
+
export interface DevUser {
|
|
9
|
+
userId: CanopyUserId;
|
|
10
|
+
name: string;
|
|
11
|
+
email: string;
|
|
12
|
+
avatarUrl?: string;
|
|
13
|
+
externalGroups: CanopyGroupId[];
|
|
14
|
+
}
|
|
15
|
+
export interface DevGroup {
|
|
16
|
+
id: CanopyGroupId;
|
|
17
|
+
name: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare const DEV_ADMIN_USER_ID: CanopyUserId;
|
|
21
|
+
export interface DevAuthConfig {
|
|
22
|
+
/**
|
|
23
|
+
* Custom mock users. If not provided, uses default users.
|
|
24
|
+
*/
|
|
25
|
+
users?: DevUser[];
|
|
26
|
+
/**
|
|
27
|
+
* Custom mock groups. If not provided, uses default groups.
|
|
28
|
+
*/
|
|
29
|
+
groups?: DevGroup[];
|
|
30
|
+
/**
|
|
31
|
+
* Default user ID when no user is selected.
|
|
32
|
+
* @default 'dev_user1_2nK8mP4xL9' (user1)
|
|
33
|
+
*/
|
|
34
|
+
defaultUserId?: CanopyUserId;
|
|
35
|
+
/**
|
|
36
|
+
* Whether to auto-set CANOPY_BOOTSTRAP_ADMIN_IDS for the admin dev user
|
|
37
|
+
* when the env var is not already set. Defaults to true.
|
|
38
|
+
*/
|
|
39
|
+
autoBootstrapAdmin?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export declare const DEFAULT_USERS: DevUser[];
|
|
42
|
+
export declare const DEFAULT_GROUPS: DevGroup[];
|
|
43
|
+
/**
|
|
44
|
+
* Dev authentication plugin implementation for CanopyCMS.
|
|
45
|
+
* Supports both cookie-based (UI) and header-based (tests) authentication.
|
|
46
|
+
*/
|
|
47
|
+
export declare class DevAuthPlugin implements AuthPlugin {
|
|
48
|
+
private users;
|
|
49
|
+
private groups;
|
|
50
|
+
private defaultUserId;
|
|
51
|
+
constructor(config?: DevAuthConfig);
|
|
52
|
+
authenticate(context: unknown): Promise<AuthenticationResult>;
|
|
53
|
+
/**
|
|
54
|
+
* Map test-app user keys to dev user IDs for backward compatibility
|
|
55
|
+
*/
|
|
56
|
+
private mapTestUserKey;
|
|
57
|
+
searchUsers(query: string, limit?: number): Promise<UserSearchResult[]>;
|
|
58
|
+
getUserMetadata(userId: CanopyUserId): Promise<UserSearchResult | null>;
|
|
59
|
+
getGroupMetadata(groupId: CanopyGroupId): Promise<GroupMetadata | null>;
|
|
60
|
+
listGroups(limit?: number): Promise<GroupMetadata[]>;
|
|
61
|
+
searchExternalGroups(query: string): Promise<Array<{
|
|
62
|
+
id: CanopyGroupId;
|
|
63
|
+
name: string;
|
|
64
|
+
}>>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Factory function for creating dev auth plugin.
|
|
68
|
+
* By default, auto-sets CANOPY_BOOTSTRAP_ADMIN_IDS to the admin dev user
|
|
69
|
+
* if the env var is not already set. Disable with { autoBootstrapAdmin: false }.
|
|
70
|
+
*/
|
|
71
|
+
export declare function createDevAuthPlugin(config?: DevAuthConfig): AuthPlugin;
|