diff --git a/services/identity/src/client_service.rs b/services/identity/src/client_service.rs --- a/services/identity/src/client_service.rs +++ b/services/identity/src/client_service.rs @@ -141,6 +141,9 @@ message.username.clone(), message.farcaster_id.clone(), )?; + self + .check_device_id_taken(®istration_state.flattened_device_key_upload) + .await?; let server_registration = comm_opaque2::server::Registration::new(); let server_message = server_registration .start( @@ -197,6 +200,9 @@ message.username.clone(), None, )?; + self + .check_device_id_taken(®istration_state.flattened_device_key_upload) + .await?; let server_registration = comm_opaque2::server::Registration::new(); let server_message = server_registration .start( @@ -322,6 +328,13 @@ let flattened_device_key_upload = construct_flattened_device_key_upload(&message)?; + if self + .check_device_id_taken(&flattened_device_key_upload) + .await + .is_err_and(|s| s.code() == tonic::Code::AlreadyExists) + { + warn!("Device ID already exists. Device data will be replaced."); + } let maybe_device_to_remove = self .get_keyserver_device_to_remove( @@ -456,6 +469,13 @@ let flattened_device_key_upload = construct_flattened_device_key_upload(&message)?; + if self + .check_device_id_taken(&flattened_device_key_upload) + .await + .is_err_and(|s| s.code() == tonic::Code::AlreadyExists) + { + warn!("Device ID already exists. Device data will be replaced."); + } let login_time = chrono::Utc::now(); let Some(user_id) = self @@ -573,6 +593,9 @@ let flattened_device_key_upload = construct_flattened_device_key_upload(&message)?; + self + .check_device_id_taken(&flattened_device_key_upload) + .await?; let login_time = chrono::Utc::now(); @@ -658,6 +681,9 @@ let flattened_device_key_upload = construct_flattened_device_key_upload(&message)?; + self + .check_device_id_taken(&flattened_device_key_upload) + .await?; let initial_device_list = message.get_and_verify_initial_device_list()?; let social_proof = @@ -722,6 +748,14 @@ let nonce = challenge_response.verify_and_get_nonce(&device_id)?; self.verify_and_remove_nonce(&nonce).await?; + if self + .check_device_id_taken(&flattened_device_key_upload) + .await + .is_err_and(|s| s.code() == tonic::Code::AlreadyExists) + { + warn!("Device ID already exists. Device data will be replaced."); + } + let user_identity = self .client .get_user_identity(&user_id) @@ -1048,6 +1082,24 @@ Ok(()) } + async fn check_device_id_taken( + &self, + key_upload: &FlattenedDeviceKeyUpload, + ) -> Result<(), tonic::Status> { + let device_id = key_upload.device_id_key.as_str(); + let device_already_exists = self + .client + .find_user_id_for_device(device_id) + .await + .map_err(handle_db_error)? + .is_some(); + if device_already_exists { + return Err(tonic::Status::already_exists("Device ID already exists")); + } + + Ok(()) + } + async fn verify_and_remove_nonce( &self, nonce: &str, diff --git a/services/identity/src/database/device_list.rs b/services/identity/src/database/device_list.rs --- a/services/identity/src/database/device_list.rs +++ b/services/identity/src/database/device_list.rs @@ -746,11 +746,13 @@ Ok(user_devices_keys) } + /// Find owner's user ID for given device ID. Useful for finding + /// devices table partition key. #[tracing::instrument(skip_all)] - pub async fn find_device_by_id( + pub async fn find_user_id_for_device( &self, device_id: &str, - ) -> Result, Error> { + ) -> Result, Error> { let response = self .client .query() @@ -785,15 +787,23 @@ return Err(Error::IllegalState); } - let Some(user_id) = results + let user_id = results .pop() .map(|mut attrs| attrs.take_attr::(devices_table::ATTR_USER_ID)) - .transpose()? - else { + .transpose()?; + + Ok(user_id) + } + + #[tracing::instrument(skip_all)] + pub async fn find_device_by_id( + &self, + device_id: &str, + ) -> Result, Error> { + let Some(user_id) = self.find_user_id_for_device(device_id).await? else { debug!("No device found with ID: {}", device_id); return Ok(None); }; - self.get_device_data(user_id, device_id).await }