Skip to main content

modo/auth/session/
meta.rs

1//! Request metadata derived from HTTP headers for session tracking.
2//!
3//! Provides [`SessionMeta`], a snapshot of client information captured when a
4//! session is created, and the [`header_str`] utility for safe header extraction.
5
6use http::HeaderMap;
7
8use super::device::{parse_device_name, parse_device_type};
9use super::fingerprint::compute_fingerprint;
10
11/// Metadata derived from request headers at the time a session is created.
12///
13/// Stored alongside the session row and used for fingerprint validation on
14/// subsequent requests.
15#[derive(Debug, Clone)]
16pub struct SessionMeta {
17    /// Client IP address (from `ClientIp` or `ConnectInfo`).
18    pub ip_address: String,
19    /// Raw `User-Agent` header value.
20    pub user_agent: String,
21    /// Human-readable device name, e.g. `"Chrome on macOS"`.
22    pub device_name: String,
23    /// Device category: `"desktop"`, `"mobile"`, or `"tablet"`.
24    pub device_type: String,
25    /// SHA-256 fingerprint derived from user-agent, accept-language, and
26    /// accept-encoding headers.
27    pub fingerprint: String,
28}
29
30impl SessionMeta {
31    /// Build `SessionMeta` from parsed header strings.
32    pub fn from_headers(
33        ip_address: String,
34        user_agent: &str,
35        accept_language: &str,
36        accept_encoding: &str,
37    ) -> Self {
38        Self {
39            ip_address,
40            device_name: parse_device_name(user_agent),
41            device_type: parse_device_type(user_agent),
42            fingerprint: compute_fingerprint(user_agent, accept_language, accept_encoding),
43            user_agent: user_agent.to_string(),
44        }
45    }
46}
47
48/// Extract a header value as a string slice, returning `""` when absent or
49/// non-UTF-8.
50pub fn header_str<'a>(headers: &'a HeaderMap, name: &str) -> &'a str {
51    headers
52        .get(name)
53        .and_then(|v| v.to_str().ok())
54        .unwrap_or("")
55}