Authorization using tower-http
In this example we will use tower-http's auth module to add a custom
authorization layer. There are also two builtin bearer
and password
layers
however since both bearer token and username and password combo are set at
compile time, the use is somewhat limited.
In this case we implement the AuthorizeRequest
trait for our custom Auth
struct, which requires to set the appropriate ResponseBody
(which in axum's
case must be axum::body::BoxBody
) as well as an authorize
method. Here,
based on the request and any data on the struct itself we can make authorization
decisions. In this example we extract the Authorization
header field and
compare it with a pre-defined value. If it matches we can return Ok(())
and
the route is taken otherwise we return an error response.
Dependencies
[dependencies]
axum = "0.5"
tower-http = { version = "0.2", features = ["auth"] }
tokio = { version = "1", features = ["full"] }
Code
use axum::body::BoxBody;
use axum::http::{header, Request, StatusCode};
use axum::response::Response;
use axum::routing::get;
use axum::{Router, Server};
use tower_http::auth::{AuthorizeRequest, RequireAuthorizationLayer};
#[derive(Clone)]
struct Auth {
expected: String,
}
impl<B> AuthorizeRequest<B> for Auth {
type ResponseBody = BoxBody;
fn authorize(&mut self, request: &mut Request<B>) -> Result<(), Response<Self::ResponseBody>> {
if let Some(header) = request.headers().get(header::AUTHORIZATION) {
if header == &self.expected {
return Ok(());
}
}
let response = Response::builder()
.status(StatusCode::UNAUTHORIZED)
.body(BoxBody::default())
.unwrap();
Err(response)
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let app = Router::new().route("/", get(|| async { "hello" })).layer(
RequireAuthorizationLayer::custom(Auth {
expected: "lol".to_string(),
}),
);
Server::bind(&"0.0.0.0:8080".parse()?)
.serve(app.into_make_service())
.await?;
Ok(())
}
Run
Start the server with
cargo run --bin auth-with-tower-http
Trying to get the root route without setting an authorization header results in a 401:
$ curl -i http://127.0.0.1:8080
HTTP/1.1 401 Unauthorized
content-length: 0
date: Tue, 08 Mar 2022 19:57:15 GMT
On the other hand, setting Authorization
appropriately succeeds:
$ curl -i -H "Authorization: lol" http://127.0.0.1:8080
HTTP/1.1 200 OK
content-type: text/plain; charset=utf-8
content-length: 5
date: Tue, 08 Mar 2022 19:59:15 GMT
hello