http_extract/
authorization.rs1use http::{HeaderMap, Request, header::AUTHORIZATION};
20
21use crate::{Error, header::extract_single_header_text};
22
23pub const BEARER_SCHEME: &str = "Bearer";
25
26pub const BASIC_SCHEME: &str = "Basic";
28
29pub const SCHEME_SEPARATOR: char = ' ';
31
32pub fn extract_header_authorization(headers: &HeaderMap) -> Result<Option<&str>, Error> {
43 extract_single_header_text(headers, &AUTHORIZATION)
44}
45
46pub fn extract_request_authorization<B>(request: &Request<B>) -> Result<Option<&str>, Error> {
53 extract_header_authorization(request.headers())
54}
55
56pub fn extract_header_bearer_token(headers: &HeaderMap) -> Result<Option<&str>, Error> {
69 Ok(extract_header_authorization(headers)?
70 .and_then(|value| extract_scheme_credentials(value, BEARER_SCHEME)))
71}
72
73pub fn extract_request_bearer_token<B>(request: &Request<B>) -> Result<Option<&str>, Error> {
80 extract_header_bearer_token(request.headers())
81}
82
83pub fn extract_header_basic_credentials(headers: &HeaderMap) -> Result<Option<&str>, Error> {
96 Ok(extract_header_authorization(headers)?
97 .and_then(|value| extract_scheme_credentials(value, BASIC_SCHEME)))
98}
99
100pub fn extract_request_basic_credentials<B>(request: &Request<B>) -> Result<Option<&str>, Error> {
107 extract_header_basic_credentials(request.headers())
108}
109
110fn extract_scheme_credentials<'a>(value: &'a str, scheme: &str) -> Option<&'a str> {
111 let candidate = value.get(..scheme.len())?;
112 if !candidate.eq_ignore_ascii_case(scheme) {
113 return None;
114 }
115
116 value.get(scheme.len()..)?.strip_prefix(SCHEME_SEPARATOR)
117}
118
119#[cfg(test)]
120mod tests {
121 use http::{HeaderMap, HeaderValue, Request, header::AUTHORIZATION};
122
123 use super::*;
124
125 #[test]
126 fn extracts_raw_and_empty_authorization_values() {
127 let mut headers = HeaderMap::new();
128 assert_eq!(extract_header_authorization(&headers).unwrap(), None);
129
130 headers.insert(AUTHORIZATION, "Bearer opaque-secret".parse().unwrap());
131 assert_eq!(
132 extract_header_authorization(&headers).unwrap(),
133 Some("Bearer opaque-secret")
134 );
135
136 headers.insert(AUTHORIZATION, "".parse().unwrap());
137 assert_eq!(extract_header_authorization(&headers).unwrap(), Some(""));
138 }
139
140 #[test]
141 fn rejects_duplicate_and_non_text_values_without_echoing_them() {
142 let mut duplicate = HeaderMap::new();
143 duplicate.append(AUTHORIZATION, "Bearer first-secret".parse().unwrap());
144 duplicate.append(AUTHORIZATION, "Basic second-secret".parse().unwrap());
145 let error = extract_header_authorization(&duplicate).unwrap_err();
146 assert!(matches!(error, Error::DuplicateHeader { .. }));
147 assert!(!format!("{error:?}").contains("secret"));
148
149 let mut non_text = HeaderMap::new();
150 non_text.insert(AUTHORIZATION, HeaderValue::from_bytes(&[0xff]).unwrap());
151 assert!(matches!(
152 extract_header_authorization(&non_text),
153 Err(Error::InvalidHeader { .. })
154 ));
155 }
156
157 #[test]
158 fn routes_bearer_without_validating_or_normalizing_credentials() {
159 assert_eq!(extract_scheme_credentials("Bearer🦀", BEARER_SCHEME), None);
160 let mut headers = HeaderMap::new();
161 assert_eq!(extract_header_bearer_token(&headers).unwrap(), None);
162
163 headers.insert(AUTHORIZATION, "Basic encoded".parse().unwrap());
164 assert_eq!(extract_header_bearer_token(&headers).unwrap(), None);
165
166 headers.insert(AUTHORIZATION, "bEaReR opaque==".parse().unwrap());
167 assert_eq!(
168 extract_header_bearer_token(&headers).unwrap(),
169 Some("opaque==")
170 );
171
172 headers.insert(AUTHORIZATION, "Bearer keep-space".parse().unwrap());
173 assert_eq!(
174 extract_header_bearer_token(&headers).unwrap(),
175 Some(" keep-space")
176 );
177
178 headers.insert(AUTHORIZATION, "Bearer ".parse().unwrap());
179 assert_eq!(extract_header_bearer_token(&headers).unwrap(), Some(""));
180
181 headers.insert(AUTHORIZATION, "Bearer".parse().unwrap());
182 assert_eq!(extract_header_bearer_token(&headers).unwrap(), None);
183
184 assert_eq!(
185 extract_scheme_credentials("Béarer token", BEARER_SCHEME),
186 None
187 );
188 assert_eq!(extract_scheme_credentials("🦀", BEARER_SCHEME), None);
189 }
190
191 #[test]
192 fn routes_basic_without_decoding_credentials() {
193 let mut headers = HeaderMap::new();
194 headers.insert(AUTHORIZATION, "Bearer token".parse().unwrap());
195 assert_eq!(extract_header_basic_credentials(&headers).unwrap(), None);
196
197 headers.insert(AUTHORIZATION, "bAsIc plain-credentials".parse().unwrap());
198 assert_eq!(
199 extract_header_basic_credentials(&headers).unwrap(),
200 Some("plain-credentials")
201 );
202
203 headers.insert(AUTHORIZATION, "Basic ".parse().unwrap());
204 assert_eq!(
205 extract_header_basic_credentials(&headers).unwrap(),
206 Some("")
207 );
208
209 headers.insert(AUTHORIZATION, "Basic keep-space".parse().unwrap());
210 assert_eq!(
211 extract_header_basic_credentials(&headers).unwrap(),
212 Some(" keep-space")
213 );
214
215 headers.insert(AUTHORIZATION, "Basic".parse().unwrap());
216 assert_eq!(extract_header_basic_credentials(&headers).unwrap(), None);
217 }
218
219 #[test]
220 fn requires_a_space_between_scheme_and_credentials() {
221 let mut headers = HeaderMap::new();
222
223 headers.insert(AUTHORIZATION, "BearerAA".parse().unwrap());
224 assert_eq!(extract_header_bearer_token(&headers).unwrap(), None);
225
226 headers.insert(AUTHORIZATION, "Bearer AA".parse().unwrap());
227 assert_eq!(extract_header_bearer_token(&headers).unwrap(), Some("AA"));
228
229 headers.insert(AUTHORIZATION, "BasicAA".parse().unwrap());
230 assert_eq!(extract_header_basic_credentials(&headers).unwrap(), None);
231
232 headers.insert(AUTHORIZATION, "Basic AA".parse().unwrap());
233 assert_eq!(
234 extract_header_basic_credentials(&headers).unwrap(),
235 Some("AA")
236 );
237 }
238
239 #[test]
240 fn request_entry_points_delegate_to_headers() {
241 let request = Request::builder()
242 .header(AUTHORIZATION, "Bearer unparsed credentials")
243 .body(())
244 .unwrap();
245
246 assert_eq!(
247 extract_request_authorization(&request).unwrap(),
248 Some("Bearer unparsed credentials")
249 );
250 assert_eq!(
251 extract_request_bearer_token(&request).unwrap(),
252 Some("unparsed credentials")
253 );
254 assert_eq!(extract_request_basic_credentials(&request).unwrap(), None);
255
256 let basic = Request::builder()
257 .header(AUTHORIZATION, "Basic encoded")
258 .body(())
259 .unwrap();
260 assert_eq!(
261 extract_request_basic_credentials(&basic).unwrap(),
262 Some("encoded")
263 );
264 }
265}