tower_http/auth/add_authorization.rs
1//! Add authorization to requests using the [`Authorization`] header.
2//!
3//! [`Authorization`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization
4//!
5//! # Example
6//!
7//! ```
8//! use tower_http::validate_request::{ValidateRequestHeader, ValidateRequestHeaderLayer};
9//! use tower_http::auth::AddAuthorizationLayer;
10//! use http::{Request, Response, StatusCode, header::AUTHORIZATION};
11//! use tower::{Service, ServiceExt, ServiceBuilder, service_fn, BoxError};
12//! use http_body_util::Full;
13//! use bytes::Bytes;
14//! # async fn handle(request: Request<Full<Bytes>>) -> Result<Response<Full<Bytes>>, BoxError> {
15//! # Ok(Response::new(Full::default()))
16//! # }
17//!
18//! # #[tokio::main]
19//! # async fn main() -> Result<(), BoxError> {
20//! # let service_that_requires_auth = ValidateRequestHeader::basic(
21//! # tower::service_fn(handle),
22//! # "username",
23//! # "password",
24//! # );
25//! let mut client = ServiceBuilder::new()
26//! // Use basic auth with the given username and password
27//! .layer(AddAuthorizationLayer::basic("username", "password"))
28//! .service(service_that_requires_auth);
29//!
30//! // Make a request, we don't have to add the `Authorization` header manually
31//! let response = client
32//! .ready()
33//! .await?
34//! .call(Request::new(Full::default()))
35//! .await?;
36//!
37//! assert_eq!(StatusCode::OK, response.status());
38//! # Ok(())
39//! # }
40//! ```
41
42use base64::Engine as _;
43use http::{HeaderValue, Request, Response};
44use std::{
45 convert::TryFrom,
46 task::{Context, Poll},
47};
48use tower_layer::Layer;
49use tower_service::Service;
50
51const BASE64: base64::engine::GeneralPurpose = base64::engine::general_purpose::STANDARD;
52
53/// Layer that applies [`AddAuthorization`] which adds authorization to all requests using the
54/// [`Authorization`] header.
55///
56/// See the [module docs](crate::auth::add_authorization) for an example.
57///
58/// You can also use [`SetRequestHeader`] if you have a use case that isn't supported by this
59/// middleware.
60///
61/// [`Authorization`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization
62/// [`SetRequestHeader`]: crate::set_header::SetRequestHeader
63#[derive(Debug, Clone)]
64pub struct AddAuthorizationLayer {
65 value: HeaderValue,
66}
67
68impl AddAuthorizationLayer {
69 /// Authorize requests using a username and password pair.
70 ///
71 /// The `Authorization` header will be set to `Basic {credentials}` where `credentials` is
72 /// `base64_encode("{username}:{password}")`.
73 ///
74 /// Since the username and password is sent in clear text it is recommended to use HTTPS/TLS
75 /// with this method. However use of HTTPS/TLS is not enforced by this middleware.
76 pub fn basic(username: &str, password: &str) -> Self {
77 let encoded = BASE64.encode(format!("{}:{}", username, password));
78 let value = HeaderValue::try_from(format!("Basic {}", encoded)).unwrap();
79 Self { value }
80 }
81
82 /// Authorize requests using a "bearer token". Commonly used for OAuth 2.
83 ///
84 /// The `Authorization` header will be set to `Bearer {token}`.
85 ///
86 /// # Panics
87 ///
88 /// Panics if the token is not a valid [`HeaderValue`].
89 pub fn bearer(token: &str) -> Self {
90 let value =
91 HeaderValue::try_from(format!("Bearer {}", token)).expect("token is not valid header");
92 Self { value }
93 }
94
95 /// Mark the header as [sensitive].
96 ///
97 /// This can for example be used to hide the header value from logs.
98 ///
99 /// [sensitive]: https://docs.rs/http/latest/http/header/struct.HeaderValue.html#method.set_sensitive
100 #[allow(clippy::wrong_self_convention)]
101 pub fn as_sensitive(mut self, sensitive: bool) -> Self {
102 self.value.set_sensitive(sensitive);
103 self
104 }
105}
106
107impl<S> Layer<S> for AddAuthorizationLayer {
108 type Service = AddAuthorization<S>;
109
110 fn layer(&self, inner: S) -> Self::Service {
111 AddAuthorization {
112 inner,
113 value: self.value.clone(),
114 }
115 }
116}
117
118/// Middleware that adds authorization all requests using the [`Authorization`] header.
119///
120/// See the [module docs](crate::auth::add_authorization) for an example.
121///
122/// You can also use [`SetRequestHeader`] if you have a use case that isn't supported by this
123/// middleware.
124///
125/// [`Authorization`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization
126/// [`SetRequestHeader`]: crate::set_header::SetRequestHeader
127#[derive(Debug, Clone)]
128pub struct AddAuthorization<S> {
129 inner: S,
130 value: HeaderValue,
131}
132
133impl<S> AddAuthorization<S> {
134 /// Authorize requests using a username and password pair.
135 ///
136 /// The `Authorization` header will be set to `Basic {credentials}` where `credentials` is
137 /// `base64_encode("{username}:{password}")`.
138 ///
139 /// Since the username and password is sent in clear text it is recommended to use HTTPS/TLS
140 /// with this method. However use of HTTPS/TLS is not enforced by this middleware.
141 pub fn basic(inner: S, username: &str, password: &str) -> Self {
142 AddAuthorizationLayer::basic(username, password).layer(inner)
143 }
144
145 /// Authorize requests using a "bearer token". Commonly used for OAuth 2.
146 ///
147 /// The `Authorization` header will be set to `Bearer {token}`.
148 ///
149 /// # Panics
150 ///
151 /// Panics if the token is not a valid [`HeaderValue`].
152 pub fn bearer(inner: S, token: &str) -> Self {
153 AddAuthorizationLayer::bearer(token).layer(inner)
154 }
155
156 define_inner_service_accessors!();
157
158 /// Mark the header as [sensitive].
159 ///
160 /// This can for example be used to hide the header value from logs.
161 ///
162 /// [sensitive]: https://docs.rs/http/latest/http/header/struct.HeaderValue.html#method.set_sensitive
163 #[allow(clippy::wrong_self_convention)]
164 pub fn as_sensitive(mut self, sensitive: bool) -> Self {
165 self.value.set_sensitive(sensitive);
166 self
167 }
168}
169
170impl<S, ReqBody, ResBody> Service<Request<ReqBody>> for AddAuthorization<S>
171where
172 S: Service<Request<ReqBody>, Response = Response<ResBody>>,
173{
174 type Response = S::Response;
175 type Error = S::Error;
176 type Future = S::Future;
177
178 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
179 self.inner.poll_ready(cx)
180 }
181
182 fn call(&mut self, mut req: Request<ReqBody>) -> Self::Future {
183 req.headers_mut()
184 .insert(http::header::AUTHORIZATION, self.value.clone());
185 self.inner.call(req)
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192 use crate::test_helpers::Body;
193 use crate::validate_request::ValidateRequestHeaderLayer;
194 use http::{Response, StatusCode};
195 use std::convert::Infallible;
196 use tower::{BoxError, Service, ServiceBuilder, ServiceExt};
197
198 #[tokio::test]
199 async fn basic() {
200 // service that requires auth for all requests
201 let svc = ServiceBuilder::new()
202 .layer(ValidateRequestHeaderLayer::basic("foo", "bar"))
203 .service_fn(echo);
204
205 // make a client that adds auth
206 let mut client = AddAuthorization::basic(svc, "foo", "bar");
207
208 let res = client
209 .ready()
210 .await
211 .unwrap()
212 .call(Request::new(Body::empty()))
213 .await
214 .unwrap();
215
216 assert_eq!(res.status(), StatusCode::OK);
217 }
218
219 #[tokio::test]
220 async fn token() {
221 // service that requires auth for all requests
222 let svc = ServiceBuilder::new()
223 .layer(ValidateRequestHeaderLayer::bearer("foo"))
224 .service_fn(echo);
225
226 // make a client that adds auth
227 let mut client = AddAuthorization::bearer(svc, "foo");
228
229 let res = client
230 .ready()
231 .await
232 .unwrap()
233 .call(Request::new(Body::empty()))
234 .await
235 .unwrap();
236
237 assert_eq!(res.status(), StatusCode::OK);
238 }
239
240 #[tokio::test]
241 async fn making_header_sensitive() {
242 let svc = ServiceBuilder::new()
243 .layer(ValidateRequestHeaderLayer::bearer("foo"))
244 .service_fn(|request: Request<Body>| async move {
245 let auth = request.headers().get(http::header::AUTHORIZATION).unwrap();
246 assert!(auth.is_sensitive());
247
248 Ok::<_, Infallible>(Response::new(Body::empty()))
249 });
250
251 let mut client = AddAuthorization::bearer(svc, "foo").as_sensitive(true);
252
253 let res = client
254 .ready()
255 .await
256 .unwrap()
257 .call(Request::new(Body::empty()))
258 .await
259 .unwrap();
260
261 assert_eq!(res.status(), StatusCode::OK);
262 }
263
264 async fn echo(req: Request<Body>) -> Result<Response<Body>, BoxError> {
265 Ok(Response::new(req.into_body()))
266 }
267}