diff --git a/services/backup/src/http/handlers/log.rs b/services/backup/src/http/handlers/log.rs
index 2f5536c37..5165eabfa 100644
--- a/services/backup/src/http/handlers/log.rs
+++ b/services/backup/src/http/handlers/log.rs
@@ -1,306 +1,342 @@
 use crate::constants::WS_FRAME_SIZE;
 use crate::database::{log_item::LogItem, DatabaseClient};
+use actix::fut::ready;
 use actix::{Actor, ActorContext, ActorFutureExt, AsyncContext, StreamHandler};
 use actix_http::ws::{CloseCode, Item};
 use actix_web::{
   web::{self, Bytes, BytesMut},
   Error, HttpRequest, HttpResponse,
 };
 use actix_web_actors::ws::{self, WebsocketContext};
 use comm_lib::auth::UserIdentity;
 use comm_lib::{
   backup::{
     DownloadLogsRequest, LogWSRequest, LogWSResponse, UploadLogRequest,
   },
   blob::{
     client::{BlobServiceClient, BlobServiceError},
     types::BlobInfo,
   },
   database::{self, blob::BlobOrDBContent},
 };
+use std::future::Future;
 use std::time::{Duration, Instant};
 use tracing::{error, info, instrument, warn};
 
 pub async fn handle_ws(
   req: HttpRequest,
-  user: UserIdentity,
   stream: web::Payload,
   blob_client: web::Data<BlobServiceClient>,
   db_client: web::Data<DatabaseClient>,
 ) -> Result<HttpResponse, Error> {
   ws::WsResponseBuilder::new(
     LogWSActor {
-      user,
+      user: None,
       blob_client: blob_client.as_ref().clone(),
       db_client: db_client.as_ref().clone(),
       last_msg_time: Instant::now(),
       buffer: BytesMut::new(),
     },
     &req,
     stream,
   )
   .frame_size(WS_FRAME_SIZE)
   .start()
 }
 
 #[derive(
   Debug, derive_more::From, derive_more::Display, derive_more::Error,
 )]
 enum LogWSError {
   Bincode(bincode::Error),
   Blob(BlobServiceError),
   DB(database::Error),
 }
 
 struct LogWSActor {
-  user: UserIdentity,
+  user: Option<UserIdentity>,
   blob_client: BlobServiceClient,
   db_client: DatabaseClient,
 
   last_msg_time: Instant,
   buffer: BytesMut,
 }
 
 impl LogWSActor {
   const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
   const CONNECTION_TIMEOUT: Duration = Duration::from_secs(10);
 
   fn handle_msg_sync(
-    &self,
+    &mut self,
     ctx: &mut WebsocketContext<LogWSActor>,
     bytes: Bytes,
   ) {
-    let fut = Self::handle_msg(
-      self.user.user_id.clone(),
-      self.blob_client.clone(),
-      self.db_client.clone(),
-      bytes,
-    );
+    match bincode::deserialize(&bytes) {
+      Ok(request) => {
+        if let LogWSRequest::Authenticate(user) = request {
+          self.user.replace(user);
+          return;
+        }
+
+        let Some(user) = &self.user else {
+          Self::spawn_response_future(
+            ctx,
+            ready(Ok(vec![LogWSResponse::Unauthenticated])),
+          );
+          return;
+        };
+
+        Self::spawn_response_future(
+          ctx,
+          Self::handle_msg(
+            user.user_id.clone(),
+            self.blob_client.clone(),
+            self.db_client.clone(),
+            request,
+          ),
+        );
+      }
+      Err(err) => {
+        error!("Error: {err:?}");
+
+        Self::spawn_response_future(
+          ctx,
+          ready(Ok(vec![LogWSResponse::ServerError])),
+        );
+      }
+    };
+  }
 
-    let fut = actix::fut::wrap_future(fut).map(
+  fn spawn_response_future(
+    ctx: &mut WebsocketContext<LogWSActor>,
+    future: impl Future<Output = Result<Vec<LogWSResponse>, LogWSError>> + 'static,
+  ) {
+    let fut = actix::fut::wrap_future(future).map(
       |responses,
        _: &mut LogWSActor,
        ctx: &mut WebsocketContext<LogWSActor>| {
         let responses = match responses {
           Ok(responses) => responses,
           Err(err) => {
             error!("Error: {err:?}");
             vec![LogWSResponse::ServerError]
           }
         };
 
         for response in responses {
           match bincode::serialize(&response) {
             Ok(bytes) => ctx.binary(bytes),
             Err(error) => {
               error!(
                 "Error serializing a response: {response:?}. Error: {error}"
               );
             }
           };
         }
       },
     );
 
     ctx.spawn(fut);
   }
 
   async fn handle_msg(
     user_id: String,
     blob_client: BlobServiceClient,
     db_client: DatabaseClient,
-    bytes: Bytes,
+    request: LogWSRequest,
   ) -> Result<Vec<LogWSResponse>, LogWSError> {
-    let request = bincode::deserialize(&bytes)?;
-
     match request {
       LogWSRequest::UploadLog(UploadLogRequest {
         backup_id,
         log_id,
         content,
         attachments,
       }) => {
         let mut attachment_blob_infos = Vec::new();
 
         for attachment in attachments.unwrap_or_default() {
           let blob_info =
             Self::create_attachment(&blob_client, attachment).await?;
 
           attachment_blob_infos.push(blob_info);
         }
 
         let mut log_item = LogItem {
           user_id,
           backup_id: backup_id.clone(),
           log_id,
           content: BlobOrDBContent::new(content),
           attachments: attachment_blob_infos,
         };
 
         log_item.ensure_size_constraints(&blob_client).await?;
         db_client.put_log_item(log_item).await?;
 
         Ok(vec![LogWSResponse::LogUploaded { backup_id, log_id }])
       }
       LogWSRequest::DownloadLogs(DownloadLogsRequest {
         backup_id,
         from_id,
       }) => {
         let (log_items, last_id) = db_client
           .fetch_log_items(&user_id, &backup_id, from_id)
           .await?;
 
         let mut messages = vec![];
 
         for LogItem {
           log_id,
           content,
           attachments,
           ..
         } in log_items
         {
           let content = content.fetch_bytes(&blob_client).await?;
           let attachments: Vec<String> =
             attachments.into_iter().map(|att| att.blob_hash).collect();
           let attachments = if attachments.is_empty() {
             None
           } else {
             Some(attachments)
           };
           messages.push(LogWSResponse::LogDownload {
             log_id,
             content,
             attachments,
           })
         }
 
         messages.push(LogWSResponse::LogDownloadFinished {
           last_log_id: last_id,
         });
 
         Ok(messages)
       }
+      LogWSRequest::Authenticate(_) => {
+        warn!("LogWSRequest::Authenticate should have been handled earlier.");
+        Ok(Vec::new())
+      }
     }
   }
 
   async fn create_attachment(
     blob_client: &BlobServiceClient,
     attachment: String,
   ) -> Result<BlobInfo, BlobServiceError> {
     let blob_info = BlobInfo {
       blob_hash: attachment,
       holder: uuid::Uuid::new_v4().to_string(),
     };
 
     if !blob_client
       .assign_holder(&blob_info.blob_hash, &blob_info.holder)
       .await?
     {
       warn!(
         "Blob attachment with hash {:?} doesn't exist",
         blob_info.blob_hash
       );
     }
 
     Ok(blob_info)
   }
 }
 
 impl Actor for LogWSActor {
   type Context = ws::WebsocketContext<Self>;
 
   #[instrument(skip_all)]
   fn started(&mut self, ctx: &mut Self::Context) {
     info!("Socket opened");
     ctx.run_interval(Self::HEARTBEAT_INTERVAL, |actor, ctx| {
       if Instant::now().duration_since(actor.last_msg_time)
         > Self::CONNECTION_TIMEOUT
       {
         warn!("Socket timeout, closing connection");
         ctx.stop();
         return;
       }
 
       ctx.ping(&[]);
     });
   }
 
   #[instrument(skip_all)]
   fn stopped(&mut self, _: &mut Self::Context) {
     info!("Socket closed");
   }
 }
 
 impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for LogWSActor {
   #[instrument(skip_all)]
   fn handle(
     &mut self,
     msg: Result<ws::Message, ws::ProtocolError>,
     ctx: &mut Self::Context,
   ) {
     let msg = match msg {
       Ok(msg) => msg,
       Err(err) => {
         warn!("Error during socket message handling: {err}");
         ctx.close(Some(CloseCode::Error.into()));
         ctx.stop();
         return;
       }
     };
 
     self.last_msg_time = Instant::now();
 
     match msg {
       ws::Message::Binary(bytes) => self.handle_msg_sync(ctx, bytes),
       // Continuations - this is mostly boilerplate code. Some websocket
       // clients may split a message into these ones
       ws::Message::Continuation(Item::FirstBinary(bytes)) => {
         if !self.buffer.is_empty() {
           warn!("Socket received continuation before previous was completed");
           ctx.close(Some(CloseCode::Error.into()));
           ctx.stop();
           return;
         }
         self.buffer.extend_from_slice(&bytes);
       }
       ws::Message::Continuation(Item::Continue(bytes)) => {
         if self.buffer.is_empty() {
           warn!("Socket received continuation message before it was started");
           ctx.close(Some(CloseCode::Error.into()));
           ctx.stop();
           return;
         }
         self.buffer.extend_from_slice(&bytes);
       }
       ws::Message::Continuation(Item::Last(bytes)) => {
         if self.buffer.is_empty() {
           warn!(
             "Socket received last continuation message before it was started"
           );
           ctx.close(Some(CloseCode::Error.into()));
           ctx.stop();
           return;
         }
         self.buffer.extend_from_slice(&bytes);
         let bytes = self.buffer.split();
 
         self.handle_msg_sync(ctx, bytes.into());
       }
       // Heartbeat
       ws::Message::Ping(message) => ctx.pong(&message),
       ws::Message::Pong(_) => (),
       // Other
       ws::Message::Text(_) | ws::Message::Continuation(Item::FirstText(_)) => {
         warn!("Socket received unsupported message");
         ctx.close(Some(CloseCode::Unsupported.into()));
         ctx.stop();
       }
       ws::Message::Close(reason) => {
         info!("Socket was closed");
         ctx.close(reason);
         ctx.stop();
       }
       ws::Message::Nop => (),
     }
   }
 }
diff --git a/services/backup/src/http/mod.rs b/services/backup/src/http/mod.rs
index 5422b5d2c..61d7a9e8d 100644
--- a/services/backup/src/http/mod.rs
+++ b/services/backup/src/http/mod.rs
@@ -1,75 +1,74 @@
 use actix_web::{web, App, HttpResponse, HttpServer};
 use anyhow::Result;
 use comm_lib::{
   blob::client::BlobServiceClient,
   http::auth::get_comm_authentication_middleware,
 };
 use tracing::info;
 
 use crate::{database::DatabaseClient, http::handlers::log::handle_ws, CONFIG};
 
 mod handlers {
   pub(super) mod backup;
   pub(super) mod log;
 }
 
 pub async fn run_http_server(
   db_client: DatabaseClient,
   blob_client: BlobServiceClient,
 ) -> Result<()> {
   info!(
     "Starting HTTP server listening at port {}",
     CONFIG.http_port
   );
 
   let db = web::Data::new(db_client);
   let blob = web::Data::new(blob_client);
 
   HttpServer::new(move || {
     App::new()
       .wrap(tracing_actix_web::TracingLogger::default())
       .wrap(comm_lib::http::cors_config(
         CONFIG.localstack_endpoint.is_some(),
       ))
       .app_data(db.clone())
       .app_data(blob.clone())
       .route("/health", web::get().to(HttpResponse::Ok))
       .service(
         // Backup services that don't require authetication
         web::scope("/backups/latest")
           .service(
             web::resource("{username}/backup_id")
               .route(web::get().to(handlers::backup::get_latest_backup_id)),
           )
           .service(web::resource("{username}/user_keys").route(
             web::get().to(handlers::backup::download_latest_backup_keys),
           )),
       )
       .service(
         // Backup services requiring authetication
         web::scope("/backups")
           .wrap(get_comm_authentication_middleware())
           .service(
             web::resource("").route(web::post().to(handlers::backup::upload)),
           )
           .service(
             web::resource("{backup_id}/user_keys")
               .route(web::get().to(handlers::backup::download_user_keys)),
           )
           .service(
             web::resource("{backup_id}/user_data")
               .route(web::get().to(handlers::backup::download_user_data)),
           ),
       )
       .service(
         web::scope("/logs")
-          .wrap(get_comm_authentication_middleware())
           .service(web::resource("").route(web::get().to(handle_ws))),
       )
   })
   .bind(("0.0.0.0", CONFIG.http_port))?
   .run()
   .await?;
 
   Ok(())
 }
diff --git a/shared/backup_client/src/lib.rs b/shared/backup_client/src/lib.rs
index 3038ee671..27b737085 100644
--- a/shared/backup_client/src/lib.rs
+++ b/shared/backup_client/src/lib.rs
@@ -1,374 +1,367 @@
 use std::time::Duration;
 
 use async_stream::{stream, try_stream};
 pub use comm_lib::auth::UserIdentity;
 pub use comm_lib::backup::{
   DownloadLogsRequest, LatestBackupIDResponse, LogWSRequest, LogWSResponse,
   UploadLogRequest,
 };
 pub use futures_util::{Sink, SinkExt, Stream, StreamExt, TryStreamExt};
 
 use hex::ToHex;
 use reqwest::{
   header::InvalidHeaderValue,
   multipart::{Form, Part},
   Body,
 };
 use sha2::{Digest, Sha256};
 use tokio_tungstenite::{
   connect_async,
   tungstenite::{
-    client::IntoClientRequest,
-    http::{header, Request},
     Error as TungsteniteError,
     Message::{Binary, Ping},
   },
 };
 
 const LOG_DOWNLOAD_RETRY_DELAY: Duration = Duration::from_secs(5);
 const LOG_DOWNLOAD_MAX_RETRY: usize = 3;
 
 #[derive(Debug, Clone)]
 pub struct BackupClient {
   url: reqwest::Url,
 }
 
 impl BackupClient {
   pub fn new<T: TryInto<reqwest::Url>>(url: T) -> Result<Self, T::Error> {
     Ok(BackupClient {
       url: url.try_into()?,
     })
   }
 }
 
 /// Backup functions
 impl BackupClient {
   pub async fn upload_backup(
     &self,
     user_identity: &UserIdentity,
     backup_data: BackupData,
   ) -> Result<(), Error> {
     let BackupData {
       backup_id,
       user_keys,
       user_data,
       attachments,
     } = backup_data;
 
     let client = reqwest::Client::new();
     let form = Form::new()
       .text("backup_id", backup_id)
       .text(
         "user_keys_hash",
         Sha256::digest(&user_keys).encode_hex::<String>(),
       )
       .part("user_keys", Part::stream(Body::from(user_keys)))
       .text(
         "user_data_hash",
         Sha256::digest(&user_data).encode_hex::<String>(),
       )
       .part("user_data", Part::stream(Body::from(user_data)))
       .text("attachments", attachments.join("\n"));
 
     let response = client
       .post(self.url.join("backups")?)
       .bearer_auth(user_identity.as_authorization_token()?)
       .multipart(form)
       .send()
       .await?;
 
     response.error_for_status()?;
 
     Ok(())
   }
 
   pub async fn download_backup_data(
     &self,
     backup_descriptor: &BackupDescriptor,
     requested_data: RequestedData,
   ) -> Result<Vec<u8>, Error> {
     let client = reqwest::Client::new();
     let url = self.url.join("backups/")?;
     let url = match backup_descriptor {
       BackupDescriptor::BackupID { backup_id, .. } => {
         url.join(&format!("{backup_id}/"))?
       }
       BackupDescriptor::Latest { username } => {
         url.join(&format!("latest/{username}/"))?
       }
     };
     let url = match &requested_data {
       RequestedData::BackupID => url.join("backup_id")?,
       RequestedData::UserKeys => url.join("user_keys")?,
       RequestedData::UserData => url.join("user_data")?,
     };
 
     let mut request = client.get(url);
 
     if let BackupDescriptor::BackupID { user_identity, .. } = backup_descriptor
     {
       request = request.bearer_auth(user_identity.as_authorization_token()?)
     }
 
     let response = request.send().await?;
 
     let result = response.error_for_status()?.bytes().await?.to_vec();
 
     Ok(result)
   }
 }
 
 /// Log functions
 impl BackupClient {
   pub async fn upload_logs(
     &self,
     user_identity: &UserIdentity,
   ) -> Result<
     (
       impl Sink<UploadLogRequest, Error = Error>,
       impl Stream<Item = Result<LogUploadConfirmation, Error>>,
     ),
     Error,
   > {
     let (tx, rx) = self.create_log_ws_connection(user_identity).await?;
 
     let rx = rx.map(|response| match response? {
       LogWSResponse::LogUploaded { backup_id, log_id } => {
         Ok(LogUploadConfirmation { backup_id, log_id })
       }
       LogWSResponse::ServerError => Err(Error::ServerError),
       msg => Err(Error::InvalidBackupMessage(msg)),
     });
 
     Ok((tx, rx))
   }
 
   /// Handles complete log download.
   /// It will try and retry download a few times, but if the issues persist
   /// the next item returned will be the last received error and the stream
   /// will be closed.
   pub async fn download_logs<'this>(
     &'this self,
     user_identity: &'this UserIdentity,
     backup_id: &'this str,
   ) -> impl Stream<Item = Result<DownloadedLog, Error>> + 'this {
     stream! {
       let mut last_downloaded_log = None;
       let mut fail_count = 0;
 
       'retry: loop {
         let stream = self.log_download_stream(user_identity, backup_id, &mut last_downloaded_log).await;
         let mut stream = Box::pin(stream);
 
         while let Some(item) = stream.next().await {
           match item {
             Ok(log) => yield Ok(log),
             Err(err) => {
               println!("Error when downloading logs: {err:?}");
 
               fail_count += 1;
               if fail_count >= LOG_DOWNLOAD_MAX_RETRY {
                 yield Err(err);
                 break 'retry;
               }
 
               tokio::time::sleep(LOG_DOWNLOAD_RETRY_DELAY).await;
               continue 'retry;
             }
           }
         }
 
         // Everything downloaded
         return;
       }
 
       println!("Log download failed!");
     }
   }
 
   /// Handles singular connection websocket connection. Returns error in case
   /// anything goes wrong e.g. missing log or connection error.
   async fn log_download_stream<'stream>(
     &'stream self,
     user_identity: &'stream UserIdentity,
     backup_id: &'stream str,
     last_downloaded_log: &'stream mut Option<usize>,
   ) -> impl Stream<Item = Result<DownloadedLog, Error>> + 'stream {
     try_stream! {
       let (tx, rx) = self.create_log_ws_connection(user_identity).await?;
 
       let mut tx = Box::pin(tx);
       let mut rx = Box::pin(rx);
 
       tx.send(DownloadLogsRequest {
         backup_id: backup_id.to_string(),
         from_id: *last_downloaded_log,
       })
       .await?;
 
       while let Some(response) = rx.try_next().await? {
         let expected_log_id = last_downloaded_log.unwrap_or(0);
         match response {
           LogWSResponse::LogDownload {
             content,
             attachments,
             log_id,
           } if log_id == expected_log_id + 1 => {
             *last_downloaded_log = Some(log_id);
             yield DownloadedLog {
               content,
               attachments,
             };
           }
           LogWSResponse::LogDownload { .. } => {
             Err(Error::LogMissing)?;
           }
           LogWSResponse::LogDownloadFinished {
             last_log_id: Some(log_id),
           } if log_id == expected_log_id => {
             tx.send(DownloadLogsRequest {
               backup_id: backup_id.to_string(),
               from_id: *last_downloaded_log,
             })
             .await?
           }
           LogWSResponse::LogDownloadFinished { last_log_id: None } => return,
           LogWSResponse::LogDownloadFinished { .. } => {
             Err(Error::LogMissing)?;
           }
           msg => Err(Error::InvalidBackupMessage(msg))?,
         }
       }
 
       Err(Error::WSClosed)?;
     }
   }
 
   async fn create_log_ws_connection<Request: Into<LogWSRequest>>(
     &self,
     user_identity: &UserIdentity,
   ) -> Result<
     (
       impl Sink<Request, Error = Error>,
       impl Stream<Item = Result<LogWSResponse, Error>>,
     ),
     Error,
   > {
-    let request = self.create_ws_request(user_identity)?;
-    let (stream, response) = connect_async(request).await?;
+    let url = self.create_ws_url()?;
+    let (stream, response) = connect_async(url).await?;
 
     if response.status().is_client_error() {
       return Err(Error::TungsteniteError(TungsteniteError::Http(response)));
     }
 
-    let (tx, rx) = stream.split();
+    let (mut tx, rx) = stream.split();
+
+    tx.send(Binary(bincode::serialize(&LogWSRequest::Authenticate(
+      user_identity.clone(),
+    ))?))
+    .await?;
 
     let tx = tx.with(|request: Request| async {
       let request: LogWSRequest = request.into();
       let request = bincode::serialize(&request)?;
       Ok(Binary(request))
     });
 
     let rx = rx.filter_map(|msg| async {
       let bytes = match msg {
         Ok(Binary(bytes)) => bytes,
         // Handled by tungstenite
         Ok(Ping(_)) => return None,
         Ok(_) => return Some(Err(Error::InvalidWSMessage)),
         Err(err) => return Some(Err(err.into())),
       };
 
       match bincode::deserialize(&bytes) {
         Ok(response) => Some(Ok(response)),
         Err(err) => Some(Err(err.into())),
       }
     });
 
     Ok((tx, rx))
   }
 
-  fn create_ws_request(
-    &self,
-    user_identity: &UserIdentity,
-  ) -> Result<Request<()>, Error> {
+  fn create_ws_url(&self) -> Result<reqwest::Url, Error> {
     let mut url = self.url.clone();
 
     match url.scheme() {
       "http" => url.set_scheme("ws").map_err(|_| Error::UrlSchemaError)?,
       "https" => url.set_scheme("wss").map_err(|_| Error::UrlSchemaError)?,
       _ => (),
     };
     let url = url.join("logs")?;
 
-    let mut request = url.into_client_request().unwrap();
-
-    let token = user_identity.as_authorization_token()?;
-    request
-      .headers_mut()
-      .insert(header::AUTHORIZATION, format!("Bearer {token}").parse()?);
-
-    Ok(request)
+    Ok(url)
   }
 }
 
 #[derive(Debug, Clone)]
 pub struct BackupData {
   pub backup_id: String,
   pub user_keys: Vec<u8>,
   pub user_data: Vec<u8>,
   pub attachments: Vec<String>,
 }
 
 #[derive(Debug, Clone)]
 pub enum BackupDescriptor {
   BackupID {
     backup_id: String,
     user_identity: UserIdentity,
   },
   Latest {
     username: String,
   },
 }
 
 #[derive(Debug, Clone, Copy)]
 pub enum RequestedData {
   BackupID,
   UserKeys,
   UserData,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct LogUploadConfirmation {
   pub backup_id: String,
   pub log_id: usize,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct DownloadedLog {
   pub content: Vec<u8>,
   pub attachments: Option<Vec<String>>,
 }
 
 #[derive(Debug, derive_more::Display, derive_more::From)]
 pub enum Error {
   InvalidAuthorizationHeader,
   UrlSchemaError,
   UrlError(url::ParseError),
   ReqwestError(reqwest::Error),
   TungsteniteError(TungsteniteError),
   JsonError(serde_json::Error),
   BincodeError(bincode::Error),
   InvalidWSMessage,
   #[display(fmt = "Error::InvalidBackupMessage({:?})", _0)]
   InvalidBackupMessage(LogWSResponse),
   ServerError,
   LogMissing,
   WSClosed,
 }
 impl std::error::Error for Error {}
 
 impl From<InvalidHeaderValue> for Error {
   fn from(_: InvalidHeaderValue) -> Self {
     Self::InvalidAuthorizationHeader
   }
 }
diff --git a/shared/comm-lib/src/backup/mod.rs b/shared/comm-lib/src/backup/mod.rs
index 0090cfb86..a681f8778 100644
--- a/shared/comm-lib/src/backup/mod.rs
+++ b/shared/comm-lib/src/backup/mod.rs
@@ -1,44 +1,47 @@
+use crate::auth::UserIdentity;
 use serde::{Deserialize, Serialize};
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct LatestBackupIDResponse {
   #[serde(rename = "backupID")]
   pub backup_id: String,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct UploadLogRequest {
   pub backup_id: String,
   pub log_id: usize,
   pub content: Vec<u8>,
   pub attachments: Option<Vec<String>>,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct DownloadLogsRequest {
   pub backup_id: String,
   pub from_id: Option<usize>,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize, derive_more::From)]
 pub enum LogWSRequest {
+  Authenticate(UserIdentity),
   UploadLog(UploadLogRequest),
   DownloadLogs(DownloadLogsRequest),
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub enum LogWSResponse {
   LogUploaded {
     backup_id: String,
     log_id: usize,
   },
   LogDownload {
     log_id: usize,
     content: Vec<u8>,
     attachments: Option<Vec<String>>,
   },
   LogDownloadFinished {
     last_log_id: Option<usize>,
   },
   ServerError,
+  Unauthenticated,
 }