proto(woo): per-browser Woo creds via X-Woo-* request headers

Prototype routing so 2–3 owners can share one store URL with different WP
application passwords. Device creds live in localStorage; server uses
request override → Settings fallback and never writes overrides into the
Settings row. Inference stays on shared Settings.

See PROTOTYPE.md for UAT with two browsers and known LAN forge risk.
This commit is contained in:
grokbot_ty
2026-09-26 12:26:21 -07:00
parent fc40c66c44
commit 2f6335ec26
8 changed files with 419 additions and 31 deletions
+63
View File
@@ -0,0 +1,63 @@
# Prototype: per-user Woo credentials
Branch: `proto-per-user-woo-creds` (cut from `overnight-whole-app`).
## Goal
2–3 owners can share one SlabUploader install and one Woo store URL, each using
their own WordPress user + application password. Inference URL/key stay global.
No full auth, ACL, or publish mutex in this prototype.
## Credential scheme
| 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 |
Woo-touching calls that attach headers:
- `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).
## 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).
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).
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**.
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.
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).
## Risks left (prototype)
- **LAN can forge headers** — there is no app login; any client on the network can
send arbitrary `X-Woo-*` values. Treat as trusted LAN only.
- Device passwords sit in **localStorage** (XSS-readable). Acceptable for this
prototype only.
- No slab ownership ACL / taxonomy mutex / publish CAS — operators must not edit
the same slab at once.
- Taxonomy cache remains shared; a sync from either user refreshes the same rows.