Skip to main content

feagi_agent/sdk/common/
auth_token.rs

1//! Authentication token for secure service access.
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6/// Fixed length for authentication tokens (32 bytes = 256 bits)
7pub const AUTH_TOKEN_LENGTH: usize = 32;
8
9/// A secure authentication token of fixed length.
10///
11/// The token value is masked in `Debug` output to prevent accidental exposure in logs.
12#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
13pub struct AuthToken {
14    value: [u8; AUTH_TOKEN_LENGTH],
15}
16
17impl AuthToken {
18    /// Create a new auth token from a fixed-length byte array.
19    pub fn new(value: [u8; AUTH_TOKEN_LENGTH]) -> Self {
20        Self { value }
21    }
22
23    /// Create a token from a hex string (64 characters for 32 bytes).
24    ///
25    /// # Errors
26    /// Returns `None` if the string is not valid hex or wrong length.
27    pub fn from_hex(hex: &str) -> Option<Self> {
28        if hex.len() != AUTH_TOKEN_LENGTH * 2 {
29            return None;
30        }
31
32        let mut value = [0u8; AUTH_TOKEN_LENGTH];
33        for (i, chunk) in hex.as_bytes().chunks(2).enumerate() {
34            let hex_byte = std::str::from_utf8(chunk).ok()?;
35            value[i] = u8::from_str_radix(hex_byte, 16).ok()?;
36        }
37        Some(Self { value })
38    }
39
40    /// Create a token from a base64 string.
41    ///
42    /// # Errors
43    /// Returns `None` if the string is not valid base64 or wrong length.
44    pub fn from_base64(b64: &str) -> Option<Self> {
45        use base64::Engine;
46        let decoded = base64::engine::general_purpose::STANDARD.decode(b64).ok()?;
47        if decoded.len() != AUTH_TOKEN_LENGTH {
48            return None;
49        }
50        let mut value = [0u8; AUTH_TOKEN_LENGTH];
51        value.copy_from_slice(&decoded);
52        Some(Self { value })
53    }
54
55    /// Get the raw token bytes.
56    ///
57    /// **Warning**: This exposes the actual token. Use carefully and avoid logging.
58    pub fn as_bytes(&self) -> &[u8; AUTH_TOKEN_LENGTH] {
59        &self.value
60    }
61
62    /// Convert to hex string (64 characters).
63    pub fn to_hex(&self) -> String {
64        self.value.iter().map(|b| format!("{:02x}", b)).collect()
65    }
66
67    /// Convert to base64 string.
68    pub fn to_base64(&self) -> String {
69        use base64::Engine;
70        base64::engine::general_purpose::STANDARD.encode(self.value)
71    }
72}
73
74// Custom Debug impl that masks the token value
75impl fmt::Debug for AuthToken {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        f.debug_struct("AuthToken")
78            .field("value", &"[REDACTED]")
79            .finish()
80    }
81}
82
83// Display shows a masked representation
84impl fmt::Display for AuthToken {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        let hex = self.to_hex();
87        write!(f, "{}...{}", &hex[..4], &hex[hex.len() - 4..])
88    }
89}