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}