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
@@ -7,6 +7,7 @@
 use comm_opaque2::grpc::protocol_error_to_grpc_status;
 use moka::future::Cache;
 use rand::rngs::OsRng;
+use siwe::eip55;
 use tonic::Response;
 use tracing::{debug, error};
 
@@ -587,11 +588,27 @@
   ) -> Result<tonic::Response<WalletLoginResponse>, tonic::Status> {
     let message = request.into_inner();
 
-    let wallet_address = parse_and_verify_siwe_message(
+    let parsed_message = parse_and_verify_siwe_message(
       &message.siwe_message,
       &message.siwe_signature,
     )?;
 
+    match self
+      .client
+      .get_nonce_from_nonces_table(&parsed_message.nonce)
+      .await
+      .map_err(handle_db_error)?
+    {
+      None => return Err(tonic::Status::invalid_argument("invalid nonce")),
+      Some(_) => self
+        .client
+        .remove_nonce_from_nonces_table(&parsed_message.nonce)
+        .await
+        .map_err(handle_db_error)?,
+    };
+
+    let wallet_address = eip55(&parsed_message.address);
+
     let (flattened_device_key_upload, social_proof) =
       if let client_proto::WalletLoginRequest {
         siwe_message: _,
diff --git a/services/identity/src/siwe.rs b/services/identity/src/siwe.rs
--- a/services/identity/src/siwe.rs
+++ b/services/identity/src/siwe.rs
@@ -1,12 +1,12 @@
 use chrono::Utc;
-use siwe::{eip55, Message};
+use siwe::Message;
 use tonic::Status;
 use tracing::error;
 
 pub fn parse_and_verify_siwe_message(
   siwe_message: &str,
   siwe_signature: &str,
-) -> Result<String, Status> {
+) -> Result<Message, Status> {
   let siwe_message: Message = siwe_message.parse().map_err(|e| {
     error!("Failed to parse SIWE message: {}", e);
     Status::invalid_argument("invalid message")
@@ -30,5 +30,5 @@
       Status::unauthenticated("message not authenticated")
     })?;
 
-  Ok(eip55(&siwe_message.address))
+  Ok(siwe_message)
 }