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

2.3 KiB

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:

{ "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:

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.