proto(woo): blocking setup gate; require X-Woo-* on Woo HTTP routes

Full-screen WooSetupGate on App mount when this browser lacks device WP
user + app password. Save + Test Woo dismisses only after a successful
test. Publish/test-woo auth failures reopen the gate.

Server: client_for_request requires X-Woo-* on publish, taxonomy sync,
and test-woo (401 woo_device_creds_required); health probe keeps
Settings. Optional SLAB_WOO_ALLOW_SETTINGS_FALLBACK for legacy tests.

See PROTOTYPE.md.
This commit is contained in:
grokbot_ty
2026-09-26 19:20:16 -07:00
parent 2f6335ec26
commit 2f9b8ebd02
16 changed files with 600 additions and 58 deletions
+24 -17
View File
@@ -13,44 +13,51 @@ No full auth, ACL, or publish mutex in this prototype.
| Layer | What |
| --- | --- |
| Browser | `localStorage` keys `slabuploader.woo.device.username` / `…appPassword` |
| HTTP | Headers `X-Woo-Username` + `X-Woo-App-Password` on Woo-touching calls |
| Server | `client_for_request(conn, headers)` → override if both headers non-blank, else Settings singleton |
| Settings | Shop-default `woo_wp_username` / encrypted `woo_app_password` — **never** updated by the device-creds path |
| Gate | Full-screen `WooSetupGate` on App mount when this browser lacks device creds |
| HTTP | Headers `X-Woo-Username` + `X-Woo-App-Password` on Woo-touching calls (**required**) |
| Server | `client_for_request(conn, headers)` → override only; **no Settings fallback** on those routes |
| Settings | Shop-default `woo_wp_username` / encrypted `woo_app_password` — used by **health probe** only; **never** updated by the device-creds / gate path |
Woo-touching calls that attach headers:
Woo-touching calls that **require** headers (401 `woo_device_creds_required` if missing):
- `POST /api/v1/slabs/{id}/publish`
- `POST /api/v1/admin/taxonomy/sync`
- `POST /api/v1/settings/test-woo`
Inference / Call 2 / Settings PUT for inference keys are unchanged (shared Settings).
Optional test escape hatch: `SLAB_WOO_ALLOW_SETTINGS_FALLBACK=1` (prefer sending headers in tests).
## Setup gate
1. On App mount, if this browser has no device username + app password → blocking overlay.
2. Fields: device WP user + app password (required); Woo base URL display-only from Settings;
inference URL/key show configured / not set (masked) and may be entered → PUT Settings
for inference only.
3. Primary CTA: **Save device creds & Test Woo**. Gate dismisses only after Test Woo succeeds.
4. Returning browser with device creds → skip gate.
5. Publish (or Test Woo) Woo auth failure / missing headers → reopen gate.
## Flow (request → WooClient)
1. Frontend reads device creds from localStorage (`wooDeviceCreds.ts`).
2. `withWooHeaders()` merges `X-Woo-*` onto the fetch.
3. Backend `client_for_request` builds a **new** `WooClient` for that request only
(Basic auth from override plaintext, or decrypt Settings password).
(Basic auth from override plaintext). Missing headers → `WooDeviceCredsRequired`.
4. Publish / taxonomy / media use that client instance — no process-global
“current user”.
## UAT with two browsers
1. Build/run this branch locally (do **not** deploy over partner UAT :10890; never :10880).
1. Deploy this branch to the **proto** stack only (`slab-proto-woo-creds` on
`:10892` / `:10893`). Do **not** touch overnight `:10890`; never `:10880`.
2. In WordPress, create two application passwords for two WP users on the same store.
3. Browser A (or profile A): Settings → **This device’s Woo credentials** → enter
user A + app password → **Save on this device** → **Test WooCommerce connection**.
3. Browser A (or profile A): setup gate → enter user A + app password →
**Save device creds & Test Woo** → gate dismisses.
4. Browser B: same for user B (separate localStorage).
5. Confirm Test Woo succeeds for both, and that **Save Changes** on shop-default
fields was not used to store the other user’s password.
5. Confirm Test Woo succeeds for both, and that device passwords were not written
into shop-default Settings.
6. Publish different slabs from each browser (do not work the same slab concurrently).
7. Optional: clear device creds and confirm fallback to shop-default Settings.
## Intentional fallback
- Missing or partial `X-Woo-*` headers → shop-default Settings credentials.
- Device localStorage empty → same fallback.
- Health probe (`woo_reachable`) always uses shop-default Settings (no request).
7. Optional: clear device creds in Settings → reload → gate reappears.
## Risks left (prototype)