diff --git a/services/reports/src/http.rs b/services/reports/src/http.rs
--- a/services/reports/src/http.rs
+++ b/services/reports/src/http.rs
@@ -4,8 +4,9 @@
 
 use crate::config::CONFIG;
 use crate::constants::REQUEST_BODY_JSON_SIZE_LIMIT;
+use crate::service::ReportsService;
 
-pub async fn run_http_server() -> Result<()> {
+pub async fn run_http_server(service: ReportsService) -> Result<()> {
   use actix_web::middleware::{Logger, NormalizePath};
   use comm_services_lib::http::cors_config;
   use tracing_actix_web::TracingLogger;
@@ -19,6 +20,7 @@
       web::JsonConfig::default().limit(REQUEST_BODY_JSON_SIZE_LIMIT);
     App::new()
       .app_data(json_cfg)
+      .app_data(service.to_owned())
       .wrap(Logger::default())
       .wrap(TracingLogger::default())
       .wrap(NormalizePath::trim())
diff --git a/services/reports/src/main.rs b/services/reports/src/main.rs
--- a/services/reports/src/main.rs
+++ b/services/reports/src/main.rs
@@ -3,8 +3,11 @@
 pub mod database;
 pub mod http;
 pub mod report_types;
+pub mod service;
 
 use anyhow::Result;
+use comm_services_lib::blob::client::BlobServiceClient;
+use service::ReportsService;
 use tracing_subscriber::filter::{EnvFilter, LevelFilter};
 
 fn configure_logging() -> Result<()> {
@@ -25,9 +28,12 @@
 #[tokio::main]
 async fn main() -> Result<()> {
   configure_logging()?;
-  config::parse_cmdline_args()?;
+  let cfg = config::parse_cmdline_args()?;
+  let aws_config = config::load_aws_config().await;
 
-  let _aws_config = config::load_aws_config().await;
+  let db = database::client::DatabaseClient::new(&aws_config);
+  let blob_client = BlobServiceClient::new(cfg.blob_service_url.clone());
+  let service = ReportsService::new(db, blob_client);
 
-  crate::http::run_http_server().await
+  crate::http::run_http_server(service).await
 }
diff --git a/services/reports/src/service.rs b/services/reports/src/service.rs
new file mode 100644
--- /dev/null
+++ b/services/reports/src/service.rs
@@ -0,0 +1,64 @@
+use actix_web::FromRequest;
+use comm_services_lib::{auth::UserIdentity, blob::client::BlobServiceClient};
+use std::future::{ready, Ready};
+
+use crate::database::client::DatabaseClient;
+#[derive(Clone)]
+pub struct ReportsService {
+  db: DatabaseClient,
+  blob_client: BlobServiceClient,
+  requesting_user_id: Option<String>,
+}
+
+impl ReportsService {
+  pub fn new(db: DatabaseClient, blob_client: BlobServiceClient) -> Self {
+    Self {
+      db,
+      blob_client,
+      requesting_user_id: None,
+    }
+  }
+
+  pub fn authenticated(&self, user: UserIdentity) -> Self {
+    let user_id = user.user_id.to_string();
+    Self {
+      db: self.db.clone(),
+      blob_client: self.blob_client.with_user_identity(user),
+      requesting_user_id: Some(user_id),
+    }
+  }
+}
+
+impl FromRequest for ReportsService {
+  type Error = actix_web::Error;
+  type Future = Ready<Result<Self, actix_web::Error>>;
+
+  #[inline]
+  fn from_request(
+    req: &actix_web::HttpRequest,
+    _payload: &mut actix_web::dev::Payload,
+  ) -> Self::Future {
+    use actix_web::HttpMessage;
+
+    let Some(service) = req.app_data::<ReportsService>() else {
+      tracing::error!(
+        "FATAL! Failed to extract ReportsService from actix app_data. \
+        Check HTTP server configuration"
+      );
+      return ready(Err(actix_web::error::ErrorInternalServerError("Internal server error")));
+    };
+
+    let auth_service =
+      if let Some(user_identity) = req.extensions().get::<UserIdentity>() {
+        tracing::trace!("Found user identity. Creating authenticated service");
+        service.authenticated(user_identity.clone())
+      } else {
+        tracing::trace!(
+          "No user identity found. Leaving unauthenticated service"
+        );
+        service.clone()
+      };
+
+    ready(Ok(auth_service))
+  }
+}