Skip to main content

cloud_sdk_reqwest/shared/
auth.rs

1use core::fmt;
2
3use cloud_sdk_sanitization::sanitize_bytes;
4use reqwest::header::HeaderValue;
5use std::vec::Vec;
6
7/// Maximum bearer-token length accepted by the adapter.
8pub const MAX_BEARER_TOKEN_BYTES: usize = 4096;
9
10const BEARER_PREFIX: &[u8] = b"Bearer ";
11
12/// Bearer-token validation error.
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub enum BearerTokenError {
15    /// Tokens must not be empty.
16    Empty,
17    /// Tokens exceed [`MAX_BEARER_TOKEN_BYTES`].
18    TooLong,
19    /// Tokens contain bytes outside the RFC bearer-token alphabet or invalid
20    /// non-trailing padding.
21    InvalidByte,
22    /// Allocation failed while taking owned secret storage.
23    AllocationFailed,
24}
25
26impl_static_error!(BearerTokenError,
27    Self::Empty => "bearer token is empty",
28    Self::TooLong => "bearer token exceeds the length limit",
29    Self::InvalidByte => "bearer token contains an invalid byte",
30    Self::AllocationFailed => "bearer-token allocation failed",
31);
32
33/// Owned bearer authorization value with redacted diagnostics and volatile
34/// cleanup of the adapter-owned bytes.
35pub struct BearerToken {
36    authorization: Vec<u8>,
37}
38
39impl BearerToken {
40    /// Validates and copies a bearer token into adapter-owned secret storage.
41    pub fn new(token: &str) -> Result<Self, BearerTokenError> {
42        if token.is_empty() {
43            return Err(BearerTokenError::Empty);
44        }
45        if token.len() > MAX_BEARER_TOKEN_BYTES {
46            return Err(BearerTokenError::TooLong);
47        }
48        let mut padding = false;
49        for byte in token.bytes() {
50            if byte == b'=' {
51                padding = true;
52            } else if padding || !is_bearer_byte(byte) {
53                return Err(BearerTokenError::InvalidByte);
54            }
55        }
56
57        let capacity = BEARER_PREFIX
58            .len()
59            .checked_add(token.len())
60            .ok_or(BearerTokenError::TooLong)?;
61        let mut authorization = Vec::new();
62        authorization
63            .try_reserve_exact(capacity)
64            .map_err(|_| BearerTokenError::AllocationFailed)?;
65        authorization.extend_from_slice(BEARER_PREFIX);
66        authorization.extend_from_slice(token.as_bytes());
67        Ok(Self { authorization })
68    }
69
70    pub(crate) fn header_value(&self) -> Result<HeaderValue, ()> {
71        let mut value = HeaderValue::from_bytes(&self.authorization).map_err(|_| ())?;
72        value.set_sensitive(true);
73        Ok(value)
74    }
75
76    #[cfg(all(
77        test,
78        any(
79            feature = "blocking-rustls",
80            feature = "blocking-rustls-webpki-roots",
81            feature = "blocking-rustls-fips"
82        )
83    ))]
84    pub(crate) fn owned_bytes(&self) -> &[u8] {
85        &self.authorization
86    }
87}
88
89impl Drop for BearerToken {
90    fn drop(&mut self) {
91        sanitize_bytes(&mut self.authorization);
92    }
93}
94
95impl fmt::Debug for BearerToken {
96    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
97        formatter.write_str("BearerToken([redacted])")
98    }
99}
100
101const fn is_bearer_byte(byte: u8) -> bool {
102    byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'.' | b'_' | b'~' | b'+' | b'/')
103}