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}