openclaw_gateway/auth/
config.rs1use std::time::Duration;
4
5use serde::{Deserialize, Serialize};
6
7const DEFAULT_TOKEN_EXPIRY_HOURS: u64 = 24;
9const DEFAULT_REFRESH_EXPIRY_DAYS: u64 = 7;
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct AuthConfig {
15 #[serde(default = "default_enabled")]
17 pub enabled: bool,
18
19 #[serde(default)]
21 pub jwt_secret: Option<String>,
22
23 #[serde(default = "default_token_expiry")]
25 pub token_expiry_hours: u64,
26
27 #[serde(default = "default_refresh_expiry")]
29 pub refresh_expiry_days: u64,
30
31 #[serde(default = "default_true")]
33 pub require_auth_for_rpc: bool,
34
35 #[serde(default = "default_true")]
37 pub require_auth_for_ws: bool,
38
39 #[serde(default = "default_public_methods")]
41 pub public_methods: Vec<String>,
42}
43
44const fn default_enabled() -> bool {
45 true
46}
47
48const fn default_true() -> bool {
49 true
50}
51
52const fn default_token_expiry() -> u64 {
53 DEFAULT_TOKEN_EXPIRY_HOURS
54}
55
56const fn default_refresh_expiry() -> u64 {
57 DEFAULT_REFRESH_EXPIRY_DAYS
58}
59
60fn default_public_methods() -> Vec<String> {
61 vec![
62 "auth.login".to_string(),
63 "setup.status".to_string(),
64 "setup.init".to_string(),
65 "system.health".to_string(),
66 "system.version".to_string(),
67 ]
68}
69
70impl Default for AuthConfig {
71 fn default() -> Self {
72 Self {
73 enabled: default_enabled(),
74 jwt_secret: None,
75 token_expiry_hours: default_token_expiry(),
76 refresh_expiry_days: default_refresh_expiry(),
77 require_auth_for_rpc: default_true(),
78 require_auth_for_ws: default_true(),
79 public_methods: default_public_methods(),
80 }
81 }
82}
83
84impl AuthConfig {
85 #[must_use]
87 pub fn builder() -> AuthConfigBuilder {
88 AuthConfigBuilder::default()
89 }
90
91 #[must_use]
93 pub const fn token_expiry(&self) -> Duration {
94 Duration::from_secs(self.token_expiry_hours * 3600)
95 }
96
97 #[must_use]
99 pub const fn refresh_expiry(&self) -> Duration {
100 Duration::from_secs(self.refresh_expiry_days * 24 * 3600)
101 }
102
103 #[must_use]
105 pub fn is_public_method(&self, method: &str) -> bool {
106 self.public_methods.iter().any(|m| m == method)
107 }
108
109 #[must_use]
111 pub fn with_env_overrides(mut self) -> Self {
112 if let Ok(secret) = std::env::var("OPENCLAW_JWT_SECRET") {
113 self.jwt_secret = Some(secret);
114 }
115
116 if std::env::var("OPENCLAW_AUTH_DISABLED")
117 .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
118 .unwrap_or(false)
119 {
120 self.enabled = false;
121 }
122
123 self
124 }
125}
126
127#[derive(Debug, Default)]
129pub struct AuthConfigBuilder {
130 config: AuthConfig,
131}
132
133impl AuthConfigBuilder {
134 #[must_use]
136 pub const fn enabled(mut self, enabled: bool) -> Self {
137 self.config.enabled = enabled;
138 self
139 }
140
141 #[must_use]
143 pub fn jwt_secret(mut self, secret: impl Into<String>) -> Self {
144 self.config.jwt_secret = Some(secret.into());
145 self
146 }
147
148 #[must_use]
150 pub const fn token_expiry_hours(mut self, hours: u64) -> Self {
151 self.config.token_expiry_hours = hours;
152 self
153 }
154
155 #[must_use]
157 pub const fn refresh_expiry_days(mut self, days: u64) -> Self {
158 self.config.refresh_expiry_days = days;
159 self
160 }
161
162 #[must_use]
164 pub const fn require_auth_for_rpc(mut self, required: bool) -> Self {
165 self.config.require_auth_for_rpc = required;
166 self
167 }
168
169 #[must_use]
171 pub const fn require_auth_for_ws(mut self, required: bool) -> Self {
172 self.config.require_auth_for_ws = required;
173 self
174 }
175
176 #[must_use]
178 pub fn public_method(mut self, method: impl Into<String>) -> Self {
179 self.config.public_methods.push(method.into());
180 self
181 }
182
183 #[must_use]
185 pub fn build(self) -> AuthConfig {
186 self.config
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 #[test]
195 fn test_default_config() {
196 let config = AuthConfig::default();
197 assert!(config.enabled);
198 assert!(config.jwt_secret.is_none());
199 assert_eq!(config.token_expiry_hours, 24);
200 assert_eq!(config.refresh_expiry_days, 7);
201 }
202
203 #[test]
204 fn test_public_methods() {
205 let config = AuthConfig::default();
206 assert!(config.is_public_method("auth.login"));
207 assert!(config.is_public_method("system.health"));
208 assert!(!config.is_public_method("session.create"));
209 }
210
211 #[test]
212 fn test_builder() {
213 let config = AuthConfig::builder()
214 .enabled(false)
215 .token_expiry_hours(12)
216 .build();
217
218 assert!(!config.enabled);
219 assert_eq!(config.token_expiry_hours, 12);
220 }
221
222 #[test]
223 fn test_durations() {
224 let config = AuthConfig::default();
225 assert_eq!(config.token_expiry(), Duration::from_secs(24 * 3600));
226 assert_eq!(config.refresh_expiry(), Duration::from_secs(7 * 24 * 3600));
227 }
228}