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.
115 lines
4.4 KiB
Markdown
115 lines
4.4 KiB
Markdown
+++
|
|
title = "Implement auth code session storage — utility module and AuthModal pre-fill integration"
|
|
priority = 7
|
|
status = "done"
|
|
ticket_type = "task"
|
|
dependencies = []
|
|
+++
|
|
|
|
<context>
|
|
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.
|
|
</context>
|
|
|
|
<goal>
|
|
**Part 1 — Storage utility (`src/bin/ui/storage.rs`)**
|
|
|
|
Create a module with three public functions that wrap the browser's `sessionStorage` API:
|
|
|
|
```rust
|
|
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.
|
|
|
|
```rust
|
|
#[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("e.id)` and pass it as
|
|
`initial_value` to `AuthModal`.
|
|
- After a successful **edit** (POST /api/quotes/:id returns 200): call
|
|
`storage::set_auth_code("e.id, &submitted_code)`.
|
|
- After a successful **delete** (DELETE /api/quotes/:id returns 204): call
|
|
`storage::clear_auth_code("e.id)`.
|
|
- If the API returns 403 (wrong code): do NOT store the code; clear any existing stored value
|
|
with `storage::clear_auth_code("e.id)` so a stale code is not re-offered.
|
|
</goal>
|
|
|
|
<constraints>
|
|
- 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.
|
|
</constraints>
|
|
|
|
<validation>
|
|
```sh
|
|
trunk build
|
|
```
|
|
</validation>
|
|
|
|
<commit>
|
|
`feat(quotesdb): implement auth code session storage utility and AuthModal pre-fill`
|
|
</commit>
|
|
|
|
<domain>quotesdb/ui</domain>
|