cloud_sdk_reqwest/shared/
auth.rs1use core::fmt;
2
3use cloud_sdk_sanitization::sanitize_bytes;
4use reqwest::header::HeaderValue;
5use std::vec::Vec;
6
7pub const MAX_BEARER_TOKEN_BYTES: usize = 4096;
9
10const BEARER_PREFIX: &[u8] = b"Bearer ";
11
12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub enum BearerTokenError {
15 Empty,
17 TooLong,
19 InvalidByte,
22 AllocationFailed,
24}
25
26pub struct BearerToken {
29 authorization: Vec<u8>,
30}
31
32impl BearerToken {
33 pub fn new(token: &str) -> Result<Self, BearerTokenError> {
35 if token.is_empty() {
36 return Err(BearerTokenError::Empty);
37 }
38 if token.len() > MAX_BEARER_TOKEN_BYTES {
39 return Err(BearerTokenError::TooLong);
40 }
41 let mut padding = false;
42 for byte in token.bytes() {
43 if byte == b'=' {
44 padding = true;
45 } else if padding || !is_bearer_byte(byte) {
46 return Err(BearerTokenError::InvalidByte);
47 }
48 }
49
50 let capacity = BEARER_PREFIX
51 .len()
52 .checked_add(token.len())
53 .ok_or(BearerTokenError::TooLong)?;
54 let mut authorization = Vec::new();
55 authorization
56 .try_reserve_exact(capacity)
57 .map_err(|_| BearerTokenError::AllocationFailed)?;
58 authorization.extend_from_slice(BEARER_PREFIX);
59 authorization.extend_from_slice(token.as_bytes());
60 Ok(Self { authorization })
61 }
62
63 pub(crate) fn header_value(&self) -> Result<HeaderValue, ()> {
64 let mut value = HeaderValue::from_bytes(&self.authorization).map_err(|_| ())?;
65 value.set_sensitive(true);
66 Ok(value)
67 }
68
69 #[cfg(all(
70 test,
71 any(feature = "blocking-rustls", feature = "blocking-rustls-fips")
72 ))]
73 pub(crate) fn owned_bytes(&self) -> &[u8] {
74 &self.authorization
75 }
76}
77
78impl Drop for BearerToken {
79 fn drop(&mut self) {
80 sanitize_bytes(&mut self.authorization);
81 }
82}
83
84impl fmt::Debug for BearerToken {
85 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
86 formatter.write_str("BearerToken([redacted])")
87 }
88}
89
90const fn is_bearer_byte(byte: u8) -> bool {
91 byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'.' | b'_' | b'~' | b'+' | b'/')
92}