Module async_require_authorization

Source
Expand description

Authorize requests using the Authorization header asynchronously.

§Example

use tower_http::auth::{AsyncRequireAuthorizationLayer, AsyncAuthorizeRequest};
use http::{Request, Response, StatusCode, header::AUTHORIZATION};
use tower::{Service, ServiceExt, ServiceBuilder, service_fn, BoxError};
use futures_core::future::BoxFuture;
use bytes::Bytes;
use http_body_util::Full;

#[derive(Clone, Copy)]
struct MyAuth;

impl<B> AsyncAuthorizeRequest<B> for MyAuth
where
    B: Send + Sync + 'static,
{
    type RequestBody = B;
    type ResponseBody = Full<Bytes>;
    type Future = BoxFuture<'static, Result<Request<B>, Response<Self::ResponseBody>>>;

    fn authorize(&mut self, mut request: Request<B>) -> Self::Future {
        Box::pin(async {
            if let Some(user_id) = check_auth(&request).await {
                // Set `user_id` as a request extension so it can be accessed by other
                // services down the stack.
                request.extensions_mut().insert(user_id);

                Ok(request)
            } else {
                let unauthorized_response = Response::builder()
                    .status(StatusCode::UNAUTHORIZED)
                    .body(Full::<Bytes>::default())
                    .unwrap();

                Err(unauthorized_response)
            }
        })
    }
}

async fn check_auth<B>(request: &Request<B>) -> Option<UserId> {
    // ...
}

#[derive(Debug, Clone)]
struct UserId(String);

async fn handle(request: Request<Full<Bytes>>) -> Result<Response<Full<Bytes>>, BoxError> {
    // Access the `UserId` that was set in `on_authorized`. If `handle` gets called the
    // request was authorized and `UserId` will be present.
    let user_id = request
        .extensions()
        .get::<UserId>()
        .expect("UserId will be there if request was authorized");

    println!("request from {:?}", user_id);

    Ok(Response::new(Full::default()))
}

let service = ServiceBuilder::new()
    // Authorize requests using `MyAuth`
    .layer(AsyncRequireAuthorizationLayer::new(MyAuth))
    .service_fn(handle);

Or using a closure:

use tower_http::auth::{AsyncRequireAuthorizationLayer, AsyncAuthorizeRequest};
use http::{Request, Response, StatusCode};
use tower::{Service, ServiceExt, ServiceBuilder, BoxError};
use futures_core::future::BoxFuture;
use http_body_util::Full;
use bytes::Bytes;

async fn check_auth<B>(request: &Request<B>) -> Option<UserId> {
    // ...
}

#[derive(Debug)]
struct UserId(String);

async fn handle(request: Request<Full<Bytes>>) -> Result<Response<Full<Bytes>>, BoxError> {
    // ...
}

let service = ServiceBuilder::new()
    .layer(AsyncRequireAuthorizationLayer::new(|request: Request<Full<Bytes>>| async move {
        if let Some(user_id) = check_auth(&request).await {
            Ok(request)
        } else {
            let unauthorized_response = Response::builder()
                .status(StatusCode::UNAUTHORIZED)
                .body(Full::<Bytes>::default())
                .unwrap();

            Err(unauthorized_response)
        }
    }))
    .service_fn(handle);

Structs§

AsyncRequireAuthorization
Middleware that authorizes all requests using the Authorization header.
AsyncRequireAuthorizationLayer
Layer that applies AsyncRequireAuthorization which authorizes all requests using the Authorization header.
ResponseFuture
Response future for AsyncRequireAuthorization.

Traits§

AsyncAuthorizeRequest
Trait for authorizing requests.