Files
shorefront/docs/plans/2026-03-01-config-download-token-design.md

78 lines
2.3 KiB
Markdown

# 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.