diff --git a/keyserver/addons/rust-node-addon/src/identity_client/register_user.rs b/keyserver/addons/rust-node-addon/src/identity_client/register_user.rs --- a/keyserver/addons/rust-node-addon/src/identity_client/register_user.rs +++ b/keyserver/addons/rust-node-addon/src/identity_client/register_user.rs @@ -49,6 +49,7 @@ device_key_upload: Some(device_key_upload), farcaster_id: None, initial_device_list: "".to_string(), + farcaster_dcs_token: None, }; // Finish OPAQUE registration and send final registration request diff --git a/lib/actions/user-actions.js b/lib/actions/user-actions.js --- a/lib/actions/user-actions.js +++ b/lib/actions/user-actions.js @@ -903,6 +903,7 @@ username: string, password: string, fid: ?string, + farcasterDCsToken: ?string, ) => Promise { const client = React.useContext(IdentityClientContext); const identityClient = client?.identityClient; @@ -914,8 +915,18 @@ const { markPrekeysAsPublished } = getConfig().olmAPI; return React.useCallback( - async (username: string, password: string, fid: ?string) => { - const response = await registerPasswordUser(username, password, fid); + async ( + username: string, + password: string, + fid: ?string, + farcasterDCsToken: ?string, + ) => { + const response = await registerPasswordUser( + username, + password, + fid, + farcasterDCsToken, + ); try { await markPrekeysAsPublished(); } catch (e) { @@ -951,12 +962,14 @@ siweMessage: string, siweSignature: string, fid: ?string, + farcasterDCsToken: ?string, ) => { const response = await registerWalletUser( walletAddress, siweMessage, siweSignature, fid, + farcasterDCsToken, ); try { await markPrekeysAsPublished(); diff --git a/lib/types/identity-service-types.js b/lib/types/identity-service-types.js --- a/lib/types/identity-service-types.js +++ b/lib/types/identity-service-types.js @@ -147,6 +147,7 @@ username: string, password: string, fid: ?string, + farcasterDCsToken: ?string, ) => Promise; // Users cannot register from web +registerReservedPasswordUser?: ( @@ -175,6 +176,7 @@ siweMessage: string, siweSignature: string, fid: ?string, + farcasterDCsToken: ?string, ) => Promise; +logInWalletUser: ( walletAddress: string, diff --git a/native/account/registration/registration-server-call.js b/native/account/registration/registration-server-call.js --- a/native/account/registration/registration-server-call.js +++ b/native/account/registration/registration-server-call.js @@ -92,6 +92,7 @@ accountSelection: UsernameAccountSelection, farcasterID: ?string, onAlertAcknowledged: ?() => mixed, + farcasterDCsToken: ?string, ) => { const identityRegisterPromise = (async () => { try { @@ -99,6 +100,7 @@ accountSelection.username, accountSelection.password, farcasterID, + farcasterDCsToken, ); } catch (e) { const messageForException = getMessageForException(e); @@ -212,6 +214,7 @@ accountSelection, farcasterID, onAlertAcknowledged, + farcasterDCsToken, ); } else { await identityRegisterEthereumAccount( diff --git a/native/cpp/CommonCpp/NativeModules/CommRustModule.h b/native/cpp/CommonCpp/NativeModules/CommRustModule.h --- a/native/cpp/CommonCpp/NativeModules/CommRustModule.h +++ b/native/cpp/CommonCpp/NativeModules/CommRustModule.h @@ -25,7 +25,8 @@ jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String farcasterID, - jsi::String initialDeviceList) override; + jsi::String initialDeviceList, + jsi::String farcasterDCsToken) override; virtual jsi::Value registerReservedPasswordUser( jsi::Runtime &rt, jsi::String username, @@ -66,7 +67,8 @@ jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String farcasterID, - jsi::String initialDeviceList) override; + jsi::String initialDeviceList, + jsi::String farcasterDCsToken) override; virtual jsi::Value logInWalletUser( jsi::Runtime &rt, jsi::String siweMessage, diff --git a/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp b/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp --- a/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp +++ b/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp @@ -44,7 +44,8 @@ jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String farcasterID, - jsi::String initialDeviceList) { + jsi::String initialDeviceList, + jsi::String farcasterDCsToken) { auto usernameRust = jsiStringToRustString(username, rt); auto passwordRust = jsiStringToRustString(password, rt); auto keyPayloadRust = jsiStringToRustString(keyPayload, rt); @@ -59,6 +60,7 @@ auto notifOneTimeKeysRust = jsiStringArrayToRustVec(notifOneTimeKeys, rt); auto farcasterIDRust = jsiStringToRustString(farcasterID, rt); auto initialDeviceListRust = jsiStringToRustString(initialDeviceList, rt); + auto farcasterDCsTokenRust = jsiStringToRustString(farcasterDCsToken, rt); return createPromiseAsJSIValue( rt, [=, this](jsi::Runtime &innerRt, std::shared_ptr promise) { @@ -79,7 +81,8 @@ notifOneTimeKeysRust, farcasterIDRust, initialDeviceListRust, - currentID); + currentID, + farcasterDCsTokenRust); } catch (const std::exception &e) { error = e.what(); }; @@ -218,7 +221,8 @@ jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String farcasterID, - jsi::String initialDeviceList) { + jsi::String initialDeviceList, + jsi::String farcasterDCsToken) { auto siweMessageRust = jsiStringToRustString(siweMessage, rt); auto siweSignatureRust = jsiStringToRustString(siweSignature, rt); auto keyPayloadRust = jsiStringToRustString(keyPayload, rt); @@ -233,6 +237,7 @@ auto notifOneTimeKeysRust = jsiStringArrayToRustVec(notifOneTimeKeys, rt); auto farcasterIDRust = jsiStringToRustString(farcasterID, rt); auto initialDeviceListRust = jsiStringToRustString(initialDeviceList, rt); + auto farcasterDCsTokenRust = jsiStringToRustString(farcasterDCsToken, rt); return createPromiseAsJSIValue( rt, [=, this](jsi::Runtime &innerRt, std::shared_ptr promise) { @@ -253,7 +258,8 @@ notifOneTimeKeysRust, farcasterIDRust, initialDeviceListRust, - currentID); + currentID, + farcasterDCsTokenRust); } catch (const std::exception &e) { error = e.what(); }; diff --git a/native/cpp/CommonCpp/_generated/rustJSI-generated.cpp b/native/cpp/CommonCpp/_generated/rustJSI-generated.cpp --- a/native/cpp/CommonCpp/_generated/rustJSI-generated.cpp +++ b/native/cpp/CommonCpp/_generated/rustJSI-generated.cpp @@ -16,7 +16,7 @@ return static_cast(&turboModule)->generateNonce(rt); } static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_registerPasswordUser(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { - return static_cast(&turboModule)->registerPasswordUser(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt), args[4].asString(rt), args[5].asString(rt), args[6].asString(rt), args[7].asString(rt), args[8].asObject(rt).asArray(rt), args[9].asObject(rt).asArray(rt), args[10].asString(rt), args[11].asString(rt)); + return static_cast(&turboModule)->registerPasswordUser(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt), args[4].asString(rt), args[5].asString(rt), args[6].asString(rt), args[7].asString(rt), args[8].asObject(rt).asArray(rt), args[9].asObject(rt).asArray(rt), args[10].asString(rt), args[11].asString(rt), args[12].asString(rt)); } static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_registerReservedPasswordUser(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->registerReservedPasswordUser(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt), args[4].asString(rt), args[5].asString(rt), args[6].asString(rt), args[7].asString(rt), args[8].asObject(rt).asArray(rt), args[9].asObject(rt).asArray(rt), args[10].asString(rt), args[11].asString(rt), args[12].asString(rt)); @@ -25,7 +25,7 @@ return static_cast(&turboModule)->logInPasswordUser(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt), args[4].asString(rt), args[5].asString(rt), args[6].asString(rt), args[7].asString(rt), args[8].asObject(rt).asArray(rt), args[9].asObject(rt).asArray(rt)); } static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_registerWalletUser(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { - return static_cast(&turboModule)->registerWalletUser(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt), args[4].asString(rt), args[5].asString(rt), args[6].asString(rt), args[7].asString(rt), args[8].asObject(rt).asArray(rt), args[9].asObject(rt).asArray(rt), args[10].asString(rt), args[11].asString(rt)); + return static_cast(&turboModule)->registerWalletUser(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt), args[4].asString(rt), args[5].asString(rt), args[6].asString(rt), args[7].asString(rt), args[8].asObject(rt).asArray(rt), args[9].asObject(rt).asArray(rt), args[10].asString(rt), args[11].asString(rt), args[12].asString(rt)); } static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_logInWalletUser(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->logInWalletUser(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt), args[4].asString(rt), args[5].asString(rt), args[6].asString(rt), args[7].asString(rt), args[8].asObject(rt).asArray(rt), args[9].asObject(rt).asArray(rt)); @@ -103,10 +103,10 @@ CommRustModuleSchemaCxxSpecJSI::CommRustModuleSchemaCxxSpecJSI(std::shared_ptr jsInvoker) : TurboModule("CommRustTurboModule", jsInvoker) { methodMap_["generateNonce"] = MethodMetadata {0, __hostFunction_CommRustModuleSchemaCxxSpecJSI_generateNonce}; - methodMap_["registerPasswordUser"] = MethodMetadata {12, __hostFunction_CommRustModuleSchemaCxxSpecJSI_registerPasswordUser}; + methodMap_["registerPasswordUser"] = MethodMetadata {13, __hostFunction_CommRustModuleSchemaCxxSpecJSI_registerPasswordUser}; methodMap_["registerReservedPasswordUser"] = MethodMetadata {13, __hostFunction_CommRustModuleSchemaCxxSpecJSI_registerReservedPasswordUser}; methodMap_["logInPasswordUser"] = MethodMetadata {10, __hostFunction_CommRustModuleSchemaCxxSpecJSI_logInPasswordUser}; - methodMap_["registerWalletUser"] = MethodMetadata {12, __hostFunction_CommRustModuleSchemaCxxSpecJSI_registerWalletUser}; + methodMap_["registerWalletUser"] = MethodMetadata {13, __hostFunction_CommRustModuleSchemaCxxSpecJSI_registerWalletUser}; methodMap_["logInWalletUser"] = MethodMetadata {10, __hostFunction_CommRustModuleSchemaCxxSpecJSI_logInWalletUser}; methodMap_["updatePassword"] = MethodMetadata {5, __hostFunction_CommRustModuleSchemaCxxSpecJSI_updatePassword}; methodMap_["deletePasswordUser"] = MethodMetadata {4, __hostFunction_CommRustModuleSchemaCxxSpecJSI_deletePasswordUser}; diff --git a/native/cpp/CommonCpp/_generated/rustJSI.h b/native/cpp/CommonCpp/_generated/rustJSI.h --- a/native/cpp/CommonCpp/_generated/rustJSI.h +++ b/native/cpp/CommonCpp/_generated/rustJSI.h @@ -21,10 +21,10 @@ public: virtual jsi::Value generateNonce(jsi::Runtime &rt) = 0; - virtual jsi::Value registerPasswordUser(jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String farcasterID, jsi::String initialDeviceList) = 0; + virtual jsi::Value registerPasswordUser(jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String farcasterID, jsi::String initialDeviceList, jsi::String farcasterDCsToken) = 0; virtual jsi::Value registerReservedPasswordUser(jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String keyserverMessage, jsi::String keyserverSignature, jsi::String initialDeviceList) = 0; virtual jsi::Value logInPasswordUser(jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys) = 0; - virtual jsi::Value registerWalletUser(jsi::Runtime &rt, jsi::String siweMessage, jsi::String siweSignature, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String farcasterID, jsi::String initialDeviceList) = 0; + virtual jsi::Value registerWalletUser(jsi::Runtime &rt, jsi::String siweMessage, jsi::String siweSignature, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String farcasterID, jsi::String initialDeviceList, jsi::String farcasterDCsToken) = 0; virtual jsi::Value logInWalletUser(jsi::Runtime &rt, jsi::String siweMessage, jsi::String siweSignature, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys) = 0; virtual jsi::Value updatePassword(jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken, jsi::String oldPassword, jsi::String newPassword) = 0; virtual jsi::Value deletePasswordUser(jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken, jsi::String password) = 0; @@ -78,13 +78,13 @@ return bridging::callFromJs( rt, &T::generateNonce, jsInvoker_, instance_); } - jsi::Value registerPasswordUser(jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String farcasterID, jsi::String initialDeviceList) override { + jsi::Value registerPasswordUser(jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String farcasterID, jsi::String initialDeviceList, jsi::String farcasterDCsToken) override { static_assert( - bridging::getParameterCount(&T::registerPasswordUser) == 13, - "Expected registerPasswordUser(...) to have 13 parameters"); + bridging::getParameterCount(&T::registerPasswordUser) == 14, + "Expected registerPasswordUser(...) to have 14 parameters"); return bridging::callFromJs( - rt, &T::registerPasswordUser, jsInvoker_, instance_, std::move(username), std::move(password), std::move(keyPayload), std::move(keyPayloadSignature), std::move(contentPrekey), std::move(contentPrekeySignature), std::move(notifPrekey), std::move(notifPrekeySignature), std::move(contentOneTimeKeys), std::move(notifOneTimeKeys), std::move(farcasterID), std::move(initialDeviceList)); + rt, &T::registerPasswordUser, jsInvoker_, instance_, std::move(username), std::move(password), std::move(keyPayload), std::move(keyPayloadSignature), std::move(contentPrekey), std::move(contentPrekeySignature), std::move(notifPrekey), std::move(notifPrekeySignature), std::move(contentOneTimeKeys), std::move(notifOneTimeKeys), std::move(farcasterID), std::move(initialDeviceList), std::move(farcasterDCsToken)); } jsi::Value registerReservedPasswordUser(jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String keyserverMessage, jsi::String keyserverSignature, jsi::String initialDeviceList) override { static_assert( @@ -102,13 +102,13 @@ return bridging::callFromJs( rt, &T::logInPasswordUser, jsInvoker_, instance_, std::move(username), std::move(password), std::move(keyPayload), std::move(keyPayloadSignature), std::move(contentPrekey), std::move(contentPrekeySignature), std::move(notifPrekey), std::move(notifPrekeySignature), std::move(contentOneTimeKeys), std::move(notifOneTimeKeys)); } - jsi::Value registerWalletUser(jsi::Runtime &rt, jsi::String siweMessage, jsi::String siweSignature, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String farcasterID, jsi::String initialDeviceList) override { + jsi::Value registerWalletUser(jsi::Runtime &rt, jsi::String siweMessage, jsi::String siweSignature, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String farcasterID, jsi::String initialDeviceList, jsi::String farcasterDCsToken) override { static_assert( - bridging::getParameterCount(&T::registerWalletUser) == 13, - "Expected registerWalletUser(...) to have 13 parameters"); + bridging::getParameterCount(&T::registerWalletUser) == 14, + "Expected registerWalletUser(...) to have 14 parameters"); return bridging::callFromJs( - rt, &T::registerWalletUser, jsInvoker_, instance_, std::move(siweMessage), std::move(siweSignature), std::move(keyPayload), std::move(keyPayloadSignature), std::move(contentPrekey), std::move(contentPrekeySignature), std::move(notifPrekey), std::move(notifPrekeySignature), std::move(contentOneTimeKeys), std::move(notifOneTimeKeys), std::move(farcasterID), std::move(initialDeviceList)); + rt, &T::registerWalletUser, jsInvoker_, instance_, std::move(siweMessage), std::move(siweSignature), std::move(keyPayload), std::move(keyPayloadSignature), std::move(contentPrekey), std::move(contentPrekeySignature), std::move(notifPrekey), std::move(notifPrekeySignature), std::move(contentOneTimeKeys), std::move(notifOneTimeKeys), std::move(farcasterID), std::move(initialDeviceList), std::move(farcasterDCsToken)); } jsi::Value logInWalletUser(jsi::Runtime &rt, jsi::String siweMessage, jsi::String siweSignature, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys) override { static_assert( diff --git a/native/identity-service/identity-service-context-provider.react.js b/native/identity-service/identity-service-context-provider.react.js --- a/native/identity-service/identity-service-context-provider.react.js +++ b/native/identity-service/identity-service-context-provider.react.js @@ -414,6 +414,7 @@ username: string, password: string, fid: ?string, + farcasterDCsToken: ?string, ) => { const { olmAPI } = getConfig(); await olmAPI.initializeCryptoAccount(); @@ -442,6 +443,7 @@ getOneTimeKeyValues(notificationsOneTimeKeys), fid ?? '', JSON.stringify(initialDeviceList), + farcasterDCsToken ?? '', ); return await processAuthResult( @@ -526,6 +528,7 @@ siweMessage: string, siweSignature: string, fid: ?string, + farcasterDCsToken: ?string, ) => { const { olmAPI } = getConfig(); await olmAPI.initializeCryptoAccount(); @@ -554,6 +557,7 @@ getOneTimeKeyValues(notificationsOneTimeKeys), fid ?? '', JSON.stringify(initialDeviceList), + farcasterDCsToken ?? '', ); return await processAuthResult( diff --git a/native/native_rust_library/src/identity.rs b/native/native_rust_library/src/identity.rs --- a/native/native_rust_library/src/identity.rs +++ b/native/native_rust_library/src/identity.rs @@ -120,6 +120,7 @@ pub device_keys: DeviceKeys, pub farcaster_id: Option, pub initial_device_list: String, + pub farcaster_dcs_token: Option, } pub struct RegisterReservedPasswordUserInfo { @@ -143,6 +144,7 @@ pub device_keys: DeviceKeys, pub farcaster_id: Option, pub initial_device_list: String, + pub farcaster_dcs_token: Option, } pub struct RestoreUserInfo { diff --git a/native/native_rust_library/src/identity/farcaster.rs b/native/native_rust_library/src/identity/farcaster.rs --- a/native/native_rust_library/src/identity/farcaster.rs +++ b/native/native_rust_library/src/identity/farcaster.rs @@ -66,7 +66,7 @@ } } -pub fn farcaster_id_string_to_option(input: &str) -> Option { +pub fn possibly_empty_string_to_option(input: &str) -> Option { if input.is_empty() { None } else { diff --git a/native/native_rust_library/src/identity/login.rs b/native/native_rust_library/src/identity/login.rs --- a/native/native_rust_library/src/identity/login.rs +++ b/native/native_rust_library/src/identity/login.rs @@ -261,6 +261,7 @@ device_key_upload: Some(wallet_user_info.device_keys.into()), farcaster_id: None, initial_device_list: "".to_string(), + farcaster_dcs_token: None, }; let mut identity_client = diff --git a/native/native_rust_library/src/identity/registration.rs b/native/native_rust_library/src/identity/registration.rs --- a/native/native_rust_library/src/identity/registration.rs +++ b/native/native_rust_library/src/identity/registration.rs @@ -13,7 +13,7 @@ use tracing::instrument; use super::{ - farcaster::farcaster_id_string_to_option, IdentityAuthResult, + farcaster::possibly_empty_string_to_option, IdentityAuthResult, RegisterPasswordUserInfo, RegisterReservedPasswordUserInfo, RegisterWalletUserInfo, PLATFORM_METADATA, }; @@ -42,6 +42,7 @@ farcaster_id: String, initial_device_list: String, promise_id: u32, + farcaster_dcs_token: String, ) { RUNTIME.spawn(async move { let password_user_info = RegisterPasswordUserInfo { @@ -57,8 +58,11 @@ content_one_time_keys, notif_one_time_keys, }, - farcaster_id: farcaster_id_string_to_option(&farcaster_id), + farcaster_id: possibly_empty_string_to_option(&farcaster_id), initial_device_list, + farcaster_dcs_token: possibly_empty_string_to_option( + &farcaster_dcs_token, + ), }; let result = register_password_user_helper(password_user_info).await; handle_string_result_as_callback(result, promise_id); @@ -121,6 +125,7 @@ farcaster_id: String, initial_device_list: String, promise_id: u32, + farcaster_dcs_token: String, ) { RUNTIME.spawn(async move { let wallet_user_info = RegisterWalletUserInfo { @@ -136,8 +141,11 @@ content_one_time_keys, notif_one_time_keys, }, - farcaster_id: farcaster_id_string_to_option(&farcaster_id), + farcaster_id: possibly_empty_string_to_option(&farcaster_id), initial_device_list, + farcaster_dcs_token: possibly_empty_string_to_option( + &farcaster_dcs_token, + ), }; let result = register_wallet_user_helper(wallet_user_info).await; handle_string_result_as_callback(result, promise_id); @@ -158,6 +166,7 @@ device_key_upload: Some(password_user_info.device_keys.into()), farcaster_id: password_user_info.farcaster_id, initial_device_list: password_user_info.initial_device_list, + farcaster_dcs_token: password_user_info.farcaster_dcs_token, }; let mut identity_client = @@ -243,6 +252,7 @@ device_key_upload: Some(wallet_user_info.device_keys.into()), farcaster_id: wallet_user_info.farcaster_id, initial_device_list: wallet_user_info.initial_device_list, + farcaster_dcs_token: wallet_user_info.farcaster_dcs_token, }; let mut identity_client = diff --git a/native/native_rust_library/src/lib.rs b/native/native_rust_library/src/lib.rs --- a/native/native_rust_library/src/lib.rs +++ b/native/native_rust_library/src/lib.rs @@ -61,6 +61,7 @@ farcaster_id: String, initial_device_list: String, promise_id: u32, + farcaster_dcs_token: String, ); #[cxx_name = "identityRegisterReservedPasswordUser"] @@ -111,6 +112,7 @@ farcaster_id: String, initial_device_list: String, promise_id: u32, + farcaster_dcs_token: String, ); #[cxx_name = "identityLogInWalletUser"] diff --git a/native/schema/CommRustModuleSchema.js b/native/schema/CommRustModuleSchema.js --- a/native/schema/CommRustModuleSchema.js +++ b/native/schema/CommRustModuleSchema.js @@ -20,6 +20,7 @@ notifOneTimeKeys: $ReadOnlyArray, farcasterID: string, initialDeviceList: string, + farcasterDCsToken: string, ) => Promise; +registerReservedPasswordUser: ( username: string, @@ -61,6 +62,7 @@ notifOneTimeKeys: $ReadOnlyArray, farcasterID: string, initialDeviceList: string, + farcasterDCsToken: string, ) => Promise; +logInWalletUser: ( siweMessage: string, diff --git a/services/commtest/src/identity/device.rs b/services/commtest/src/identity/device.rs --- a/services/commtest/src/identity/device.rs +++ b/services/commtest/src/identity/device.rs @@ -93,6 +93,7 @@ }), farcaster_id: None, initial_device_list: initial_device_list.unwrap_or_default(), + farcaster_dcs_token: None, }; let mut identity_client = get_unauthenticated_client( 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 @@ -77,6 +77,7 @@ pub user_id: Option, pub farcaster_id: Option, pub initial_device_list: Option, + pub farcaster_dcs_token: Option, } #[derive(Clone, Serialize, Deserialize)] @@ -157,6 +158,7 @@ None, message.username.clone(), message.farcaster_id.clone(), + message.farcaster_dcs_token.clone(), )?; self .check_device_id_taken( @@ -215,6 +217,7 @@ Some(user_id), original_username, None, + None, )?; self .check_device_id_taken( @@ -603,6 +606,7 @@ platform_metadata, login_time, message.farcaster_id, + message.farcaster_dcs_token, None, ) .await?; @@ -688,6 +692,7 @@ platform_metadata, login_time, message.farcaster_id, + message.farcaster_dcs_token, initial_device_list, ) .await?; @@ -1439,6 +1444,7 @@ user_id: Option, username: String, farcaster_id: Option, + farcaster_dcs_token: Option, ) -> Result { Ok(UserRegistrationInfo { username, @@ -1448,6 +1454,7 @@ user_id, farcaster_id, initial_device_list: message.get_and_verify_initial_device_list()?, + farcaster_dcs_token, }) } 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 @@ -18,6 +18,8 @@ pub const USERS_TABLE_DEVICELIST_TIMESTAMP_ATTRIBUTE_NAME: &str = "deviceListTimestamp"; pub const USERS_TABLE_FARCASTER_ID_ATTRIBUTE_NAME: &str = "farcasterID"; +pub const USERS_TABLE_FARCASTER_DCS_TOKEN_ATTRIBUTE_NAME: &str = + "farcasterDCsToken"; pub const USERS_TABLE_USERNAME_LOWER_ATTRIBUTE_NAME: &str = "usernameLower"; pub const USERS_TABLE_USERNAME_INDEX: &str = "username-index"; pub const USERS_TABLE_WALLET_ADDRESS_INDEX: &str = "walletAddress-index"; @@ -205,6 +207,7 @@ pub const NONCE_EXPIRED: &str = "nonce_expired"; pub const FID_TAKEN: &str = "fid_taken"; pub const CANNOT_LINK_FID: &str = "cannot_link_fid"; + pub const CANNOT_LINK_FARCASTER_DCS: &str = "cannot_link_farcaster_dcs"; pub const INVALID_PLATFORM_METADATA: &str = "invalid_platform_metadata"; pub const MISSING_CREDENTIALS: &str = "missing_credentials"; pub const BAD_CREDENTIALS: &str = "bad_credentials"; 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 @@ -48,6 +48,7 @@ RESERVED_USERNAMES_TABLE_USERNAME_LOWER_INDEX, RESERVED_USERNAMES_TABLE_USER_ID_ATTRIBUTE, USERS_TABLE, USERS_TABLE_DEVICES_MAP_DEVICE_TYPE_ATTRIBUTE_NAME, + USERS_TABLE_FARCASTER_DCS_TOKEN_ATTRIBUTE_NAME, USERS_TABLE_FARCASTER_ID_ATTRIBUTE_NAME, USERS_TABLE_PARTITION_KEY, USERS_TABLE_REGISTRATION_ATTRIBUTE, USERS_TABLE_SOCIAL_PROOF_ATTRIBUTE_NAME, USERS_TABLE_USERNAME_ATTRIBUTE, USERS_TABLE_USERNAME_LOWER_ATTRIBUTE_NAME, @@ -168,6 +169,7 @@ None, registration_state.user_id, registration_state.farcaster_id, + registration_state.farcaster_dcs_token, ) .await?; @@ -218,6 +220,7 @@ platform_metadata: PlatformMetadata, access_token_creation_time: DateTime, farcaster_id: Option, + farcaster_dcs_token: Option, initial_device_list: Option, ) -> Result { let wallet_identity = EthereumIdentity { @@ -230,6 +233,7 @@ Some(wallet_identity), user_id, farcaster_id, + farcaster_dcs_token, ) .await?; @@ -276,6 +280,7 @@ wallet_identity: Option, user_id: Option, farcaster_id: Option, + farcaster_dcs_token: Option, ) -> Result { let user_id = user_id.unwrap_or_else(generate_uuid); let mut user = HashMap::from([( @@ -317,6 +322,13 @@ ); } + if let Some(token) = farcaster_dcs_token { + user.insert( + USERS_TABLE_FARCASTER_DCS_TOKEN_ATTRIBUTE_NAME.to_string(), + AttributeValue::S(token), + ); + } + let put_user = Put::builder() .table_name(USERS_TABLE) .set_item(Some(user)) diff --git a/services/identity/src/database/farcaster.rs b/services/identity/src/database/farcaster.rs --- a/services/identity/src/database/farcaster.rs +++ b/services/identity/src/database/farcaster.rs @@ -9,6 +9,7 @@ use crate::constants::error_types; use crate::constants::USERS_TABLE; +use crate::constants::USERS_TABLE_FARCASTER_DCS_TOKEN_ATTRIBUTE_NAME; use crate::constants::USERS_TABLE_FARCASTER_ID_ATTRIBUTE_NAME; use crate::constants::USERS_TABLE_FARCASTER_ID_INDEX; use crate::constants::USERS_TABLE_PARTITION_KEY; @@ -68,8 +69,9 @@ farcaster_id: String, ) -> Result<(), Error> { let update_expression = format!( - "SET {0} = if_not_exists({0}, :val)", + "SET {0} = if_not_exists({0}, :val) REMOVE {1}", USERS_TABLE_FARCASTER_ID_ATTRIBUTE_NAME, + USERS_TABLE_FARCASTER_DCS_TOKEN_ATTRIBUTE_NAME, ); let response = self @@ -107,12 +109,46 @@ Ok(()) } - pub async fn remove_farcaster_id( + pub async fn add_farcaster_dcs_token( &self, user_id: String, + farcaster_dcs_token: String, ) -> Result<(), Error> { - let update_expression = - format!("REMOVE {}", USERS_TABLE_FARCASTER_ID_ATTRIBUTE_NAME); + let update_expression = format!( + "SET {0} = :val", + USERS_TABLE_FARCASTER_DCS_TOKEN_ATTRIBUTE_NAME, + ); + + self + .client + .update_item() + .table_name(USERS_TABLE) + .key(USERS_TABLE_PARTITION_KEY, AttributeValue::S(user_id)) + .update_expression(update_expression) + .expression_attribute_values( + ":val", + AttributeValue::S(farcaster_dcs_token.clone()), + ) + .return_values(ReturnValue::UpdatedNew) + .send() + .await + .map_err(|e| { + error!( + errorType = error_types::FARCASTER_DB_LOG, + "DDB client failed to add Farcaster DCs token: {:?}", e + ); + Error::AwsSdk(e.into()) + })?; + + Ok(()) + } + + pub async fn unlink_farcaster(&self, user_id: String) -> Result<(), Error> { + let update_expression = format!( + "REMOVE {}, {}", + USERS_TABLE_FARCASTER_ID_ATTRIBUTE_NAME, + USERS_TABLE_FARCASTER_DCS_TOKEN_ATTRIBUTE_NAME + ); self .client diff --git a/services/identity/src/ddb_utils.rs b/services/identity/src/ddb_utils.rs --- a/services/identity/src/ddb_utils.rs +++ b/services/identity/src/ddb_utils.rs @@ -8,6 +8,7 @@ use crate::{ constants::{ + USERS_TABLE_FARCASTER_DCS_TOKEN_ATTRIBUTE_NAME, USERS_TABLE_FARCASTER_ID_ATTRIBUTE_NAME, USERS_TABLE_SOCIAL_PROOF_ATTRIBUTE_NAME, USERS_TABLE_USERNAME_ATTRIBUTE, USERS_TABLE_WALLET_ADDRESS_ATTRIBUTE, @@ -166,6 +167,7 @@ pub struct DBIdentity { pub identifier: Identifier, pub farcaster_id: Option, + pub farcaster_dcs_token: Option, } pub enum Identifier { @@ -195,12 +197,16 @@ let farcaster_id = value.take_attr(USERS_TABLE_FARCASTER_ID_ATTRIBUTE_NAME)?; + let farcaster_dcs_token = + value.take_attr(USERS_TABLE_FARCASTER_DCS_TOKEN_ATTRIBUTE_NAME)?; + let username_result = value.take_attr(USERS_TABLE_USERNAME_ATTRIBUTE); if let Ok(username) = username_result { return Ok(DBIdentity { identifier: Identifier::Username(username), farcaster_id, + farcaster_dcs_token, }); } @@ -218,6 +224,7 @@ social_proof, }), farcaster_id, + farcaster_dcs_token, }) } else { Err(Self::Error::MalformedItem) 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 @@ -26,11 +26,11 @@ DeletePasswordUserFinishRequest, DeletePasswordUserStartRequest, DeletePasswordUserStartResponse, GetDeviceListRequest, GetDeviceListResponse, InboundKeyInfo, InboundKeysForUserRequest, InboundKeysForUserResponse, - KeyserverKeysResponse, LinkFarcasterAccountRequest, OutboundKeyInfo, - OutboundKeysForUserRequest, OutboundKeysForUserResponse, - PeersDeviceListsRequest, PeersDeviceListsResponse, - PrimaryDeviceLogoutRequest, PrivilegedDeleteUsersRequest, - PrivilegedResetUserPasswordFinishRequest, + KeyserverKeysResponse, LinkFarcasterAccountRequest, + LinkFarcasterDCsAccountRequest, OutboundKeyInfo, OutboundKeysForUserRequest, + OutboundKeysForUserResponse, PeersDeviceListsRequest, + PeersDeviceListsResponse, PrimaryDeviceLogoutRequest, + PrivilegedDeleteUsersRequest, PrivilegedResetUserPasswordFinishRequest, PrivilegedResetUserPasswordStartRequest, PrivilegedResetUserPasswordStartResponse, RefreshUserPrekeysRequest, UpdateDeviceListRequest, UpdateUserPasswordFinishRequest, @@ -1097,7 +1097,7 @@ .await?; if get_farcaster_users_response.len() > 1 { - error!( + warn!( errorType = error_types::GRPC_SERVICES_LOG, "multiple users associated with the same Farcaster ID" ); @@ -1124,6 +1124,46 @@ Ok(Response::new(response)) } + #[tracing::instrument(skip_all)] + async fn link_farcaster_d_cs_account( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + let (user_id, _) = get_user_and_device_id(&request)?; + let message = request.into_inner(); + + info!( + user_id = redact_sensitive_data(&user_id), + "Attempting to link Farcaster DCs account" + ); + + let fid_count = self + .db_client + .get_farcaster_users(vec![message.farcaster_id.clone()]) + .await? + .len(); + + if fid_count != 1 { + warn!( + errorType = error_types::GRPC_SERVICES_LOG, + fid = redact_sensitive_data(&message.farcaster_id), + count = fid_count, + "Farcaster ID missing or non-unique" + ); + return Err(Status::failed_precondition( + tonic_status_messages::CANNOT_LINK_FARCASTER_DCS, + )); + } + + self + .db_client + .add_farcaster_dcs_token(user_id, message.farcaster_dcs_token) + .await?; + + let response = Empty {}; + Ok(Response::new(response)) + } + #[tracing::instrument(skip_all)] async fn unlink_farcaster_account( &self, @@ -1136,7 +1176,7 @@ "Attempting to unlink Farcaster account." ); - self.db_client.remove_farcaster_id(user_id).await?; + self.db_client.unlink_farcaster(user_id).await?; let response = Empty {}; Ok(Response::new(response)) 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 @@ -97,6 +97,10 @@ // Called by an existing user to unlink their Farcaster account rpc UnlinkFarcasterAccount(identity.unauth.Empty) returns (identity.unauth.Empty) {} + // Called by an existing user with a linked Farcaster account to link + // their DCs + rpc LinkFarcasterDCsAccount(LinkFarcasterDCsAccountRequest) returns + (identity.unauth.Empty) {} /* Miscellaneous actions */ @@ -362,6 +366,11 @@ string farcaster_id = 1; } +message LinkFarcasterDCsAccountRequest { + string farcaster_dcs_token = 1; + string farcaster_id = 2; +} + // FindUserIdentities message UserIdentitiesRequest { diff --git a/shared/protos/identity_unauth.proto b/shared/protos/identity_unauth.proto --- a/shared/protos/identity_unauth.proto +++ b/shared/protos/identity_unauth.proto @@ -133,6 +133,7 @@ // } // It's an empty string for older clients which don't sign device lists yet. string initial_device_list = 5; + optional string farcaster_dcs_token = 6; } message ReservedRegistrationStartRequest { @@ -231,6 +232,7 @@ // It's an empty string when used outside `RegisterWalletUser` RPC // and for older clients which don't sign device lists yet. string initial_device_list = 5; + optional string farcaster_dcs_token = 6; } // Primary backup restore