feat: require a write token or session on mutating portal API routes
- Adds IPFS_WRITE_TOKEN to lib/config.ts - Default-deny per route (not per method): the public explorer issues reads as POST - Existing portal session JWT remains valid, so the logged-in UI keeps working - Public gateway reads (/ipfs/<cid>) are unchanged
This commit is contained in:
@@ -222,6 +222,58 @@ Catch-all API proxy. Routes:
|
||||
|
||||
---
|
||||
|
||||
## Write authentication
|
||||
|
||||
Muterende `/api/*`-endpoints eisen een token; lees-endpoints blijven publiek.
|
||||
Ingang: `authorizeWrite()` in `src/app/api/[...path]/write-auth.ts`, aangeroepen
|
||||
vanuit `POST` en `DELETE` in `route.ts`. De waarde komt uit
|
||||
`ipfsWriteToken()` in `src/lib/config.ts` (env `IPFS_WRITE_TOKEN`).
|
||||
|
||||
| Endpoint | Methode | Write-gate |
|
||||
|---|---|---|
|
||||
| `/api/add` | POST | ✅ |
|
||||
| `/api/files/upload` | POST | ✅ |
|
||||
| `/api/files/<cid>` | DELETE | ✅ |
|
||||
| `/api/pins` | POST | ✅ |
|
||||
| `/api/pins/<cid>` | DELETE | ✅ |
|
||||
| `/api/ipns/publish`, `/api/ipns/keys/gen/<naam>` | POST | ✅ |
|
||||
| `/api/users/<naam>` | DELETE | ✅ |
|
||||
| `/api/explorer/{ls,cat,stat,resolve}/<cid>` | POST | ❌ lees-only |
|
||||
| `/api/pins`, `/api/files`, `/api/explorer/*`, `/api/health` | GET | ❌ publiek |
|
||||
| `/ipfs/<cid>` (gateway) | GET | ❌ publiek |
|
||||
| `/api/users` (registratie) | POST | ❌ open — eigen auth van Python-backend |
|
||||
|
||||
**Toegang tot een mutatie — één van beide volstaat:**
|
||||
|
||||
1. `Authorization: Bearer $IPFS_WRITE_TOKEN` (MAOS → portal, server-to-server).
|
||||
Vergelijking is constant-time via SHA-256 digests (`crypto.timingSafeEqual`).
|
||||
2. De bestaande portalsessie-JWT cookie `ipfs-portal-session` — identieke
|
||||
verificatie als `src/middleware.ts`. Zónder dit pad zouden de eigen
|
||||
upload-/pin-pagina's van de portal kapotgaan.
|
||||
|
||||
Geen van beide → `401` (geen `Authorization`-header) of `403` (header aanwezig,
|
||||
verkeerde waarde), met `WWW-Authenticate: Bearer`.
|
||||
|
||||
**`IPFS_WRITE_TOKEN` niet geconfigureerd** → writes blijven **open** met een
|
||||
eenmalige luide waarschuwing in de log. `src/lib/config.ts` heeft geen
|
||||
"required in production"-patroon (alleen defaults, in tegenstelling tot
|
||||
`JWT_SECRET` in `auth-server.ts`), en een harde fail zou elke nog niet
|
||||
geprovisioneerde deploy direct breken — vandaar bewust fail-open + waarschuwing.
|
||||
|
||||
**Besluit — publieke lees-endpoints**: `GET /api/pins` en `GET /api/files`
|
||||
blijven anoniem toegankelijk. Ze lekken alleen metadata (CID's, namen, maten);
|
||||
content is sowieso publiek via de gateway. Ze achter de sessie zetten zou de
|
||||
publieke explorer (pin-badges) én MAOS' pin-status/inventory-reads breken — die
|
||||
sturen vandaag geen `Authorization`-header op GET. Zodra MAOS ook GET's met
|
||||
Bearer verstuurt kan dit alsnog achter de gate.
|
||||
|
||||
**Rest-risico**: `POST /api/users` (registratie én admin-aanmaak, zelfde
|
||||
endpoint) blijft open — een registrant heeft noch token noch sessie. De
|
||||
Python-backend is daar de autoriteit (`X-Admin-Key` + doorgestuurd
|
||||
`X-Session-Token`).
|
||||
|
||||
---
|
||||
|
||||
## Smart Contract (Chain 270 — zkSync Local)
|
||||
|
||||
**Address**: `0xCBc6b8aeea129c206F4836799621C833Bf8B9BDe`
|
||||
|
||||
Reference in New Issue
Block a user