dioxus_cookie/types.rs
1use std::time::Duration;
2
3/// Cross-site request cookie policy (SameSite attribute).
4///
5/// Controls when cookies are sent with cross-site requests, providing
6/// protection against CSRF attacks.
7///
8/// # Variants
9///
10/// - `Strict` — Cookie only sent with same-site requests
11/// - `Lax` — Cookie sent with same-site requests and top-level navigation (default)
12/// - `None` — Cookie sent with all requests (requires `Secure` flag)
13#[derive(Clone, Debug, Default)]
14pub enum SameSite {
15 /// Cookie only sent with same-site requests.
16 ///
17 /// Most restrictive. Use for sensitive operations like banking or account changes.
18 Strict,
19 /// Cookie sent with same-site requests and top-level navigation.
20 ///
21 /// Balanced protection. Good for most session cookies.
22 #[default]
23 Lax,
24 /// Cookie sent with all requests, including cross-site.
25 ///
26 /// Requires `Secure` flag. Use only when cross-site access is necessary
27 /// (e.g., embedded widgets, third-party integrations).
28 None,
29}
30
31impl SameSite {
32 /// Returns the string representation for use in cookie headers.
33 pub fn as_str(&self) -> &'static str {
34 match self {
35 SameSite::Strict => "Strict",
36 SameSite::Lax => "Lax",
37 SameSite::None => "None",
38 }
39 }
40}
41
42/// Configuration options for setting cookies.
43///
44/// The default options prioritize security:
45/// - `http_only: true` — prevents JavaScript access
46/// - `secure: true` — HTTPS only
47/// - `same_site: Lax` — CSRF protection
48/// - `path: "/"` — available to all routes
49///
50/// # Example
51///
52/// ```rust,ignore
53/// use dioxus_cookie::{CookieOptions, SameSite};
54/// use std::time::Duration;
55///
56/// let options = CookieOptions {
57/// max_age: Some(Duration::from_secs(86400 * 7)), // 7 days
58/// http_only: true,
59/// secure: true,
60/// same_site: SameSite::Strict,
61/// path: "/".to_string(),
62/// };
63/// ```
64#[derive(Clone, Debug)]
65pub struct CookieOptions {
66 /// Cookie lifetime. `None` = session cookie (deleted when browser closes).
67 pub max_age: Option<Duration>,
68 /// If `true`, cookie is inaccessible to JavaScript/WASM. Default: `true`.
69 ///
70 /// Always use `true` for session tokens to prevent XSS attacks.
71 pub http_only: bool,
72 /// If `true`, cookie is only sent over HTTPS. Default: `true`.
73 pub secure: bool,
74 /// Cross-site request policy. Default: [`SameSite::Lax`].
75 pub same_site: SameSite,
76 /// URL path scope for the cookie. Default: `"/"`.
77 pub path: String,
78}
79
80impl Default for CookieOptions {
81 fn default() -> Self {
82 Self {
83 max_age: None,
84 http_only: true,
85 secure: true,
86 same_site: SameSite::Lax,
87 path: "/".to_string(),
88 }
89 }
90}
91
92impl CookieOptions {
93 /// Builds a `Set-Cookie` header string from the options.
94 ///
95 /// The value is URL-encoded automatically.
96 pub fn build_header(&self, name: &str, value: &str) -> String {
97 let mut s = format!("{}={}", name, urlencoding::encode(value));
98
99 if self.http_only {
100 s.push_str("; HttpOnly");
101 }
102 if self.secure {
103 s.push_str("; Secure");
104 }
105 s.push_str("; SameSite=");
106 s.push_str(self.same_site.as_str());
107 s.push_str("; Path=");
108 s.push_str(&self.path);
109
110 if let Some(max_age) = self.max_age {
111 s.push_str("; Max-Age=");
112 s.push_str(&max_age.as_secs().to_string());
113 }
114
115 s
116 }
117}
118
119/// Error type for cookie operations.
120///
121/// On the server, this automatically converts to `ServerFnError`
122/// for seamless error propagation from `#[server]` functions.
123#[derive(Debug, Clone, thiserror::Error)]
124#[error("CookieError: {message}")]
125pub struct CookieError {
126 /// Human-readable error description.
127 pub message: String,
128}
129
130impl CookieError {
131 /// Creates a new cookie error with the given message.
132 pub fn new(message: impl Into<String>) -> Self {
133 Self {
134 message: message.into(),
135 }
136 }
137}
138
139#[cfg(feature = "server")]
140impl From<CookieError> for dioxus::prelude::ServerFnError {
141 fn from(err: CookieError) -> Self {
142 dioxus::prelude::ServerFnError::new(err.message)
143 }
144}