1fn constant_time_eq(a: &str, b: &str) -> bool {
8 if a.len() != b.len() {
9 return false;
10 }
11 a.bytes()
12 .zip(b.bytes())
13 .fold(0, |acc, (x, y)| acc | (x ^ y))
14 == 0
15}
16
17#[derive(Debug)]
19pub struct BearerAuth {
20 token: String,
21}
22
23impl BearerAuth {
24 pub fn new(token: impl Into<String>) -> Self {
26 Self {
27 token: token.into(),
28 }
29 }
30
31 pub fn generate() -> Self {
41 use std::sync::atomic::{AtomicU64, Ordering};
42 use std::time::{SystemTime, UNIX_EPOCH};
43
44 static COUNTER: AtomicU64 = AtomicU64::new(0);
45 let time = SystemTime::now()
46 .duration_since(UNIX_EPOCH)
47 .unwrap_or_default()
48 .as_nanos() as u64;
49 let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
50 let mut s = time.wrapping_add(counter).wrapping_add(0x9e3779b97f4a7c15);
52 let mut token = String::with_capacity(32);
53 for _ in 0..32 {
54 s ^= s << 13;
55 s ^= s >> 7;
56 s ^= s << 17;
57 let nibble = (s & 0xF) as u8;
58 token.push(if nibble < 10 {
59 b'0' + nibble
60 } else {
61 b'a' + nibble - 10
62 } as char);
63 }
64 Self { token }
65 }
66
67 pub fn validate(&self, header_value: &str) -> bool {
69 if let Some(token) = header_value.strip_prefix("Bearer ") {
70 constant_time_eq(token.trim(), &self.token)
71 } else {
72 false
73 }
74 }
75
76 pub fn token(&self) -> &str {
78 &self.token
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn validate_correct_token() {
88 let auth = BearerAuth::new("my-secret-token");
89 assert!(auth.validate("Bearer my-secret-token"));
90 }
91
92 #[test]
93 fn reject_wrong_token() {
94 let auth = BearerAuth::new("correct");
95 assert!(!auth.validate("Bearer wrong"));
96 }
97
98 #[test]
99 fn reject_missing_prefix() {
100 let auth = BearerAuth::new("token");
101 assert!(!auth.validate("token"));
102 assert!(!auth.validate("Basic token"));
103 }
104
105 #[test]
106 fn generate_produces_32_char_hex() {
107 let auth = BearerAuth::generate();
108 let token = auth.token();
109 assert_eq!(token.len(), 32);
110 assert!(token.chars().all(|c| c.is_ascii_hexdigit()));
111 }
112
113 #[test]
114 fn generate_unique() {
115 let a = BearerAuth::generate();
116 let b = BearerAuth::generate();
117 assert_ne!(a.token(), b.token());
118 }
119}