diff --git a/tower-http/CHANGELOG.md b/tower-http/CHANGELOG.md
index d8e8cf56..0f6e8d3f 100644
--- a/tower-http/CHANGELOG.md
+++ b/tower-http/CHANGELOG.md
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
# Unreleased
+- Add `AddAuthorizationLayer` for setting the `Authorization` header on
+ requests.
- Add example of using `SharedClassifier`.
- Add `StatusInRangeAsFailures` which is a response classifier that considers
responses with status code in a certain range as failures. Useful for HTTP
diff --git a/tower-http/src/auth/add_authorization.rs b/tower-http/src/auth/add_authorization.rs
new file mode 100644
index 00000000..202c42d9
--- /dev/null
+++ b/tower-http/src/auth/add_authorization.rs
@@ -0,0 +1,262 @@
+//! Add authorization to requests using the [`Authorization`] header.
+//!
+//! [`Authorization`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization
+//!
+//! # Example
+//!
+//! ```
+//! use tower_http::auth::AddAuthorizationLayer;
+//! use hyper::{Request, Response, Body, Error};
+//! use http::{StatusCode, header::AUTHORIZATION};
+//! use tower::{Service, ServiceExt, ServiceBuilder, service_fn};
+//! # async fn handle(request: Request
) -> Result, Error> {
+//! # Ok(Response::new(Body::empty()))
+//! # }
+//!
+//! # #[tokio::main]
+//! # async fn main() -> Result<(), Box> {
+//! # let service_that_requires_auth = tower_http::auth::RequireAuthorization::basic(
+//! # tower::service_fn(handle),
+//! # "username",
+//! # "password",
+//! # );
+//! let mut client = ServiceBuilder::new()
+//! // Use basic auth with the given username and password
+//! .layer(AddAuthorizationLayer::basic("username", "password"))
+//! .service(service_that_requires_auth);
+//!
+//! // Make a request, we don't have to add the `Authorization` header manually
+//! let response = client
+//! .ready()
+//! .await?
+//! .call(Request::new(Body::empty()))
+//! .await?;
+//!
+//! assert_eq!(StatusCode::OK, response.status());
+//! # Ok(())
+//! # }
+//! ```
+
+use http::{HeaderValue, Request};
+use std::{
+ convert::TryFrom,
+ task::{Context, Poll},
+};
+use tower_layer::Layer;
+use tower_service::Service;
+
+/// Layer that applies [`AddAuthorization`] which adds authorization to all requests using the
+/// [`Authorization`] header.
+///
+/// See the [module docs](crate::auth::add_authorization) for an example.
+///
+/// You can also use [`SetRequestHeader`] if you have a use case that isn't supported by this
+/// middleware.
+///
+/// [`Authorization`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization
+/// [`SetRequestHeader`]: crate::set_header::SetRequestHeader
+#[derive(Debug, Clone)]
+pub struct AddAuthorizationLayer {
+ value: HeaderValue,
+}
+
+impl AddAuthorizationLayer {
+ /// Authorize requests using a username and password pair.
+ ///
+ /// The `Authorization` header will be set to `Basic {credentials}` where `credentials` is
+ /// `base64_encode("{username}:{password}")`.
+ ///
+ /// Since the username and password is sent in clear text it is recommended to use HTTPS/TLS
+ /// with this method. However use of HTTPS/TLS is not enforced by this middleware.
+ pub fn basic(username: &str, password: &str) -> Self {
+ let encoded = base64::encode(format!("{}:{}", username, password));
+ let value = HeaderValue::try_from(format!("Basic {}", encoded)).unwrap();
+ Self { value }
+ }
+
+ /// Authorize requests using a "bearer token". Commonly used for OAuth 2.
+ ///
+ /// The `Authorization` header will be set to `Bearer {token}`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the token is not a valid [`HeaderValue`](http::header::HeaderValue).
+ pub fn bearer(token: &str) -> Self {
+ let value =
+ HeaderValue::try_from(format!("Bearer {}", token)).expect("token is not valid header");
+ Self { value }
+ }
+
+ /// Mark the header as [sensitive].
+ ///
+ /// This can for example be used to hide the header value from logs.
+ ///
+ /// [sensitive]: https://docs.rs/http/latest/http/header/struct.HeaderValue.html#method.set_sensitive
+ #[allow(clippy::wrong_self_convention)]
+ pub fn as_sensitive(mut self, sensitive: bool) -> Self {
+ self.value.set_sensitive(sensitive);
+ self
+ }
+}
+
+impl Layer for AddAuthorizationLayer {
+ type Service = AddAuthorization;
+
+ fn layer(&self, inner: S) -> Self::Service {
+ AddAuthorization {
+ inner,
+ value: self.value.clone(),
+ }
+ }
+}
+
+/// Middleware that adds authorization all requests using the [`Authorization`] header.
+///
+/// See the [module docs](crate::auth::add_authorization) for an example.
+///
+/// You can also use [`SetRequestHeader`] if you have a use case that isn't supported by this
+/// middleware.
+///
+/// [`Authorization`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization
+/// [`SetRequestHeader`]: crate::set_header::SetRequestHeader
+#[derive(Debug, Clone)]
+pub struct AddAuthorization {
+ inner: S,
+ value: HeaderValue,
+}
+
+impl AddAuthorization {
+ /// Authorize requests using a username and password pair.
+ ///
+ /// The `Authorization` header will be set to `Basic {credentials}` where `credentials` is
+ /// `base64_encode("{username}:{password}")`.
+ ///
+ /// Since the username and password is sent in clear text it is recommended to use HTTPS/TLS
+ /// with this method. However use of HTTPS/TLS is not enforced by this middleware.
+ pub fn basic(inner: S, username: &str, password: &str) -> Self {
+ AddAuthorizationLayer::basic(username, password).layer(inner)
+ }
+
+ /// Authorize requests using a "bearer token". Commonly used for OAuth 2.
+ ///
+ /// The `Authorization` header will be set to `Bearer {token}`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the token is not a valid [`HeaderValue`](http::header::HeaderValue).
+ pub fn bearer(inner: S, token: &str) -> Self {
+ AddAuthorizationLayer::bearer(token).layer(inner)
+ }
+
+ define_inner_service_accessors!();
+
+ /// Mark the header as [sensitive].
+ ///
+ /// This can for example be used to hide the header value from logs.
+ ///
+ /// [sensitive]: https://docs.rs/http/latest/http/header/struct.HeaderValue.html#method.set_sensitive
+ #[allow(clippy::wrong_self_convention)]
+ pub fn as_sensitive(mut self, sensitive: bool) -> Self {
+ self.value.set_sensitive(sensitive);
+ self
+ }
+}
+
+impl Service> for AddAuthorization
+where
+ S: Service>,
+{
+ type Response = S::Response;
+ type Error = S::Error;
+ type Future = S::Future;
+
+ fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> {
+ self.inner.poll_ready(cx)
+ }
+
+ fn call(&mut self, mut req: Request) -> Self::Future {
+ req.headers_mut()
+ .insert(http::header::AUTHORIZATION, self.value.clone());
+ self.inner.call(req)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ #[allow(unused_imports)]
+ use super::*;
+ use crate::auth::RequireAuthorizationLayer;
+ use http::{Response, StatusCode};
+ use hyper::Body;
+ use tower::{BoxError, Service, ServiceBuilder, ServiceExt};
+
+ #[tokio::test]
+ async fn basic() {
+ // service that requires auth for all requests
+ let svc = ServiceBuilder::new()
+ .layer(RequireAuthorizationLayer::basic("foo", "bar"))
+ .service_fn(echo);
+
+ // make a client that adds auth
+ let mut client = AddAuthorization::basic(svc, "foo", "bar");
+
+ let res = client
+ .ready()
+ .await
+ .unwrap()
+ .call(Request::new(Body::empty()))
+ .await
+ .unwrap();
+
+ assert_eq!(res.status(), StatusCode::OK);
+ }
+
+ #[tokio::test]
+ async fn token() {
+ // service that requires auth for all requests
+ let svc = ServiceBuilder::new()
+ .layer(RequireAuthorizationLayer::bearer("foo"))
+ .service_fn(echo);
+
+ // make a client that adds auth
+ let mut client = AddAuthorization::bearer(svc, "foo");
+
+ let res = client
+ .ready()
+ .await
+ .unwrap()
+ .call(Request::new(Body::empty()))
+ .await
+ .unwrap();
+
+ assert_eq!(res.status(), StatusCode::OK);
+ }
+
+ #[tokio::test]
+ async fn making_header_sensitive() {
+ let svc = ServiceBuilder::new()
+ .layer(RequireAuthorizationLayer::bearer("foo"))
+ .service_fn(|request: Request| async move {
+ let auth = request.headers().get(http::header::AUTHORIZATION).unwrap();
+ assert!(auth.is_sensitive());
+
+ Ok::<_, hyper::Error>(Response::new(Body::empty()))
+ });
+
+ let mut client = AddAuthorization::bearer(svc, "foo").as_sensitive(true);
+
+ let res = client
+ .ready()
+ .await
+ .unwrap()
+ .call(Request::new(Body::empty()))
+ .await
+ .unwrap();
+
+ assert_eq!(res.status(), StatusCode::OK);
+ }
+
+ async fn echo(req: Request) -> Result, BoxError> {
+ Ok(Response::new(req.into_body()))
+ }
+}
diff --git a/tower-http/src/auth/mod.rs b/tower-http/src/auth/mod.rs
index 41d67732..da40b149 100644
--- a/tower-http/src/auth/mod.rs
+++ b/tower-http/src/auth/mod.rs
@@ -1,8 +1,10 @@
//! Authorization related middleware.
+pub mod add_authorization;
pub mod require_authorization;
#[doc(inline)]
-pub use self::require_authorization::{
- AuthorizeRequest, RequireAuthorization, RequireAuthorizationLayer,
+pub use self::{
+ add_authorization::{AddAuthorization, AddAuthorizationLayer},
+ require_authorization::{AuthorizeRequest, RequireAuthorization, RequireAuthorizationLayer},
};
diff --git a/tower-http/src/auth/require_authorization.rs b/tower-http/src/auth/require_authorization.rs
index 2bf3bcc0..466d65ea 100644
--- a/tower-http/src/auth/require_authorization.rs
+++ b/tower-http/src/auth/require_authorization.rs
@@ -28,7 +28,7 @@
//! .unwrap();
//!
//! let response = service
-//! .ready_and()
+//! .ready()
//! .await?
//! .call(request)
//! .await?;
@@ -41,7 +41,7 @@
//! .unwrap();
//!
//! let response = service
-//! .ready_and()
+//! .ready()
//! .await?
//! .call(request)
//! .await?;