nblm_core/auth/oauth/
config.rs

1use super::{OAuthConfig, OAuthError, Result};
2
3/// OAuth client configuration loaded from the environment.
4#[derive(Debug, Clone)]
5pub struct OAuthClientConfig {
6    pub client_id: String,
7    pub client_secret: Option<String>,
8    pub redirect_uri: String,
9    pub audience: Option<String>,
10}
11
12impl OAuthClientConfig {
13    /// Load client configuration from environment variables.
14    pub fn from_env() -> Result<Self> {
15        let client_id = std::env::var("NBLM_OAUTH_CLIENT_ID")
16            .map_err(|_| OAuthError::MissingEnvVar("NBLM_OAUTH_CLIENT_ID"))?;
17
18        let client_secret = std::env::var("NBLM_OAUTH_CLIENT_SECRET").ok();
19        let redirect_uri = std::env::var("NBLM_OAUTH_REDIRECT_URI")
20            .unwrap_or_else(|_| OAuthConfig::DEFAULT_REDIRECT_URI.to_string());
21        let audience = std::env::var("NBLM_OAUTH_AUDIENCE").ok();
22
23        Ok(Self {
24            client_id,
25            client_secret,
26            redirect_uri,
27            audience,
28        })
29    }
30
31    /// Convert this configuration into a complete `OAuthConfig` value.
32    pub fn into_oauth_config(self) -> OAuthConfig {
33        OAuthConfig {
34            auth_endpoint: OAuthConfig::AUTH_ENDPOINT.to_string(),
35            token_endpoint: OAuthConfig::TOKEN_ENDPOINT.to_string(),
36            client_id: self.client_id,
37            client_secret: self.client_secret,
38            redirect_uri: self.redirect_uri,
39            scopes: vec![
40                OAuthConfig::SCOPE_CLOUD_PLATFORM.to_string(),
41                OAuthConfig::SCOPE_DRIVE_FILE.to_string(),
42            ],
43            audience: self.audience,
44            additional_params: Default::default(),
45        }
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52    use serial_test::serial;
53
54    struct EnvGuard {
55        key: &'static str,
56        original: Option<String>,
57    }
58
59    impl EnvGuard {
60        fn new(key: &'static str) -> Self {
61            Self {
62                key,
63                original: std::env::var(key).ok(),
64            }
65        }
66    }
67
68    impl Drop for EnvGuard {
69        fn drop(&mut self) {
70            if let Some(value) = &self.original {
71                std::env::set_var(self.key, value);
72            } else {
73                std::env::remove_var(self.key);
74            }
75        }
76    }
77
78    #[test]
79    #[serial]
80    fn from_env_requires_client_id() {
81        let _guard = EnvGuard::new("NBLM_OAUTH_CLIENT_ID");
82        std::env::remove_var("NBLM_OAUTH_CLIENT_ID");
83        let err = OAuthClientConfig::from_env().unwrap_err();
84        assert!(matches!(
85            err,
86            OAuthError::MissingEnvVar("NBLM_OAUTH_CLIENT_ID")
87        ));
88    }
89
90    #[test]
91    #[serial]
92    fn from_env_uses_defaults() {
93        let _guard_id = EnvGuard::new("NBLM_OAUTH_CLIENT_ID");
94        let _guard_secret = EnvGuard::new("NBLM_OAUTH_CLIENT_SECRET");
95        let _guard_redirect = EnvGuard::new("NBLM_OAUTH_REDIRECT_URI");
96        let _guard_audience = EnvGuard::new("NBLM_OAUTH_AUDIENCE");
97
98        std::env::set_var("NBLM_OAUTH_CLIENT_ID", "client-id");
99        std::env::remove_var("NBLM_OAUTH_CLIENT_SECRET");
100        std::env::remove_var("NBLM_OAUTH_REDIRECT_URI");
101        std::env::remove_var("NBLM_OAUTH_AUDIENCE");
102
103        let config = OAuthClientConfig::from_env().unwrap();
104        assert_eq!(config.client_id, "client-id");
105        assert_eq!(config.redirect_uri, OAuthConfig::DEFAULT_REDIRECT_URI);
106        assert!(config.client_secret.is_none());
107        assert!(config.audience.is_none());
108    }
109}