Skip to main content

kraken_api_client/auth/
credentials.rs

1//! Credential management for Kraken API authentication.
2
3use secrecy::{ExposeSecret, SecretString};
4use std::sync::Arc;
5
6/// API credentials containing the key and secret.
7#[derive(Clone)]
8pub struct Credentials {
9    /// The API key (public identifier)
10    pub api_key: String,
11    /// The API secret (private, used for signing)
12    api_secret: SecretString,
13}
14
15impl Credentials {
16    /// Create new credentials from an API key and secret.
17    pub fn new(api_key: impl Into<String>, api_secret: impl Into<String>) -> Self {
18        Self {
19            api_key: api_key.into(),
20            api_secret: SecretString::from(api_secret.into()),
21        }
22    }
23
24    /// Get the API secret for signing.
25    ///
26    /// This method exposes the secret - use carefully.
27    pub fn expose_secret(&self) -> &str {
28        self.api_secret.expose_secret()
29    }
30}
31
32impl std::fmt::Debug for Credentials {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        f.debug_struct("Credentials")
35            .field("api_key", &self.api_key)
36            .field("api_secret", &"[REDACTED]")
37            .finish()
38    }
39}
40
41/// Trait for providing API credentials.
42///
43/// Implement this trait to customize how credentials are retrieved,
44/// for example from a secrets manager or environment variables.
45pub trait CredentialsProvider: Send + Sync {
46    /// Get the credentials.
47    fn get_credentials(&self) -> &Credentials;
48}
49
50/// Static credentials provider that holds credentials directly.
51#[derive(Clone)]
52pub struct StaticCredentials {
53    credentials: Credentials,
54}
55
56impl StaticCredentials {
57    /// Create a new static credentials provider.
58    pub fn new(api_key: impl Into<String>, api_secret: impl Into<String>) -> Self {
59        Self {
60            credentials: Credentials::new(api_key, api_secret),
61        }
62    }
63}
64
65impl CredentialsProvider for StaticCredentials {
66    fn get_credentials(&self) -> &Credentials {
67        &self.credentials
68    }
69}
70
71impl CredentialsProvider for Arc<StaticCredentials> {
72    fn get_credentials(&self) -> &Credentials {
73        &self.credentials
74    }
75}
76
77/// Credentials provider that reads from environment variables.
78///
79/// By default, reads from `KRAKEN_API_KEY` and `KRAKEN_API_SECRET`.
80pub struct EnvCredentials {
81    credentials: Credentials,
82}
83
84impl EnvCredentials {
85    /// Create credentials from default environment variables.
86    ///
87    /// Reads `KRAKEN_API_KEY` and `KRAKEN_API_SECRET`.
88    ///
89    /// # Panics
90    ///
91    /// Panics if the environment variables are not set.
92    pub fn from_env() -> Self {
93        Self::from_env_vars("KRAKEN_API_KEY", "KRAKEN_API_SECRET")
94    }
95
96    /// Create credentials from custom environment variable names.
97    ///
98    /// # Panics
99    ///
100    /// Panics if the environment variables are not set.
101    pub fn from_env_vars(key_var: &str, secret_var: &str) -> Self {
102        let api_key = std::env::var(key_var)
103            .unwrap_or_else(|_| panic!("Environment variable {key_var} not set"));
104        let api_secret = std::env::var(secret_var)
105            .unwrap_or_else(|_| panic!("Environment variable {secret_var} not set"));
106
107        Self {
108            credentials: Credentials::new(api_key, api_secret),
109        }
110    }
111
112    /// Try to create credentials from default environment variables.
113    ///
114    /// Returns `None` if the environment variables are not set.
115    pub fn try_from_env() -> Option<Self> {
116        Self::try_from_env_vars("KRAKEN_API_KEY", "KRAKEN_API_SECRET")
117    }
118
119    /// Try to create credentials from custom environment variable names.
120    ///
121    /// Returns `None` if the environment variables are not set.
122    pub fn try_from_env_vars(key_var: &str, secret_var: &str) -> Option<Self> {
123        let api_key = std::env::var(key_var).ok()?;
124        let api_secret = std::env::var(secret_var).ok()?;
125
126        Some(Self {
127            credentials: Credentials::new(api_key, api_secret),
128        })
129    }
130}
131
132impl CredentialsProvider for EnvCredentials {
133    fn get_credentials(&self) -> &Credentials {
134        &self.credentials
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141
142    #[test]
143    fn test_credentials_debug_redacted() {
144        let creds = Credentials::new("my_key", "super_secret");
145        let debug_str = format!("{:?}", creds);
146        assert!(debug_str.contains("my_key"));
147        assert!(!debug_str.contains("super_secret"));
148        assert!(debug_str.contains("[REDACTED]"));
149    }
150
151    #[test]
152    fn test_static_credentials() {
153        let provider = StaticCredentials::new("key", "secret");
154        let creds = provider.get_credentials();
155        assert_eq!(creds.api_key, "key");
156        assert_eq!(creds.expose_secret(), "secret");
157    }
158}