You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
vibed/quotesdb/.beans/quotesdb-5dda--implement-au...

4.5 KiB

title status type priority created_at updated_at
Implement auth code session storage — utility module and AuthModal pre-fill integration completed task high 2026-03-10T23:32:07Z 2026-03-10T23:32:07Z
Resolved from TRIAGE ticket 0bc655. The auth code (4-word passphrase) that authorises edit and delete operations must be available to the UI without forcing the user to re-enter it on every interaction within a browsing session.

Chosen strategy: session storage per quote ID. The code is stored in the browser's sessionStorage under the key auth_code_{id} when first entered. It is automatically cleared when the tab closes. No explicit clear-on-delete is required (session storage is short-lived by design), but it is good practice and should be included.

Options considered:

  • localStorage: ruled out — indefinite persistence is unnecessary; the app tells users to store the code externally anyway, and localStorage has a wider XSS exposure window.
  • Component state only: ruled out — code is lost on any page navigation or reload, making the edit/delete flow unusable in practice.
**Part 1 — Storage utility (`src/bin/ui/storage.rs`)**

Create a module with three public functions that wrap the browser's sessionStorage API:

use web_sys::window;

/// Retrieve the stored auth code for a given quote ID, if any.
pub fn get_auth_code(quote_id: &str) -> Option<String> {
    let storage = window()?.session_storage().ok()??;
    storage.get_item(&format!("auth_code_{quote_id}")).ok()?
}

/// Persist the auth code for a quote ID in sessionStorage.
pub fn set_auth_code(quote_id: &str, code: &str) {
    if let Some(Ok(storage)) = window().map(|w| w.session_storage()) {
        if let Some(storage) = storage {
            let _ = storage.set_item(&format!("auth_code_{quote_id}"), code);
        }
    }
}

/// Remove the auth code for a quote ID from sessionStorage (call after DELETE).
pub fn clear_auth_code(quote_id: &str) {
    if let Some(Ok(storage)) = window().map(|w| w.session_storage()) {
        if let Some(storage) = storage {
            let _ = storage.remove_item(&format!("auth_code_{quote_id}"));
        }
    }
}

Expose this module from the UI binary root: add mod storage; to src/bin/ui/main.rs.

Part 2 — AuthModal pre-fill

Update the AuthModal component (ticket f850c6) to accept an initial_value: Option<String> prop. Pre-populate the <input> value from this prop when the modal opens. The parent component is responsible for reading from storage and passing the value in.

#[derive(Properties, PartialEq)]
pub struct AuthModalProps {
    pub on_submit: Callback<String>,
    pub on_cancel: Callback<()>,
    pub initial_value: Option<String>,  // pre-fill if auth code is already stored
}

Part 3 — SingleQuotePage integration

In the SingleQuotePage (or whichever component renders edit/delete for a quote), integrate storage around the AuthModal:

  • Before opening the modal: read storage::get_auth_code(&quote.id) and pass it as initial_value to AuthModal.
  • After a successful edit (POST /api/quotes/:id returns 200): call storage::set_auth_code(&quote.id, &submitted_code).
  • After a successful delete (DELETE /api/quotes/:id returns 204): call storage::clear_auth_code(&quote.id).
  • If the API returns 403 (wrong code): do NOT store the code; clear any existing stored value with storage::clear_auth_code(&quote.id) so a stale code is not re-offered.
- The storage utility must compile only for `wasm32-unknown-unknown` — `web_sys::window()` is not available on the host target. Gate the module under `#[cfg(target_arch = "wasm32")]` or ensure it is only imported by the `ui` binary, which is always compiled for wasm32. - `web_sys` must be available with the `Window`, `Storage` features — confirm these are included in the `web_sys` dependency in `Cargo.toml` (ticket 93515e covers UI Cargo.toml setup). - Do NOT use `gloo-storage` — it wraps localStorage by default and the API difference matters. Use `web_sys` directly as shown above. - The key pattern is `auth_code_{quote_id}` (underscore separator, not slash or dot). - Session storage is tab-scoped: no cross-tab contamination is possible — no additional scoping by domain or user is needed. ```sh trunk build ``` `feat(quotesdb): implement auth code session storage utility and AuthModal pre-fill`

quotesdb/ui