Skip to content

Commit 7597a10

Browse files
authored
Merge pull request #46 from chronon/f/token-auth-only
f/token auth only
2 parents 9380e8d + 36e161c commit 7597a10

File tree

9 files changed

+232
-254
lines changed

9 files changed

+232
-254
lines changed

.github/copilot-instructions.md

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -224,22 +224,22 @@ Authenticated endpoints for managing photos. All admin endpoints use shared util
224224
- Pattern: All admin endpoints use discriminated unions for validation results
225225

226226
- **Upload** (`POST /admin/api/images`):
227-
- Validates via Cloudflare Access (service tokens or IdP users)
227+
- Validates via Cloudflare Access (service tokens)
228228
- Authorizes client ID against user's `authorized_client_ids` in KV
229229
- Uploads photo to Cloudflare Images
230230
- Inserts metadata to D1
231231
- Returns `{ success: true, id: string, filename: string, uploaded: string }`
232232

233233
- **Lookup** (`GET /admin/api/images/by-name/[photoName]`):
234-
- Validates via Cloudflare Access (service tokens or IdP users)
234+
- Validates via Cloudflare Access (service tokens)
235235
- Authorizes client ID against user's `authorized_client_ids` in KV
236236
- Searches for photo by name (case-insensitive)
237237
- Returns most recent photo if multiple matches exist
238238
- Returns `{ success: true, id: string, name: string, captured: string, uploaded: string }`
239239
- Use case: Enables automation tools (like Apple Shortcuts) to find image ID by photo name
240240

241241
- **Delete** (`DELETE /admin/api/images/[imageId]`):
242-
- Validates via Cloudflare Access (service tokens or IdP users)
242+
- Validates via Cloudflare Access (service tokens)
243243
- Authorizes client ID against user's `authorized_client_ids` in KV
244244
- Verifies ownership (prevents cross-user deletion)
245245
- Deletes metadata from D1
@@ -277,7 +277,7 @@ All `/admin/*` routes use centralized authentication via SvelteKit hooks:
277277
7. Sets `event.locals.adminAuth` with authenticated context
278278
8. Request proceeds to handler with authenticated user info
279279

280-
### Supported Authentication Types
280+
### Supported Authentication Type
281281

282282
**Service Tokens** (Machine-to-machine):
283283

@@ -290,17 +290,6 @@ Headers:
290290
- Used for automated uploads from scripts/applications
291291
- Client ID validated against `authorized_client_ids` in KV
292292

293-
**IdP Users** (Browser-based):
294-
295-
```
296-
Headers:
297-
CF-Access-Jwt-Assertion: eyJhbGc...
298-
```
299-
300-
- Authenticated via identity providers (Google, GitHub, etc.)
301-
- Email address extracted from JWT
302-
- Email can be added to `authorized_client_ids` for authorization
303-
304293
### Local Development Bypass
305294

306295
Development uses authentication bypass that **only** activates when `CF_ACCESS_TEAM_DOMAIN=dev`:
@@ -339,9 +328,8 @@ export const POST: RequestHandler = async ({ locals }) => {
339328
}
340329

341330
const { username, identity } = locals.adminAuth;
342-
// identity.type: 'service_token' | 'idp_user'
331+
// identity.type: 'service_token'
343332
// identity.clientId: string
344-
// identity.email?: string (for IdP users)
345333

346334
// Your handler logic here...
347335
};
@@ -376,7 +364,6 @@ const event = {
376364
3. **Test with real service tokens** - Use `pnpm preview` for realistic testing
377365
4. **Never commit secrets** - Keep `.dev.vars` gitignored
378366
5. **Update authorized_client_ids** - Add client IDs to KV config for authorization
379-
6. **Use appropriate auth type** - Service tokens for automation, IdP for browsers
380367

381368
### Domain-Based Routing
382369

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@ vite.config.ts.timestamp-*
3030
/.claude
3131

3232
# config
33-
config/
33+
config/app.jsonc
34+
config/app.kv.json
3435
.wrangler

CLAUDE.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,9 @@ The app uses a centralized authentication system for all `/admin/*` routes:
100100
5. Checks client ID against user's `authorized_client_ids` in KV
101101
6. Sets authenticated context in `event.locals.adminAuth` for downstream handlers
102102

103-
**Supported Authentication Types:**
103+
**Supported Authentication Type:**
104104

105105
- **Service Tokens** - For automated clients (client ID from `common_name` JWT claim or header)
106-
- **IdP Users** - For browser-based access (client ID from `email` JWT claim)
107106

108107
**Local Development:**
109108

README.md

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ All `/admin/*` routes are protected by a centralized authentication system that
270270
**Flow:**
271271

272272
1. Request hits `/admin/*` route
273-
2. Cloudflare Access validates credentials at edge (service token or IdP authentication)
273+
2. Cloudflare Access validates credentials at edge (service token authentication)
274274
3. `handleAdminAuth` hook intercepts request
275275
4. Extracts identity from Cloudflare Access headers
276276
5. Validates JWT claims (expiration, issuer)
@@ -327,20 +327,14 @@ Add the Client ID to user's authorized list in `config/app.jsonc`:
327327
pnpm deploy
328328
```
329329

330-
### Supported Authentication Types
330+
### Supported Authentication Type
331331

332332
**Service Tokens** (Machine-to-machine):
333333

334334
- Used for automated uploads from scripts/applications
335335
- Client ID and secret passed via headers
336336
- Validated against `authorized_client_ids` in KV
337337

338-
**IdP Users** (Browser-based):
339-
340-
- Users authenticated via identity providers (Google, GitHub, etc.)
341-
- Email address extracted from JWT
342-
- Can be added to `authorized_client_ids` for authorization
343-
344338
### Local Development
345339

346340
Local development uses an authentication bypass that only activates when `CF_ACCESS_TEAM_DOMAIN=dev`:
@@ -503,7 +497,7 @@ The application deploys as a single Cloudflare Worker with:
503497
- **Cloudflare KV**: Stores all configuration (global, user, authorized client IDs)
504498
- **Cloudflare D1**: Stores image metadata with indexed queries
505499
- **Cloudflare Images**: Stores and delivers photo files
506-
- **Cloudflare Access**: Authenticates all `/admin/*` requests with service tokens or IdP users
500+
- **Cloudflare Access**: Authenticates all `/admin/*` requests with service tokens
507501
- **Environment variables**: `CF_ACCESS_TEAM_DOMAIN`, `CF_IMAGES_TOKEN` (via wrangler vars)
508502
- **Development secrets**: `DEV_USER`, `DEV_CLIENT_ID` (via .dev.vars, not deployed)
509503
- **Auto-generated routes**: Based on `config/app.jsonc`, one route per user domain

config/app-example.jsonc

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
{
2+
"global": {
3+
// Cloudflare Images delivery URL (find in Images dashboard)
4+
"imageBase": "https://imagedelivery.net/YOUR-ACCOUNT-HASH",
5+
// Default variant for image delivery
6+
"imageVariant": "default",
7+
// Username to use for localhost development
8+
"devUser": "yourname"
9+
},
10+
"wrangler": {
11+
// Cloudflare Worker name
12+
"name": "photochron",
13+
// Workers compatibility date
14+
"compatibility_date": "2024-10-04",
15+
"vars": {
16+
// Cloudflare account ID (find in dashboard URL or Overview page)
17+
"CF_ACCOUNT_ID": "your-cloudflare-account-id",
18+
// Cloudflare Access team domain (e.g., https://yourteam.cloudflareaccess.com)
19+
"CF_ACCESS_TEAM_DOMAIN": "https://yourteam.cloudflareaccess.com"
20+
},
21+
"kv_namespaces": [
22+
{
23+
"binding": "PCHRON_KV",
24+
// Create with: wrangler kv namespace create PCHRON_KV
25+
"id": "your-kv-namespace-id"
26+
}
27+
],
28+
"d1_databases": [
29+
{
30+
"binding": "PCHRON_DB",
31+
"database_name": "photochron",
32+
// Create with: wrangler d1 create photochron
33+
"database_id": "your-d1-database-id"
34+
}
35+
]
36+
},
37+
"users": {
38+
// Username (used in URL path extraction)
39+
"alice": {
40+
// Domain for this user's gallery
41+
"domain": "alice.example.com",
42+
"profile": {
43+
// Display name
44+
"name": "alice"
45+
},
46+
"avatar": {
47+
// Cloudflare Images ID for user avatar
48+
"id": "00000000-0000-0000-0000-000000000000",
49+
"variant": "default"
50+
},
51+
// Service token client IDs allowed to upload/delete for this user
52+
"authorized_client_ids": ["your-service-token-client-id.access"]
53+
},
54+
"bob": {
55+
"domain": "bob.example.com",
56+
"profile": {
57+
"name": "bob"
58+
},
59+
"avatar": {
60+
"id": "11111111-1111-1111-1111-111111111111",
61+
"variant": "default"
62+
},
63+
"authorized_client_ids": ["another-service-token-client-id.access"]
64+
}
65+
}
66+
}

package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,22 @@
2727
"devDependencies": {
2828
"@cloudflare/workers-types": "^4.20251011.0",
2929
"@eslint/compat": "^1.2.5",
30-
"@eslint/js": "^9.37.0",
30+
"@eslint/js": "^9.38.0",
3131
"@sveltejs/adapter-cloudflare": "^7.2.4",
32-
"@sveltejs/kit": "^2.47.0",
32+
"@sveltejs/kit": "^2.47.1",
3333
"@sveltejs/vite-plugin-svelte": "^6.2.1",
3434
"@tailwindcss/vite": "^4.1.14",
35-
"@types/node": "^24.8.0",
35+
"@types/node": "^24.8.1",
3636
"@vitest/browser": "^3.2.3",
37-
"eslint": "^9.37.0",
37+
"eslint": "^9.38.0",
3838
"eslint-config-prettier": "^10.1.8",
39-
"eslint-plugin-svelte": "^3.12.4",
39+
"eslint-plugin-svelte": "^3.12.5",
4040
"globals": "^16.4.0",
4141
"jsonc-parser": "^3.3.1",
4242
"prettier": "^3.6.2",
4343
"prettier-plugin-svelte": "^3.4.0",
44-
"prettier-plugin-tailwindcss": "^0.7.0",
45-
"svelte": "^5.40.1",
44+
"prettier-plugin-tailwindcss": "^0.7.1",
45+
"svelte": "^5.41.0",
4646
"svelte-check": "^4.3.3",
4747
"tailwindcss": "^4.1.14",
4848
"tsx": "^4.20.6",

0 commit comments

Comments
 (0)