cloud_sdk_reqwest/shared/
auth.rs1use core::fmt;
2
3use cloud_sdk_sanitization::{SecretBuffer, sanitize_bytes};
4use reqwest::header::HeaderValue;
5#[cfg(test)]
6use std::sync::{
7 Arc,
8 atomic::{AtomicUsize, Ordering},
9};
10use std::vec::Vec;
11
12pub const MAX_BEARER_TOKEN_BYTES: usize = 4096;
14
15const BEARER_PREFIX: &[u8] = b"Bearer ";
16
17#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub enum BearerTokenError {
20 Empty,
22 TooLong,
24 InvalidByte,
27 AllocationFailed,
29}
30
31impl_static_error!(BearerTokenError,
32 Self::Empty => "bearer token is empty",
33 Self::TooLong => "bearer token exceeds the length limit",
34 Self::InvalidByte => "bearer token contains an invalid byte",
35 Self::AllocationFailed => "bearer-token allocation failed",
36);
37
38pub struct BearerToken {
41 authorization: Vec<u8>,
42 #[cfg(test)]
43 drop_probe: Option<Arc<AtomicUsize>>,
44}
45
46impl BearerToken {
47 pub fn new(token: &str) -> Result<Self, BearerTokenError> {
49 Self::from_bytes(token.as_bytes())
50 }
51
52 pub fn from_mut_bytes(token: &mut [u8]) -> Result<Self, BearerTokenError> {
55 let result = Self::from_bytes(token);
56 sanitize_bytes(token);
57 result
58 }
59
60 pub fn from_secret_buffer(token: SecretBuffer<'_>) -> Result<Self, BearerTokenError> {
63 Self::from_bytes(token.as_slice())
64 }
65
66 fn from_bytes(token: &[u8]) -> Result<Self, BearerTokenError> {
67 if token.is_empty() {
68 return Err(BearerTokenError::Empty);
69 }
70 if token.len() > MAX_BEARER_TOKEN_BYTES {
71 return Err(BearerTokenError::TooLong);
72 }
73 let mut padding = false;
74 for byte in token.iter().copied() {
75 if byte == b'=' {
76 padding = true;
77 } else if padding || !is_bearer_byte(byte) {
78 return Err(BearerTokenError::InvalidByte);
79 }
80 }
81
82 let capacity = BEARER_PREFIX
83 .len()
84 .checked_add(token.len())
85 .ok_or(BearerTokenError::TooLong)?;
86 let mut authorization = Vec::new();
87 authorization
88 .try_reserve_exact(capacity)
89 .map_err(|_| BearerTokenError::AllocationFailed)?;
90 authorization.extend_from_slice(BEARER_PREFIX);
91 authorization.extend_from_slice(token);
92 Ok(Self {
93 authorization,
94 #[cfg(test)]
95 drop_probe: None,
96 })
97 }
98
99 pub(crate) fn header_value(&self) -> Result<HeaderValue, ()> {
100 let mut value = HeaderValue::from_bytes(&self.authorization).map_err(|_| ())?;
101 value.set_sensitive(true);
102 Ok(value)
103 }
104
105 #[cfg(test)]
106 pub(crate) fn owned_bytes(&self) -> &[u8] {
107 &self.authorization
108 }
109
110 #[cfg(test)]
111 pub(crate) fn with_drop_probe(
112 token: &str,
113 probe: Arc<AtomicUsize>,
114 ) -> Result<Self, BearerTokenError> {
115 let mut value = Self::new(token)?;
116 value.drop_probe = Some(probe);
117 Ok(value)
118 }
119}
120
121impl Drop for BearerToken {
122 fn drop(&mut self) {
123 sanitize_bytes(&mut self.authorization);
124 #[cfg(test)]
125 if let Some(probe) = &self.drop_probe {
126 probe.fetch_add(1, Ordering::SeqCst);
127 }
128 }
129}
130
131impl fmt::Debug for BearerToken {
132 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
133 formatter.write_str("BearerToken([redacted])")
134 }
135}
136
137const fn is_bearer_byte(byte: u8) -> bool {
138 byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'.' | b'_' | b'~' | b'+' | b'/')
139}