1

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?

dzCodes
  • 25
  • 6
  • This is a job for a middleware I think https://actix.rs/docs/middleware/ . I have an open source app with an example of authentication middleware if you want to have a look ... https://lab.frogg.it/kuadrado-software/krustacea/-/blob/master/src/middleware/authentication.rs – Peterrabbit Jan 30 '23 at 21:39
  • @Peterrabbit thanks, but this unfortunately by the look of it doesn't set anything on the request. Auth service for me returns CustomClaim struct with info about the user. I would like to have this struct available inside handlers. – dzCodes Feb 01 '23 at 12:26
  • A middleware is just a service that calls another, you can return the request with your auth data in the innermost service. I suggest you reading a tute about actix mw. – Peterrabbit Feb 01 '23 at 13:49
  • Thanks I'll do a bit more reading.. I managed to implement it using mw in the past, but couldn't figure out how to make the data available to extract easily inside handlers. Working solution for me here was to just omit extract function, even though it would be much better with extract function, so I could use it inside middleware to auth whole service, and still use CustomClaim as parameter in handler functions just where I need it (without calling auth service twice)... – dzCodes Feb 01 '23 at 14:06

1 Answers1

1

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 
dzCodes
  • 25
  • 6