Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F32911598
D7534.1768206206.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
19 KB
Referenced Files
None
Subscribers
None
D7534.1768206206.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D7534: [identity] register user finish
Attached
Detach File
Event Timeline
Log In to Comment