Page MenuHomePhorge

D7534.1768206206.diff
No OneTemporary

Size
19 KB
Referenced Files
None
Subscribers
None

D7534.1768206206.diff

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
@@ -23,6 +23,7 @@
database::{DatabaseClient, Error as DBError, KeyPayload},
id::generate_uuid,
nonce::generate_nonce_data,
+ token::AccessTokenData,
};
use aws_sdk_dynamodb::Error as DynamoDBError;
pub use client_proto::identity_client_service_server::{
@@ -40,16 +41,16 @@
#[derive(Clone)]
pub struct UserRegistrationInfo {
- username: String,
- device_id_key: String,
- key_payload: String,
- key_payload_signature: String,
- identity_prekey: String,
- identity_prekey_signature: String,
- identity_onetime_keys: Vec<String>,
- notif_prekey: String,
- notif_prekey_signature: String,
- notif_onetime_keys: Vec<String>,
+ pub username: String,
+ pub device_id_key: String,
+ pub key_payload: String,
+ pub key_payload_signature: String,
+ pub identity_prekey: String,
+ pub identity_prekey_signature: String,
+ pub identity_onetime_keys: Vec<String>,
+ pub notif_prekey: String,
+ pub notif_prekey_signature: String,
+ pub notif_onetime_keys: Vec<String>,
}
#[derive(derive_more::Constructor)]
@@ -144,9 +145,51 @@
async fn register_password_user_finish(
&self,
- _request: tonic::Request<RegistrationFinishRequest>,
+ request: tonic::Request<RegistrationFinishRequest>,
) -> Result<tonic::Response<RegistrationFinishResponse>, tonic::Status> {
- unimplemented!();
+ let message = request.into_inner();
+
+ if let Some(WorkflowInProgress::Registration(state)) =
+ self.cache.get(&message.session_id)
+ {
+ self.cache.invalidate(&message.session_id).await;
+
+ let server_registration = comm_opaque2::server::Registration::new();
+ let password_file = server_registration
+ .finish(&message.opaque_registration_upload)
+ .map_err(comm_opaque2::grpc::protocol_error_to_grpc_status)?;
+
+ let device_id = state.device_id_key.clone();
+ let user_id = self
+ .client
+ .add_user_to_users_table(state, password_file)
+ .await
+ .map_err(handle_db_error)?;
+
+ // Create access token
+ let token = AccessTokenData::new(
+ message.session_id,
+ device_id,
+ crate::token::AuthType::Password,
+ &mut OsRng,
+ );
+
+ let access_token = token.access_token.clone();
+
+ self
+ .client
+ .put_access_token_data(token)
+ .await
+ .map_err(handle_db_error)?;
+
+ let response = RegistrationFinishResponse {
+ user_id,
+ access_token,
+ };
+ Ok(Response::new(response))
+ } else {
+ Err(tonic::Status::not_found("session not found"))
+ }
}
async fn update_user_password_start(
diff --git a/services/identity/src/constants.rs b/services/identity/src/constants.rs
--- a/services/identity/src/constants.rs
+++ b/services/identity/src/constants.rs
@@ -8,74 +8,65 @@
// DynamoDB
// User table information, supporting opaque_ke 2.0 and X3DH information
-pub mod opaque2 {
- // Users can sign in either through username+password or Eth wallet.
- //
- // This structure should be aligned with the messages defined in
- // shared/protos/identity_client.proto
- //
- // Structure for a user should be:
- // {
- // userID: String,
- // opaqueRegistrationData: Option<String>,
- // username: Option<String>,
- // walletAddress: Option<String>,
- // devices: HashMap<String, Device>
- // }
- //
- // A device is defined as:
- // {
- // deviceType: String, # client or keyserver
- // keyPayload: String,
- // identityPreKey: String,
- // identityPreKeySignature: String,
- // identityOneTimeKeys: Vec<String>,
- // notifPreKey: String,
- // notifPreKeySignature: String,
- // notifOneTimeKeys: Vec<String>,
- // }
- // }
- //
- // Additional context:
- // "devices" uses the signing public identity key of the device as a key for the devices map
- // "keyPayload" is a JSON encoded string containing identity and notif keys (both signature and verification)
- // if "deviceType" == "keyserver", then the device will not have any notif key information
-
- pub const USERS_TABLE: &str = "identity-users-opaque2";
- pub const USERS_TABLE_PARTITION_KEY: &str = "userID";
- pub const USERS_TABLE_REGISTRATION_ATTRIBUTE: &str = "opaqueRegistrationData";
- pub const USERS_TABLE_USERNAME_ATTRIBUTE: &str = "username";
- pub const USERS_TABLE_DEVICES_ATTRIBUTE: &str = "devices";
- pub const USERS_TABLE_DEVICES_MAP_KEY_PAYLOAD_ATTRIBUTE_NAME: &str =
- "keyPayload";
- pub const USERS_TABLE_DEVICES_MAP_IDENTITY_PREKEY_ATTRIBUTE_NAME: &str =
- "identityPreKey";
- pub const USERS_TABLE_DEVICES_MAP_IDENTITY_PREKEY_SIGNATURE_ATTRIBUTE_NAME:
- &str = "identityPreKeySignature";
- pub const USERS_TABLE_DEVICES_MAP_IDENTITY_ONETIME_KEYS_ATTRIBUTE_NAME: &str =
- "identityOneTimeKeys";
- pub const USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_ATTRIBUTE_NAME: &str =
- "preKey";
- pub const USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_SIGNATURE_ATTRIBUTE_NAME:
- &str = "preKeySignature";
- pub const USERS_TABLE_DEVICES_MAP_NOTIF_ONETIME_KEYS_ATTRIBUTE_NAME: &str =
- "notifOneTimeKeys";
- pub const USERS_TABLE_WALLET_ADDRESS_ATTRIBUTE: &str = "walletAddress";
- pub const USERS_TABLE_USERNAME_INDEX: &str = "username-index";
- pub const USERS_TABLE_WALLET_ADDRESS_INDEX: &str = "walletAddress-index";
-}
+
+// Users can sign in either through username+password or Eth wallet.
+//
+// This structure should be aligned with the messages defined in
+// shared/protos/identity_client.proto
+//
+// Structure for a user should be:
+// {
+// userID: String,
+// opaqueRegistrationData: Option<String>,
+// username: Option<String>,
+// walletAddress: Option<String>,
+// devices: HashMap<String, Device>
+// }
+//
+// A device is defined as:
+// {
+// deviceType: String, # client or keyserver
+// keyPayload: String,
+// keyPayloadSignature: String,
+// identityPreKey: String,
+// identityPreKeySignature: String,
+// identityOneTimeKeys: Vec<String>,
+// notifPreKey: String,
+// notifPreKeySignature: String,
+// notifOneTimeKeys: Vec<String>,
+// }
+// }
+//
+// Additional context:
+// "devices" uses the signing public identity key of the device as a key for the devices map
+// "keyPayload" is a JSON encoded string containing identity and notif keys (both signature and verification)
+// if "deviceType" == "keyserver", then the device will not have any notif key information
pub const USERS_TABLE: &str = "identity-users";
pub const USERS_TABLE_PARTITION_KEY: &str = "userID";
-pub const USERS_TABLE_REGISTRATION_ATTRIBUTE: &str = "pakeRegistrationData";
+pub const USERS_TABLE_REGISTRATION_ATTRIBUTE: &str = "opaqueRegistrationData";
pub const USERS_TABLE_USERNAME_ATTRIBUTE: &str = "username";
pub const USERS_TABLE_DEVICES_ATTRIBUTE: &str = "devices";
-pub const USERS_TABLE_DEVICE_ATTRIBUTE_NAME: &str = "device";
-pub const USERS_TABLE_DEVICES_MAP_ATTRIBUTE_NAME: &str = "signingPublicKey";
+pub const USERS_TABLE_DEVICES_MAP_DEVICE_TYPE_ATTRIBUTE_NAME: &str =
+ "deviceType";
+pub const USERS_TABLE_DEVICES_MAP_KEY_PAYLOAD_ATTRIBUTE_NAME: &str =
+ "keyPayload";
+pub const USERS_TABLE_DEVICES_MAP_KEY_PAYLOAD_SIGNATURE_ATTRIBUTE_NAME: &str =
+ "keyPayloadSignature";
+pub const USERS_TABLE_DEVICES_MAP_IDENTITY_PREKEY_ATTRIBUTE_NAME: &str =
+ "identityPreKey";
+pub const USERS_TABLE_DEVICES_MAP_IDENTITY_PREKEY_SIGNATURE_ATTRIBUTE_NAME:
+ &str = "identityPreKeySignature";
+pub const USERS_TABLE_DEVICES_MAP_IDENTITY_ONETIME_KEYS_ATTRIBUTE_NAME: &str =
+ "identityOneTimeKeys";
+pub const USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_ATTRIBUTE_NAME: &str = "preKey";
+pub const USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_SIGNATURE_ATTRIBUTE_NAME: &str =
+ "preKeySignature";
+pub const USERS_TABLE_DEVICES_MAP_NOTIF_ONETIME_KEYS_ATTRIBUTE_NAME: &str =
+ "notifOneTimeKeys";
pub const USERS_TABLE_WALLET_ADDRESS_ATTRIBUTE: &str = "walletAddress";
pub const USERS_TABLE_USERNAME_INDEX: &str = "username-index";
pub const USERS_TABLE_WALLET_ADDRESS_INDEX: &str = "walletAddress-index";
-pub const USERS_TABLE_INITIALIZATION_INFO: &str = "initializationInfo";
pub const ACCESS_TOKEN_TABLE: &str = "identity-tokens";
pub const ACCESS_TOKEN_TABLE_PARTITION_KEY: &str = "userID";
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
@@ -6,7 +6,7 @@
use aws_config::SdkConfig;
use aws_sdk_dynamodb::model::AttributeValue;
use aws_sdk_dynamodb::output::{
- DeleteItemOutput, GetItemOutput, PutItemOutput, QueryOutput, UpdateItemOutput,
+ DeleteItemOutput, GetItemOutput, PutItemOutput, QueryOutput,
};
use aws_sdk_dynamodb::types::Blob;
use aws_sdk_dynamodb::{Client, Error as DynamoDBError};
@@ -15,6 +15,7 @@
use serde::{Deserialize, Serialize};
use tracing::{debug, error, info, warn};
+use crate::client_service::UserRegistrationInfo;
use crate::config::CONFIG;
use crate::constants::{
ACCESS_TOKEN_SORT_KEY, ACCESS_TOKEN_TABLE,
@@ -22,12 +23,21 @@
ACCESS_TOKEN_TABLE_PARTITION_KEY, ACCESS_TOKEN_TABLE_TOKEN_ATTRIBUTE,
ACCESS_TOKEN_TABLE_VALID_ATTRIBUTE, NONCE_TABLE,
NONCE_TABLE_CREATED_ATTRIBUTE, NONCE_TABLE_PARTITION_KEY, USERS_TABLE,
- USERS_TABLE_DEVICES_ATTRIBUTE, USERS_TABLE_DEVICES_MAP_ATTRIBUTE_NAME,
- USERS_TABLE_DEVICE_ATTRIBUTE_NAME, USERS_TABLE_INITIALIZATION_INFO,
+ USERS_TABLE_DEVICES_ATTRIBUTE,
+ USERS_TABLE_DEVICES_MAP_DEVICE_TYPE_ATTRIBUTE_NAME,
+ USERS_TABLE_DEVICES_MAP_IDENTITY_ONETIME_KEYS_ATTRIBUTE_NAME,
+ USERS_TABLE_DEVICES_MAP_IDENTITY_PREKEY_ATTRIBUTE_NAME,
+ USERS_TABLE_DEVICES_MAP_IDENTITY_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_ONETIME_KEYS_ATTRIBUTE_NAME,
+ USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_ATTRIBUTE_NAME,
+ USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_SIGNATURE_ATTRIBUTE_NAME,
USERS_TABLE_PARTITION_KEY, USERS_TABLE_REGISTRATION_ATTRIBUTE,
USERS_TABLE_USERNAME_ATTRIBUTE, USERS_TABLE_USERNAME_INDEX,
USERS_TABLE_WALLET_ADDRESS_ATTRIBUTE, USERS_TABLE_WALLET_ADDRESS_INDEX,
};
+use crate::id::generate_uuid;
use crate::nonce::NonceData;
use crate::token::{AccessTokenData, AuthType};
use comm_opaque::Cipher;
@@ -55,6 +65,20 @@
}
}
+pub enum Device {
+ Client,
+ Keyserver,
+}
+
+impl Display for Device {
+ fn fmt(&self, f: &mut Formatter) -> FmtResult {
+ match self {
+ Device::Client => write!(f, "client"),
+ Device::Keyserver => write!(f, "keyserver"),
+ }
+ }
+}
+
#[derive(Clone)]
pub struct DatabaseClient {
client: Arc<Client>,
@@ -111,148 +135,87 @@
}
}
- pub async fn get_session_initialization_info(
- &self,
- user_id: &str,
- ) -> Result<Option<HashMap<String, HashMap<String, String>>>, Error> {
- match self.get_item_from_users_table(user_id).await {
- Ok(GetItemOutput {
- item: Some(mut item),
- ..
- }) => parse_devices_attribute(item.remove(USERS_TABLE_DEVICES_ATTRIBUTE))
- .map(Some)
- .map_err(Error::Attribute),
- Ok(_) => {
- info!("No item found for user {} in users table", user_id);
- Ok(None)
- }
- Err(e) => {
- error!(
- "DynamoDB client failed to get session initialization info for user {}: {}",
- user_id, e
- );
- Err(e)
- }
- }
- }
-
- pub async fn update_users_table(
- &self,
- user_id: String,
- signing_public_key: Option<String>,
- registration: Option<ServerRegistration<Cipher>>,
- username: Option<String>,
- session_initialization_info: Option<&HashMap<String, String>>,
- ) -> Result<UpdateItemOutput, Error> {
- let mut update_expression_parts = Vec::new();
- let mut expression_attribute_names = HashMap::new();
- let mut expression_attribute_values = HashMap::new();
- if let Some(reg) = registration {
- update_expression_parts
- .push(format!("{} = :r", USERS_TABLE_REGISTRATION_ATTRIBUTE));
- expression_attribute_values.insert(
- ":r".to_string(),
- AttributeValue::B(Blob::new(reg.serialize())),
- );
- };
- if let Some(username) = username {
- update_expression_parts
- .push(format!("{} = :u", USERS_TABLE_USERNAME_ATTRIBUTE));
- expression_attribute_values
- .insert(":u".to_string(), AttributeValue::S(username));
- };
- if let Some(public_key) = signing_public_key {
- let device_info = match session_initialization_info {
- Some(info) => info
- .iter()
- .map(|(k, v)| (k.to_string(), AttributeValue::S(v.to_string())))
- .collect(),
- None => HashMap::new(),
- };
-
- // How we construct the update expression will depend on whether the user
- // already exists or not
- if let GetItemOutput { item: Some(_), .. } =
- self.get_item_from_users_table(&user_id).await?
- {
- update_expression_parts.push(format!(
- "{}.#{} = :k",
- USERS_TABLE_DEVICES_ATTRIBUTE, USERS_TABLE_DEVICES_MAP_ATTRIBUTE_NAME,
- ));
- expression_attribute_names.insert(
- format!("#{}", USERS_TABLE_DEVICES_MAP_ATTRIBUTE_NAME),
- public_key,
- );
- expression_attribute_values
- .insert(":k".to_string(), AttributeValue::M(device_info));
- } else {
- update_expression_parts
- .push(format!("{} = :k", USERS_TABLE_DEVICES_ATTRIBUTE));
- let mut devices = HashMap::new();
- devices.insert(public_key, AttributeValue::M(device_info));
- expression_attribute_values
- .insert(":k".to_string(), AttributeValue::M(devices));
- };
- };
-
- self
- .client
- .update_item()
- .table_name(USERS_TABLE)
- .key(USERS_TABLE_PARTITION_KEY, AttributeValue::S(user_id))
- .update_expression(format!("SET {}", update_expression_parts.join(",")))
- .set_expression_attribute_names(
- if expression_attribute_names.is_empty() {
- None
- } else {
- Some(expression_attribute_names)
- },
- )
- .set_expression_attribute_values(
- if expression_attribute_values.is_empty() {
- None
- } else {
- Some(expression_attribute_values)
- },
- )
- .send()
- .await
- .map_err(|e| Error::AwsSdk(e.into()))
- }
-
pub async fn add_user_to_users_table(
&self,
- user_id: String,
- registration: ServerRegistration<Cipher>,
- username: String,
- signing_public_key: String,
- session_initialization_info: &HashMap<String, String>,
- ) -> Result<PutItemOutput, Error> {
- let device_info: HashMap<String, AttributeValue> =
- session_initialization_info
- .iter()
- .map(|(k, v)| (k.to_string(), AttributeValue::S(v.to_string())))
- .collect();
+ registration_state: UserRegistrationInfo,
+ password_file: Vec<u8>,
+ ) -> Result<String, Error> {
+ let user_id = generate_uuid();
+ let device_info = HashMap::from([
+ (
+ USERS_TABLE_DEVICES_MAP_DEVICE_TYPE_ATTRIBUTE_NAME.to_string(),
+ AttributeValue::S(Device::Client.to_string()),
+ ),
+ (
+ USERS_TABLE_DEVICES_MAP_KEY_PAYLOAD_ATTRIBUTE_NAME.to_string(),
+ AttributeValue::S(registration_state.key_payload),
+ ),
+ (
+ USERS_TABLE_DEVICES_MAP_KEY_PAYLOAD_SIGNATURE_ATTRIBUTE_NAME
+ .to_string(),
+ AttributeValue::S(registration_state.key_payload_signature),
+ ),
+ (
+ USERS_TABLE_DEVICES_MAP_IDENTITY_PREKEY_ATTRIBUTE_NAME.to_string(),
+ AttributeValue::S(registration_state.identity_prekey),
+ ),
+ (
+ USERS_TABLE_DEVICES_MAP_IDENTITY_PREKEY_SIGNATURE_ATTRIBUTE_NAME
+ .to_string(),
+ AttributeValue::S(registration_state.identity_prekey_signature),
+ ),
+ (
+ USERS_TABLE_DEVICES_MAP_IDENTITY_ONETIME_KEYS_ATTRIBUTE_NAME
+ .to_string(),
+ AttributeValue::L(
+ registration_state
+ .identity_onetime_keys
+ .into_iter()
+ .map(AttributeValue::S)
+ .collect(),
+ ),
+ ),
+ (
+ USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_ATTRIBUTE_NAME.to_string(),
+ AttributeValue::S(registration_state.notif_prekey),
+ ),
+ (
+ USERS_TABLE_DEVICES_MAP_NOTIF_PREKEY_SIGNATURE_ATTRIBUTE_NAME
+ .to_string(),
+ AttributeValue::S(registration_state.notif_prekey_signature),
+ ),
+ (
+ USERS_TABLE_DEVICES_MAP_NOTIF_ONETIME_KEYS_ATTRIBUTE_NAME.to_string(),
+ AttributeValue::L(
+ registration_state
+ .notif_onetime_keys
+ .into_iter()
+ .map(AttributeValue::S)
+ .collect(),
+ ),
+ ),
+ ]);
+ let devices = HashMap::from([(
+ registration_state.device_id_key,
+ AttributeValue::M(device_info),
+ )]);
- let item = HashMap::from([
+ let user = HashMap::from([
(
USERS_TABLE_PARTITION_KEY.to_string(),
- AttributeValue::S(user_id),
+ AttributeValue::S(user_id.clone()),
),
(
USERS_TABLE_USERNAME_ATTRIBUTE.to_string(),
- AttributeValue::S(username),
+ AttributeValue::S(registration_state.username),
),
(
- USERS_TABLE_REGISTRATION_ATTRIBUTE.to_string(),
- AttributeValue::B(Blob::new(registration.serialize())),
+ USERS_TABLE_DEVICES_ATTRIBUTE.to_string(),
+ AttributeValue::M(devices),
),
(
- USERS_TABLE_DEVICES_ATTRIBUTE.to_string(),
- AttributeValue::M(HashMap::from([(
- signing_public_key,
- AttributeValue::M(device_info),
- )])),
+ USERS_TABLE_REGISTRATION_ATTRIBUTE.to_string(),
+ AttributeValue::B(Blob::new(password_file)),
),
]);
@@ -260,12 +223,13 @@
.client
.put_item()
.table_name(USERS_TABLE)
- .set_item(Some(item))
+ .set_item(Some(user))
.send()
.await
- .map_err(|e| Error::AwsSdk(e.into()))
- }
+ .map_err(|e| Error::AwsSdk(e.into()))?;
+ Ok(user_id)
+ }
pub async fn delete_user(
&self,
user_id: String,
@@ -714,36 +678,6 @@
}
}
-fn parse_devices_attribute(
- attribute: Option<AttributeValue>,
-) -> Result<HashMap<String, HashMap<String, String>>, DBItemError> {
- let mut devices = HashMap::new();
- let ddb_devices =
- parse_map_attribute(USERS_TABLE_DEVICES_ATTRIBUTE, attribute)?;
-
- for (signing_public_key, session_initialization_info) in ddb_devices {
- let session_initialization_info_map = parse_map_attribute(
- USERS_TABLE_DEVICE_ATTRIBUTE_NAME,
- Some(session_initialization_info),
- )?;
- let mut inner_hash_map = HashMap::new();
- for (initialization_component_name, initialization_component_value) in
- session_initialization_info_map
- {
- let initialization_piece_value_string = parse_string_attribute(
- USERS_TABLE_INITIALIZATION_INFO,
- Some(initialization_component_value),
- )?;
- inner_hash_map.insert(
- initialization_component_name,
- initialization_piece_value_string,
- );
- }
- devices.insert(signing_public_key, inner_hash_map);
- }
- Ok(devices)
-}
-
fn parse_map_attribute(
attribute_name: &'static str,
attribute_value: Option<AttributeValue>,
diff --git a/shared/protos/identity_client.proto b/shared/protos/identity_client.proto
--- a/shared/protos/identity_client.proto
+++ b/shared/protos/identity_client.proto
@@ -132,6 +132,8 @@
}
message RegistrationFinishResponse {
+ // Unique identifier for newly registered user
+ string userID = 1;
// After successful unpacking of user credentials, return token
string accessToken = 2;
}

File Metadata

Mime Type
text/plain
Expires
Mon, Jan 12, 8:23 AM (6 h, 49 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5922433
Default Alt Text
D7534.1768206206.diff (19 KB)

Event Timeline