diff --git a/keyserver/addons/rust-node-addon/src/identity_client/get_inbound_keys_for_user.rs b/keyserver/addons/rust-node-addon/src/identity_client/get_inbound_keys_for_user.rs --- a/keyserver/addons/rust-node-addon/src/identity_client/get_inbound_keys_for_user.rs +++ b/keyserver/addons/rust-node-addon/src/identity_client/get_inbound_keys_for_user.rs @@ -1,6 +1,4 @@ -use grpc_clients::identity::protos::authenticated::{ - identity::IdentityInfo, EthereumIdentity, Identity, InboundKeysForUserRequest, -}; +use grpc_clients::identity::protos::authenticated::InboundKeysForUserRequest; use super::*; @@ -36,19 +34,11 @@ .ok_or(Error::from_status(Status::GenericFailure))?, )?; - let (username, wallet_address) = match response.identity { - Some(Identity { - identity_info: Some(IdentityInfo::Username(u)), - }) => (Some(u), None), - Some(Identity { - identity_info: - Some(IdentityInfo::EthIdentity(EthereumIdentity { - wallet_address: w, - .. // We ignore the social proof for now - })), - }) => (None, Some(w)), - _ => (None, None), - }; + let identity = response + .identity + .ok_or_else(|| Error::from_status(Status::GenericFailure))?; + let username = Some(identity.username); + let wallet_address = identity.eth_identity.map(|eth| eth.wallet_address); let inbound_key_info_response = InboundKeyInfoResponse { payload: device_inbound_key_info.payload, diff --git a/services/commtest/tests/identity_integration_tests.rs b/services/commtest/tests/identity_integration_tests.rs --- a/services/commtest/tests/identity_integration_tests.rs +++ b/services/commtest/tests/identity_integration_tests.rs @@ -4,7 +4,7 @@ use commtest::service_addr; use grpc_clients::identity::{ get_auth_client, get_unauthenticated_client, - protos::auth::{identity::IdentityInfo, Identity, UserIdentityRequest}, + protos::auth::{Identity, UserIdentityRequest}, protos::unauthenticated::{ find_user_id_request::Identifier, FindUserIdRequest, }, @@ -68,7 +68,7 @@ matches!( response.identity, Some(Identity { - identity_info: Some(IdentityInfo::Username(username)) + username, .. }) if username == expected_username ), "username doesn't match" diff --git a/services/identity/src/grpc_services/authenticated.rs b/services/identity/src/grpc_services/authenticated.rs --- a/services/identity/src/grpc_services/authenticated.rs +++ b/services/identity/src/grpc_services/authenticated.rs @@ -15,9 +15,9 @@ use tracing::{debug, error, warn}; use super::protos::auth::{ - identity, identity_client_service_server::IdentityClientService, - GetDeviceListRequest, GetDeviceListResponse, Identity, InboundKeyInfo, - InboundKeysForUserRequest, InboundKeysForUserResponse, KeyserverKeysResponse, + identity_client_service_server::IdentityClientService, GetDeviceListRequest, + GetDeviceListResponse, InboundKeyInfo, InboundKeysForUserRequest, + InboundKeysForUserResponse, KeyserverKeysResponse, LinkFarcasterAccountRequest, OutboundKeyInfo, OutboundKeysForUserRequest, OutboundKeysForUserResponse, RefreshUserPrekeysRequest, UpdateDeviceListRequest, UpdateUserPasswordFinishRequest, @@ -145,8 +145,6 @@ &self, request: tonic::Request, ) -> Result, tonic::Status> { - use identity::IdentityInfo; - let message = request.into_inner(); let user_id = &message.user_id; @@ -169,13 +167,9 @@ .map_err(handle_db_error)? .ok_or_else(|| tonic::Status::not_found("user not found"))?; - let identity_info = IdentityInfo::try_from(identifier)?; - Ok(tonic::Response::new(InboundKeysForUserResponse { devices: transformed_devices, - identity: Some(Identity { - identity_info: Some(identity_info), - }), + identity: Some(identifier.try_into()?), })) } @@ -183,8 +177,6 @@ &self, request: Request, ) -> Result, Status> { - use identity::IdentityInfo; - let message = request.into_inner(); let identifier = self @@ -193,10 +185,6 @@ .await .map_err(handle_db_error)? .ok_or_else(|| tonic::Status::not_found("user not found"))?; - let identity_info = IdentityInfo::try_from(identifier)?; - let identity = Some(Identity { - identity_info: Some(identity_info), - }); let Some(keyserver_info) = self .db_client @@ -216,7 +204,7 @@ let response = Response::new(KeyserverKeysResponse { keyserver_info: Some(keyserver_info.into()), - identity, + identity: Some(identifier.try_into()?), primary_device_identity_info: Some(primary_device_keys.into()), }); @@ -465,8 +453,6 @@ &self, request: tonic::Request, ) -> Result, tonic::Status> { - use identity::IdentityInfo; - let message = request.into_inner(); let identifier = self .db_client @@ -475,13 +461,10 @@ .map_err(handle_db_error)? .ok_or_else(|| tonic::Status::not_found("user not found"))?; - let identity_info = IdentityInfo::try_from(identifier)?; - let identity = Some(Identity { - identity_info: Some(identity_info), - }); - - let response = Response::new(UserIdentityResponse { identity }); - return Ok(response); + let response = UserIdentityResponse { + identity: Some(identifier.try_into()?), + }; + return Ok(Response::new(response)); } } diff --git a/services/identity/src/grpc_utils.rs b/services/identity/src/grpc_utils.rs --- a/services/identity/src/grpc_utils.rs +++ b/services/identity/src/grpc_utils.rs @@ -2,14 +2,13 @@ use ed25519_dalek::{PublicKey, Signature, Verifier}; use serde::Deserialize; use tonic::Status; +use tracing::error; use crate::{ database::DeviceRow, ddb_utils::Identifier as DBIdentifier, grpc_services::protos::{ - auth::{ - identity::IdentityInfo, EthereumIdentity, InboundKeyInfo, OutboundKeyInfo, - }, + auth::{EthereumIdentity, Identity, InboundKeyInfo, OutboundKeyInfo}, unauth::{ DeviceKeyUpload, ExistingDeviceLoginRequest, OpaqueLoginStartRequest, RegistrationStartRequest, ReservedRegistrationStartRequest, @@ -17,6 +16,7 @@ WalletAuthRequest, }, }, + siwe::SocialProof, }; #[derive(Deserialize)] @@ -242,19 +242,32 @@ } } -impl TryFrom for IdentityInfo { +impl TryFrom for Identity { type Error = Status; fn try_from(value: DBIdentifier) -> Result { - match value { - DBIdentifier::Username(username) => Ok(IdentityInfo::Username(username)), + let identity = match value { + DBIdentifier::Username(username) => Identity { + username, + eth_identity: None, + }, DBIdentifier::WalletAddress(eth_identity) => { - Ok(IdentityInfo::EthIdentity(EthereumIdentity { - wallet_address: eth_identity.wallet_address, - social_proof: eth_identity.social_proof, - })) + let SocialProof { message, signature } = + eth_identity.social_proof.try_into().map_err(|err| { + error!("Failed to construct wallet identity: {err}"); + Status::internal("unexpected error") + })?; + Identity { + username: eth_identity.wallet_address.clone(), + eth_identity: Some(EthereumIdentity { + wallet_address: eth_identity.wallet_address, + siwe_message: message, + siwe_signature: signature, + }), + } } - } + }; + Ok(identity) } } 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,6 +1,6 @@ use chrono::Utc; use regex::Regex; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use siwe::Message; use tonic::Status; use tracing::error; @@ -40,10 +40,21 @@ ethereum_address_regex.is_match(candidate) } -#[derive(derive_more::Constructor, Serialize)] +#[derive(derive_more::Constructor, Serialize, Deserialize)] pub struct SocialProof { - message: String, - signature: String, + pub message: String, + pub signature: String, +} + +impl TryFrom for SocialProof { + type Error = crate::error::Error; + + fn try_from(value: String) -> Result { + serde_json::from_str(&value).map_err(|err| { + error!("Failed to deserialize social proof: {err}"); + err.into() + }) + } } #[cfg(test)] diff --git a/shared/protos/identity_auth.proto b/shared/protos/identity_auth.proto --- a/shared/protos/identity_auth.proto +++ b/shared/protos/identity_auth.proto @@ -80,14 +80,14 @@ message EthereumIdentity { string wallet_address = 1; - string social_proof = 2; + string siwe_message = 2; + string siwe_signature = 3; } message Identity { - oneof identity_info { - string username = 1; - EthereumIdentity eth_identity = 2; - } + // this is wallet address for Ethereum users + string username = 1; + optional EthereumIdentity eth_identity = 2; } // UploadOneTimeKeys diff --git a/web/protobufs/identity-auth-structs.cjs b/web/protobufs/identity-auth-structs.cjs --- a/web/protobufs/identity-auth-structs.cjs +++ b/web/protobufs/identity-auth-structs.cjs @@ -28,7 +28,6 @@ goog.exportSymbol('proto.identity.auth.GetDeviceListRequest', null, global); goog.exportSymbol('proto.identity.auth.GetDeviceListResponse', null, global); goog.exportSymbol('proto.identity.auth.Identity', null, global); -goog.exportSymbol('proto.identity.auth.Identity.IdentityInfoCase', null, global); goog.exportSymbol('proto.identity.auth.InboundKeyInfo', null, global); goog.exportSymbol('proto.identity.auth.InboundKeysForUserRequest', null, global); goog.exportSymbol('proto.identity.auth.InboundKeysForUserResponse', null, global); @@ -77,7 +76,7 @@ * @constructor */ proto.identity.auth.Identity = function(opt_data) { - jspb.Message.initialize(this, opt_data, 0, -1, null, proto.identity.auth.Identity.oneofGroups_); + jspb.Message.initialize(this, opt_data, 0, -1, null, null); }; goog.inherits(proto.identity.auth.Identity, jspb.Message); if (goog.DEBUG && !COMPILED) { @@ -498,7 +497,8 @@ proto.identity.auth.EthereumIdentity.toObject = function(includeInstance, msg) { var f, obj = { walletAddress: jspb.Message.getFieldWithDefault(msg, 1, ""), - socialProof: jspb.Message.getFieldWithDefault(msg, 2, "") + siweMessage: jspb.Message.getFieldWithDefault(msg, 2, ""), + siweSignature: jspb.Message.getFieldWithDefault(msg, 3, "") }; if (includeInstance) { @@ -541,7 +541,11 @@ break; case 2: var value = /** @type {string} */ (reader.readString()); - msg.setSocialProof(value); + msg.setSiweMessage(value); + break; + case 3: + var value = /** @type {string} */ (reader.readString()); + msg.setSiweSignature(value); break; default: reader.skipField(); @@ -579,13 +583,20 @@ f ); } - f = message.getSocialProof(); + f = message.getSiweMessage(); if (f.length > 0) { writer.writeString( 2, f ); } + f = message.getSiweSignature(); + if (f.length > 0) { + writer.writeString( + 3, + f + ); + } }; @@ -608,10 +619,10 @@ /** - * optional string social_proof = 2; + * optional string siwe_message = 2; * @return {string} */ -proto.identity.auth.EthereumIdentity.prototype.getSocialProof = function() { +proto.identity.auth.EthereumIdentity.prototype.getSiweMessage = function() { return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); }; @@ -620,40 +631,32 @@ * @param {string} value * @return {!proto.identity.auth.EthereumIdentity} returns this */ -proto.identity.auth.EthereumIdentity.prototype.setSocialProof = function(value) { +proto.identity.auth.EthereumIdentity.prototype.setSiweMessage = function(value) { return jspb.Message.setProto3StringField(this, 2, value); }; - -/** - * Oneof group definitions for this message. Each group defines the field - * numbers belonging to that group. When of these fields' value is set, all - * other fields in the group are cleared. During deserialization, if multiple - * fields are encountered for a group, only the last value seen will be kept. - * @private {!Array>} - * @const - */ -proto.identity.auth.Identity.oneofGroups_ = [[1,2]]; - /** - * @enum {number} + * optional string siwe_signature = 3; + * @return {string} */ -proto.identity.auth.Identity.IdentityInfoCase = { - IDENTITY_INFO_NOT_SET: 0, - USERNAME: 1, - ETH_IDENTITY: 2 +proto.identity.auth.EthereumIdentity.prototype.getSiweSignature = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); }; + /** - * @return {proto.identity.auth.Identity.IdentityInfoCase} + * @param {string} value + * @return {!proto.identity.auth.EthereumIdentity} returns this */ -proto.identity.auth.Identity.prototype.getIdentityInfoCase = function() { - return /** @type {proto.identity.auth.Identity.IdentityInfoCase} */(jspb.Message.computeOneofCase(this, proto.identity.auth.Identity.oneofGroups_[0])); +proto.identity.auth.EthereumIdentity.prototype.setSiweSignature = function(value) { + return jspb.Message.setProto3StringField(this, 3, value); }; + + if (jspb.Message.GENERATE_TO_OBJECT) { /** * Creates an object representation of this proto. @@ -759,8 +762,8 @@ */ proto.identity.auth.Identity.serializeBinaryToWriter = function(message, writer) { var f = undefined; - f = /** @type {string} */ (jspb.Message.getField(message, 1)); - if (f != null) { + f = message.getUsername(); + if (f.length > 0) { writer.writeString( 1, f @@ -791,25 +794,7 @@ * @return {!proto.identity.auth.Identity} returns this */ proto.identity.auth.Identity.prototype.setUsername = function(value) { - return jspb.Message.setOneofField(this, 1, proto.identity.auth.Identity.oneofGroups_[0], value); -}; - - -/** - * Clears the field making it undefined. - * @return {!proto.identity.auth.Identity} returns this - */ -proto.identity.auth.Identity.prototype.clearUsername = function() { - return jspb.Message.setOneofField(this, 1, proto.identity.auth.Identity.oneofGroups_[0], undefined); -}; - - -/** - * Returns whether this field is set. - * @return {boolean} - */ -proto.identity.auth.Identity.prototype.hasUsername = function() { - return jspb.Message.getField(this, 1) != null; + return jspb.Message.setProto3StringField(this, 1, value); }; @@ -828,7 +813,7 @@ * @return {!proto.identity.auth.Identity} returns this */ proto.identity.auth.Identity.prototype.setEthIdentity = function(value) { - return jspb.Message.setOneofWrapperField(this, 2, proto.identity.auth.Identity.oneofGroups_[0], value); + return jspb.Message.setWrapperField(this, 2, value); }; diff --git a/web/protobufs/identity-auth-structs.cjs.flow b/web/protobufs/identity-auth-structs.cjs.flow --- a/web/protobufs/identity-auth-structs.cjs.flow +++ b/web/protobufs/identity-auth-structs.cjs.flow @@ -13,8 +13,11 @@ getWalletAddress(): string; setWalletAddress(value: string): EthereumIdentity; - getSocialProof(): string; - setSocialProof(value: string): EthereumIdentity; + getSiweMessage(): string; + setSiweMessage(value: string): EthereumIdentity; + + getSiweSignature(): string; + setSiweSignature(value: string): EthereumIdentity; serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): EthereumIdentityObject; @@ -26,11 +29,10 @@ export type EthereumIdentityObject = { walletAddress: string, - socialProof: string, + siweMessage: string, + siweSignature: string, } -export type IdentityInfoCase = 0 | 1 | 2; - declare export class Identity extends Message { getUsername(): string; setUsername(value: string): Identity; @@ -40,8 +42,6 @@ hasEthIdentity(): boolean; clearEthIdentity(): Identity; - getIdentityInfoCase(): IdentityInfoCase; - serializeBinary(): Uint8Array; toObject(includeInstance?: boolean): IdentityObject; static toObject(includeInstance: boolean, msg: Identity): IdentityObject;