use std::time::Duration;
use serde::{Deserialize, Serialize};
const DEFAULT_TOKEN_EXPIRY_HOURS: u64 = 24;
const DEFAULT_REFRESH_EXPIRY_DAYS: u64 = 7;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthConfig {
#[serde(default = "default_enabled")]
pub enabled: bool,
#[serde(default)]
pub jwt_secret: Option<String>,
#[serde(default = "default_token_expiry")]
pub token_expiry_hours: u64,
#[serde(default = "default_refresh_expiry")]
pub refresh_expiry_days: u64,
#[serde(default = "default_true")]
pub require_auth_for_rpc: bool,
#[serde(default = "default_true")]
pub require_auth_for_ws: bool,
#[serde(default = "default_public_methods")]
pub public_methods: Vec<String>,
}
const fn default_enabled() -> bool {
true
}
const fn default_true() -> bool {
true
}
const fn default_token_expiry() -> u64 {
DEFAULT_TOKEN_EXPIRY_HOURS
}
const fn default_refresh_expiry() -> u64 {
DEFAULT_REFRESH_EXPIRY_DAYS
}
fn default_public_methods() -> Vec<String> {
vec![
"auth.login".to_string(),
"setup.status".to_string(),
"setup.init".to_string(),
"system.health".to_string(),
"system.version".to_string(),
]
}
impl Default for AuthConfig {
fn default() -> Self {
Self {
enabled: default_enabled(),
jwt_secret: None,
token_expiry_hours: default_token_expiry(),
refresh_expiry_days: default_refresh_expiry(),
require_auth_for_rpc: default_true(),
require_auth_for_ws: default_true(),
public_methods: default_public_methods(),
}
}
}
impl AuthConfig {
#[must_use]
pub fn builder() -> AuthConfigBuilder {
AuthConfigBuilder::default()
}
#[must_use]
pub const fn token_expiry(&self) -> Duration {
Duration::from_secs(self.token_expiry_hours * 3600)
}
#[must_use]
pub const fn refresh_expiry(&self) -> Duration {
Duration::from_secs(self.refresh_expiry_days * 24 * 3600)
}
#[must_use]
pub fn is_public_method(&self, method: &str) -> bool {
self.public_methods.iter().any(|m| m == method)
}
#[must_use]
pub fn with_env_overrides(mut self) -> Self {
if let Ok(secret) = std::env::var("OPENCLAW_JWT_SECRET") {
self.jwt_secret = Some(secret);
}
if std::env::var("OPENCLAW_AUTH_DISABLED")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
{
self.enabled = false;
}
self
}
}
#[derive(Debug, Default)]
pub struct AuthConfigBuilder {
config: AuthConfig,
}
impl AuthConfigBuilder {
#[must_use]
pub const fn enabled(mut self, enabled: bool) -> Self {
self.config.enabled = enabled;
self
}
#[must_use]
pub fn jwt_secret(mut self, secret: impl Into<String>) -> Self {
self.config.jwt_secret = Some(secret.into());
self
}
#[must_use]
pub const fn token_expiry_hours(mut self, hours: u64) -> Self {
self.config.token_expiry_hours = hours;
self
}
#[must_use]
pub const fn refresh_expiry_days(mut self, days: u64) -> Self {
self.config.refresh_expiry_days = days;
self
}
#[must_use]
pub const fn require_auth_for_rpc(mut self, required: bool) -> Self {
self.config.require_auth_for_rpc = required;
self
}
#[must_use]
pub const fn require_auth_for_ws(mut self, required: bool) -> Self {
self.config.require_auth_for_ws = required;
self
}
#[must_use]
pub fn public_method(mut self, method: impl Into<String>) -> Self {
self.config.public_methods.push(method.into());
self
}
#[must_use]
pub fn build(self) -> AuthConfig {
self.config
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = AuthConfig::default();
assert!(config.enabled);
assert!(config.jwt_secret.is_none());
assert_eq!(config.token_expiry_hours, 24);
assert_eq!(config.refresh_expiry_days, 7);
}
#[test]
fn test_public_methods() {
let config = AuthConfig::default();
assert!(config.is_public_method("auth.login"));
assert!(config.is_public_method("system.health"));
assert!(!config.is_public_method("session.create"));
}
#[test]
fn test_builder() {
let config = AuthConfig::builder()
.enabled(false)
.token_expiry_hours(12)
.build();
assert!(!config.enabled);
assert_eq!(config.token_expiry_hours, 12);
}
#[test]
fn test_durations() {
let config = AuthConfig::default();
assert_eq!(config.token_expiry(), Duration::from_secs(24 * 3600));
assert_eq!(config.refresh_expiry(), Duration::from_secs(7 * 24 * 3600));
}
}