2c6062a53e
The /web/ prefix predated the /api/ split; today it just clutters every URL
without disambiguating anything. All 16 browser routes move to the apex
(/cases, /login, /magic, /events, /audio/...). The 6 /api/* routes are
unchanged. A new /->>/cases redirect closes the apex 404.
The open-redirect guard in magic.rs and case_actions.rs flips from a
positive whitelist (starts_with("/web/")) to a deny-list: same-origin path,
not protocol-relative, not under /api/, no \. The /api/ exclusion is now
load-bearing and covered by tests.
Pre-production: no transition redirects.
28 lines
902 B
Rust
28 lines
902 B
Rust
//! Short-lived, one-time tokens that turn a desktop API-key auth into a
|
|
//! browser session without an interactive password login.
|
|
//!
|
|
//! Lifecycle: the desktop client calls `POST /api/auth/magic-link` (API-key
|
|
//! authenticated), gets a token, opens the browser at `/magic?token=…`.
|
|
//! The web handler removes the token (one-time-use) and installs a regular
|
|
//! `WebSession`. TTL is intentionally short — the token only needs to
|
|
//! survive the click → browser-launch → first-request round trip.
|
|
|
|
use std::collections::HashMap;
|
|
use std::sync::Arc;
|
|
use std::time::Instant;
|
|
|
|
use tokio::sync::RwLock;
|
|
|
|
pub struct PendingMagicLink {
|
|
pub slug: String,
|
|
pub role: String,
|
|
pub return_to: String,
|
|
pub expires_at: Instant,
|
|
}
|
|
|
|
pub type MagicLinkStore = Arc<RwLock<HashMap<String, PendingMagicLink>>>;
|
|
|
|
pub fn new_store() -> MagicLinkStore {
|
|
Arc::new(RwLock::new(HashMap::new()))
|
|
}
|