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