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
@@ -1,49 +1,49 @@
-pub mod client_proto {
-  tonic::include_proto!("identity.client");
-}
-
+// Standard library imports
 use std::str::FromStr;
 
-use crate::database::{self, Device};
-use crate::error::Error as DBError;
-use crate::{
-  client_service::client_proto::{
-    AddReservedUsernamesRequest, DeleteUserRequest, Empty,
-    GenerateNonceResponse, InboundKeysForUserRequest,
-    InboundKeysForUserResponse, LogoutRequest, OpaqueLoginFinishRequest,
-    OpaqueLoginFinishResponse, OpaqueLoginStartRequest,
-    OpaqueLoginStartResponse, OutboundKeysForUserRequest,
-    OutboundKeysForUserResponse, RefreshUserPreKeysRequest,
-    RegistrationFinishRequest, RegistrationFinishResponse,
-    RegistrationStartRequest, RegistrationStartResponse,
-    RemoveReservedUsernameRequest, ReservedRegistrationStartRequest,
-    UpdateUserPasswordFinishRequest, UpdateUserPasswordStartRequest,
-    UpdateUserPasswordStartResponse, UploadOneTimeKeysRequest,
-    VerifyUserAccessTokenRequest, VerifyUserAccessTokenResponse,
-    WalletLoginRequest, WalletLoginResponse,
-  },
-  config::CONFIG,
-  database::{DatabaseClient, KeyPayload},
-  id::generate_uuid,
-  nonce::generate_nonce_data,
-  reserved_users::{
-    validate_add_reserved_usernames_message,
-    validate_remove_reserved_username_message,
-    validate_signed_account_ownership_message,
-  },
-  siwe::parse_and_verify_siwe_message,
-  token::{AccessTokenData, AuthType},
-};
+// External crate imports
 use aws_sdk_dynamodb::Error as DynamoDBError;
-pub use client_proto::identity_client_service_server::{
-  IdentityClientService, IdentityClientServiceServer,
-};
 use comm_opaque2::grpc::protocol_error_to_grpc_status;
 use moka::future::Cache;
 use rand::rngs::OsRng;
 use tonic::Response;
 use tracing::{debug, error};
 
+// Workspace crate imports
+use crate::client_service::client_proto::{
+  AddReservedUsernamesRequest, DeleteUserRequest, Empty, GenerateNonceResponse,
+  InboundKeysForUserRequest, InboundKeysForUserResponse, LogoutRequest,
+  OpaqueLoginFinishRequest, OpaqueLoginFinishResponse, OpaqueLoginStartRequest,
+  OpaqueLoginStartResponse, OutboundKeysForUserRequest,
+  OutboundKeysForUserResponse, RefreshUserPreKeysRequest,
+  RegistrationFinishRequest, RegistrationFinishResponse,
+  RegistrationStartRequest, RegistrationStartResponse,
+  RemoveReservedUsernameRequest, ReservedRegistrationStartRequest,
+  UpdateUserPasswordFinishRequest, UpdateUserPasswordStartRequest,
+  UpdateUserPasswordStartResponse, UploadOneTimeKeysRequest,
+  VerifyUserAccessTokenRequest, VerifyUserAccessTokenResponse,
+  WalletLoginRequest, WalletLoginResponse,
+};
+use crate::config::CONFIG;
+use crate::database::{DatabaseClient, Device, KeyPayload};
+use crate::error::Error as DBError;
+use crate::id::generate_uuid;
+use crate::nonce::generate_nonce_data;
+use crate::reserved_users::{
+  validate_add_reserved_usernames_message,
+  validate_remove_reserved_username_message,
+  validate_signed_account_ownership_message,
+};
+use crate::siwe::parse_and_verify_siwe_message;
+use crate::token::{AccessTokenData, AuthType};
+pub use client_proto::identity_client_service_server::{
+  IdentityClientService, IdentityClientServiceServer,
+};
+
+pub mod client_proto {
+  tonic::include_proto!("identity.client");
+}
+
 #[derive(Clone)]
 pub enum WorkflowInProgress {
   Registration(Box<UserRegistrationInfo>),
@@ -80,7 +80,7 @@
   pub notif_prekey: String,
   pub notif_prekey_signature: String,
   pub notif_one_time_keys: Vec<String>,
-  pub device_type: database::Device,
+  pub device_type: Device,
 }
 
 #[derive(derive_more::Constructor)]
diff --git a/services/identity/src/database.rs b/services/identity/src/database.rs
--- a/services/identity/src/database.rs
+++ b/services/identity/src/database.rs
@@ -1277,7 +1277,7 @@
 }
 
 type AttributeName = String;
-type DeviceKeys = HashMap<String, String>;
+pub type DeviceKeys = HashMap<String, String>;
 type Devices = HashMap<String, DeviceKeys>;
 
 fn create_simple_primary_key(
diff --git a/services/identity/src/grpc_utils.rs b/services/identity/src/grpc_utils.rs
new file mode 100644
--- /dev/null
+++ b/services/identity/src/grpc_utils.rs
@@ -0,0 +1,86 @@
+use std::collections::HashMap;
+
+use tonic::Status;
+use tracing::error;
+
+use crate::{
+  client_service::client_proto::{IdentityKeyInfo, InboundKeyInfo, PreKey},
+  constants::{
+    USERS_TABLE_DEVICES_MAP_CONTENT_PREKEY_ATTRIBUTE_NAME,
+    USERS_TABLE_DEVICES_MAP_CONTENT_PREKEY_SIGNATURE_ATTRIBUTE_NAME,
+    USERS_TABLE_DEVICES_MAP_KEY_PAYLOAD_ATTRIBUTE_NAME,
+    USERS_TABLE_DEVICES_MAP_KEY_PAYLOAD_SIGNATURE_ATTRIBUTE_NAME,
+    USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_ATTRIBUTE_NAME,
+    USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_SIGNATURE_ATTRIBUTE_NAME,
+    USERS_TABLE_DEVICES_MAP_SOCIAL_PROOF_ATTRIBUTE_NAME,
+  },
+  database::DeviceKeys,
+  token::AuthType,
+};
+
+struct DeviceInfoWithAuth<'a> {
+  device_info: HashMap<String, String>,
+  auth_type: &'a AuthType,
+}
+
+impl<'a> TryFrom<DeviceInfoWithAuth<'_>> for InboundKeyInfo {
+  type Error = tonic::Status;
+
+  fn try_from(data: DeviceInfoWithAuth) -> Result<Self, Self::Error> {
+    let mut device_info = data.device_info;
+
+    let payload = extract_key(
+      &mut device_info,
+      USERS_TABLE_DEVICES_MAP_KEY_PAYLOAD_ATTRIBUTE_NAME,
+    )?;
+    let payload_signature = extract_key(
+      &mut device_info,
+      USERS_TABLE_DEVICES_MAP_KEY_PAYLOAD_SIGNATURE_ATTRIBUTE_NAME,
+    )?;
+
+    let social_proof =
+      device_info.remove(USERS_TABLE_DEVICES_MAP_SOCIAL_PROOF_ATTRIBUTE_NAME);
+    if social_proof.is_none() && data.auth_type == &AuthType::Wallet {
+      error!("Social proof missing for wallet user");
+      return Err(tonic::Status::failed_precondition(
+        "Database item malformed",
+      ));
+    }
+
+    let identity_info = IdentityKeyInfo {
+      payload,
+      payload_signature,
+      social_proof,
+    };
+
+    let mut create_prekey =
+      |key_attr, signature_attr| -> Result<PreKey, Status> {
+        Ok(PreKey {
+          pre_key: extract_key(&mut device_info, key_attr)?,
+          pre_key_signature: extract_key(&mut device_info, signature_attr)?,
+        })
+      };
+
+    Ok(InboundKeyInfo {
+      identity_info: Some(identity_info),
+      content_prekey: Some(create_prekey(
+        USERS_TABLE_DEVICES_MAP_CONTENT_PREKEY_ATTRIBUTE_NAME,
+        USERS_TABLE_DEVICES_MAP_CONTENT_PREKEY_SIGNATURE_ATTRIBUTE_NAME,
+      )?),
+      notif_prekey: Some(create_prekey(
+        USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_ATTRIBUTE_NAME,
+        USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_SIGNATURE_ATTRIBUTE_NAME,
+      )?),
+    })
+  }
+}
+
+fn extract_key(
+  device_info: &mut DeviceKeys,
+  key: &str,
+) -> Result<String, Status> {
+  device_info.remove(key).ok_or_else(|| {
+    error!("{} missing from device info", key);
+    Status::failed_precondition("Database item malformed")
+  })
+}
diff --git a/services/identity/src/main.rs b/services/identity/src/main.rs
--- a/services/identity/src/main.rs
+++ b/services/identity/src/main.rs
@@ -12,6 +12,7 @@
 pub mod ddb_utils;
 pub mod error;
 mod grpc_services;
+mod grpc_utils;
 mod id;
 mod keygen;
 mod nonce;
diff --git a/services/identity/src/token.rs b/services/identity/src/token.rs
--- a/services/identity/src/token.rs
+++ b/services/identity/src/token.rs
@@ -6,7 +6,7 @@
 
 use crate::constants::ACCESS_TOKEN_LENGTH;
 
-#[derive(Clone)]
+#[derive(Clone, Eq, PartialEq)]
 pub enum AuthType {
   Password,
   Wallet,