8

I am implementing an authentication extractor in actix-web 2.0.0 using sqlx to access the database. I have this code:

use actix_web::{dev, web, Error, HttpRequest, FromRequest};
use actix_web::error::ErrorUnauthorized;
use futures::future::{ok, err, Ready};
use sqlx::PgPool;
use serde_derive::Deserialize;

use crate::model::User;

#[derive(Debug, Deserialize)]
pub struct Auth {
    user_id: u32,
}

impl FromRequest for Auth {
    type Error = Error;
    type Future = Ready<Result<Self, Self::Error>>;
    type Config = ();

    fn from_request(req: &HttpRequest, _: &mut dev::Payload) -> Self::Future {
        use actix_web::HttpMessage;

        let db_pool = req.app_data::<web::Data<PgPool>>().unwrap();
        let error = ErrorUnauthorized("{\"details\": \"Please log in\"}");

        if let Some(session_id) = req.cookie("sessionid") {
            log::info!("Session id {}", session_id);
            // let result = User::find_by_session(db_pool.get_ref(), session_id).await;
            ok(Auth { user_id: 0 })
        } else {
            err(error)
        }

    }
}

Of course, I cannot use await there. I saw an example which uses type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>> and returns Box::pin(async move { ... }) but I couldn't make it work (had problems with lifetimes of req).

1 Answer 1

15

I managed to do it. I extracted cookie before async move so there are no problems with req.

use std::pin::Pin;
use futures::Future;
use actix_web::{dev, web, Error, HttpRequest, FromRequest};
use actix_web::error::ErrorUnauthorized;
use sqlx::PgPool;
use serde_derive::Deserialize;

use crate::model::User;

#[derive(Debug, Deserialize)]
pub struct Auth {
    user_id: u32,
}

impl FromRequest for Auth {
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
    type Config = ();

    fn from_request(req: &HttpRequest, _: &mut dev::Payload) -> Self::Future {
        use actix_web::HttpMessage;

        let db_pool = req.app_data::<web::Data<PgPool>>().unwrap().clone();
        let cookie = req.cookie("sessionid");

        Box::pin(async move {
            let error = Err(ErrorUnauthorized("{\"details\": \"Please log in\"}"));

            if let Some(session_id) = cookie {
                let result = User::find_by_session(db_pool.get_ref(), session_id).await;
                // auth code
                Ok(Auth { user_id: 0 })
            } else {
                error
            }
        })
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot, I have a similar problem! (This entire thing is super ugly though)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.