Search code examples
rustactix-web

what is the correct way of implementing auth in actix_web?


I've been doing rust for just short while but didn't much struggle until this issue. I want to authenticate each request, and make returned value (from auth service) available inside requests.

I've read implementing FromRequest for the struct returned by auth server should make it available.

But I have issues implementing it correctly. It requires extract and from_request functions both returning self, and if I understand it a little correctly, extract is being called the first, so I should set it somewhere in request extensions, and get it from there inside from_request. I have following code:

impl FromRequest for CustomClaim {
    type Error = actix_web::Error;
    type Future = std::pin::Pin<Box<dyn std::future::Future<Output = Result<Self, Self::Error>>>>;
    fn extract(req: &HttpRequest) -> Self::Future {
        let auth_header = req
            .headers()
            .get("Authorization")
            .unwrap()
            .to_str()
            .unwrap()
            .to_owned();

        let boxed_future = Box::pin(async move {
                let claim_future = get_claim(&auth_header).await;
                claim_future
        });

        req.extensions_mut().insert(boxed_future);
        boxed_future
    }

    fn from_request(req: &HttpRequest, payload: &mut actix_web::dev::Payload) -> Self::Future {
        let boxed_future = req.extensions().get::<Self::Future>().unwrap();
        boxed_future
    }
}


async fn get_claim(auth_header: &str) -> Result<CustomClaim, actix_web::Error>

But from_request doesn't compile because it returns a reference (error message: mismatched types expected struct `Pin\<Box\<(dyn futures::Future\<Output = Result\<auth::CustomClaim, actix_web::Error\>\> + 'static)\>\>`found reference`&Pin\<Box\<dyn futures::Future\<Output = Result\<auth::CustomClaim, actix_web::Error\>\>\>\>` )

Doing .clone() doesn't help because clone is not implemented on that future type so it still returns a reference. Can I implement clone for the future? Is my approach wrong?


Solution

  • Ok so I feel little dumb but the answer is - you (or I) don't need the extract function. Only from_request is required.

    So solution is, move the code from extract function to from_request, omit extract, and don't set anything on the request:

    impl FromRequest for CustomClaim {
        type Error = actix_web::Error;
        type Future = std::pin::Pin<Box<dyn std::future::Future<Output = Result<Self, Self::Error>>>>;
    
        fn from_request(req: &HttpRequest, _payload: &mut actix_web::dev::Payload) -> Self::Future {
            let auth_header = req
                .headers()
                .get("Authorization")
                .unwrap()
                .to_str()
                .unwrap()
                .to_owned();
    
            Box::pin(async move {
                    let claim_future = get_claim(&auth_header).await;
                    claim_future
            })
        }
    }
    

    Then it's possible to add a parameter in authenticated handlers:

    #[post("/authed-post")]
    async fn authed_post(
        _req: HttpRequest,
        claim: CustomClaim,
    // ...
    ) -> impl Responder