Skip to main content

openclaw_node/auth/
api_key.rs

1//! Safe API key wrapper that prevents accidental logging.
2
3use napi_derive::napi;
4
5use openclaw_core::secrets::ApiKey;
6
7/// Safe API key wrapper.
8///
9/// This wrapper prevents accidental logging or printing of API keys.
10/// The key value is hidden from `toString()` and debug output.
11///
12/// ```javascript
13/// const key = new NodeApiKey('sk-secret-key');
14/// console.log(key.toString()); // "[REDACTED]"
15/// console.log(key.exposeSecretForApiCall()); // "sk-secret-key"
16/// ```
17#[napi]
18pub struct NodeApiKey {
19    pub(crate) inner: ApiKey,
20}
21
22#[napi]
23impl NodeApiKey {
24    /// Create a new API key from a string.
25    ///
26    /// **Security Note**: Prefer loading keys from `CredentialStore`
27    /// rather than hardcoding them.
28    #[napi(constructor)]
29    #[must_use]
30    pub fn new(key: String) -> Self {
31        Self {
32            inner: ApiKey::new(key),
33        }
34    }
35
36    /// Returns "[REDACTED]" - safe for logging.
37    ///
38    /// This method intentionally hides the key value to prevent
39    /// accidental exposure in logs.
40    #[napi]
41    #[must_use]
42    pub fn to_string(&self) -> String {
43        "[REDACTED]".to_string()
44    }
45
46    /// Get the actual key value.
47    ///
48    /// **Warning**: Only use this when actually sending to an API.
49    /// The verbose method name is intentional to discourage casual use.
50    #[napi]
51    #[must_use]
52    pub fn expose_secret_for_api_call(&self) -> String {
53        self.inner.expose().to_string()
54    }
55
56    /// Check if the key is empty.
57    #[napi]
58    #[must_use]
59    pub fn is_empty(&self) -> bool {
60        self.inner.expose().is_empty()
61    }
62
63    /// Get the length of the key.
64    #[napi]
65    #[must_use]
66    pub fn length(&self) -> u32 {
67        self.inner.expose().len() as u32
68    }
69
70    /// Check if the key starts with a prefix (e.g., "sk-ant-" for Anthropic).
71    #[napi]
72    #[must_use]
73    pub fn starts_with(&self, prefix: String) -> bool {
74        self.inner.expose().starts_with(&prefix)
75    }
76}
77
78impl std::fmt::Debug for NodeApiKey {
79    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80        write!(f, "NodeApiKey([REDACTED])")
81    }
82}