docs: add design doc for config download token
This commit is contained in:
77
docs/plans/2026-03-01-config-download-token-design.md
Normal file
77
docs/plans/2026-03-01-config-download-token-design.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# Config Download Token Design
|
||||||
|
|
||||||
|
**Date:** 2026-03-01
|
||||||
|
**Status:** Approved
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
Downloading a generated config ZIP from the command line requires extracting an httpOnly OIDC
|
||||||
|
session cookie from the browser, which is fragile and not scriptable. Users need a stable,
|
||||||
|
per-config token they can embed in automation scripts.
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
|
||||||
|
Add a `download_token` field to each `Config`. The existing generate endpoint accepts this token
|
||||||
|
in the POST body as an alternative to OIDC cookie auth, allowing unauthenticated-but-authorized
|
||||||
|
downloads.
|
||||||
|
|
||||||
|
## Data Model
|
||||||
|
|
||||||
|
- Add `download_token: str` column to `configs` table.
|
||||||
|
- Value: `secrets.token_urlsafe(32)` — 32 bytes of URL-safe random data (43 characters).
|
||||||
|
- Generated automatically on config creation.
|
||||||
|
- Stored as plaintext (the token is low-value; it only grants read access to a single config's
|
||||||
|
generated output).
|
||||||
|
- Alembic migration backfills existing configs with auto-generated tokens.
|
||||||
|
|
||||||
|
## API Changes
|
||||||
|
|
||||||
|
### Modified: `POST /api/configs/{id}/generate`
|
||||||
|
|
||||||
|
Accepts an optional JSON body:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "token": "..." }
|
||||||
|
```
|
||||||
|
|
||||||
|
Auth logic (either is sufficient):
|
||||||
|
1. Valid OIDC `access_token` cookie + `owner_id` match
|
||||||
|
2. `token` in body matches `config.download_token` (no owner filter needed)
|
||||||
|
|
||||||
|
Error responses:
|
||||||
|
- No cookie and no/wrong token → 401
|
||||||
|
- Valid token but wrong config ID → 404
|
||||||
|
|
||||||
|
Example curl usage:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST "https://host/api/configs/1/generate?format=zip" \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"token": "abc..."}' -o shorewall.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
### New: `POST /api/configs/{id}/regenerate-token`
|
||||||
|
|
||||||
|
- OIDC-protected, owner-only.
|
||||||
|
- Generates a new `secrets.token_urlsafe(32)`, saves it to the config, returns it.
|
||||||
|
- Response: `{ "download_token": "..." }`
|
||||||
|
- Non-owner → 403.
|
||||||
|
|
||||||
|
## Schema Changes
|
||||||
|
|
||||||
|
- `ConfigOut` gains `download_token: str`.
|
||||||
|
- New `GenerateRequest` Pydantic model: `token: Optional[str] = None`.
|
||||||
|
|
||||||
|
## Frontend Changes
|
||||||
|
|
||||||
|
On the Config Detail page header area (above the tabs):
|
||||||
|
|
||||||
|
- Read-only text field showing `download_token`.
|
||||||
|
- Copy-to-clipboard icon button.
|
||||||
|
- "Regenerate" button that calls the new endpoint and updates the displayed value.
|
||||||
|
|
||||||
|
## Migration
|
||||||
|
|
||||||
|
New Alembic migration `0012_config_add_download_token.py`:
|
||||||
|
- Add `download_token` column with `server_default=''`.
|
||||||
|
- Backfill with `secrets.token_urlsafe(32)` for all existing rows via a data migration step.
|
||||||
Reference in New Issue
Block a user