How to share app state with middleware in Actix Rust?

Hey everyone. I'm learning how to use Actix and I'm at the point where I want to implement a BasicAuth middleware. I need to create a connection with a PostgreSQL database and as such I need access to the app state. I've tried many solutions such as this but I really don't understand how to work around the issue.

Here's my code:

pub struct AppState {
    db: Pool<Postgres>,
}

#[derive(Serialize)]
struct AuthUser {
    club_uid: String,
    name: String,
    password_hash: String,
}

impl<'r> FromRow<'r, PgRow> for AuthUser {
    fn from_row(row: &'r PgRow) -> Result<Self, sqlx::Error> {
        let club_uid: String = row.try_get("club_uid")?;
        let name: String = row.try_get("name")?;
        let password_hash: String = row.try_get("password_hash")?;

        Ok(AuthUser {club_uid, name, password_hash })
    }
}

async fn authenticator(req: ServiceRequest, creds: BasicAuth) -> Result<ServiceRequest, (Error, ServiceRequest)> {
    let username = creds.user_id();
    let password = creds.password();

    let state = req.app_data::<AppState>().unwrap();

    match password {
        None => Err((ErrorUnauthorized("Must provide a password"), req)),
        Some(pass) => {
            match sqlx::query_as::<_, AuthUser>(
                "SELECT CAST(club_uid AS TEXT), name, password_hash 
                    FROM club WHERE club = $1"
            )
            .bind(username.to_string())
            .fetch_one(&state.db)
            .await
            {
                Ok(user) => {
                    let is_valid = bcrypt::verify(pass, &user.password_hash).unwrap();
                    println!("{:?}", &user.password_hash);
                    if is_valid {
                        Ok(req)
                    } else {
                        Err((ErrorUnauthorized("nope"), req))
                    }
                }
                Err(error) => Err((ErrorUnauthorized(format!("{:?}", error)), req)),
            }
        }
    }
}

Any assistance would be appreciated. Thank you in advance.

What issue?

As mentioned in the post that you linked, there's the app_data method on ServiceRequest to access the shared state.

Thanks I realized that I had to simply place the Data wrapper over AppState:

    let state = req.app_data::<Data<AppState>>().unwrap();

That's called an extractor, not a wrapper. But yes, that's what you were missing.

1 Like

Thanks for the correction