diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt index 55476f4ef..4982cfff2 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/XMTPModule.kt @@ -13,6 +13,7 @@ import expo.modules.kotlin.exception.Exceptions import expo.modules.kotlin.functions.Coroutine import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition +import expo.modules.xmtpreactnativesdk.wrappers.AuthParamsWrapper import expo.modules.xmtpreactnativesdk.wrappers.ClientWrapper import expo.modules.xmtpreactnativesdk.wrappers.ConsentWrapper import expo.modules.xmtpreactnativesdk.wrappers.ConsentWrapper.Companion.consentStateToString @@ -227,10 +228,11 @@ class XMTPModule : Module() { // Auth functions // AsyncFunction("auth") { - { address: String, environment: String, appVersion: String?, hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, enableV3: Boolean?, dbEncryptionKey: List?, dbDirectory: String?, historySyncUrl: String? -> + { address: String, hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, dbEncryptionKey: List?, authParams: String -> logV("auth") val reactSigner = ReactNativeSigner(module = this@XMTPModule, address = address) signer = reactSigner + val authOptions = AuthParamsWrapper.authParamsFromJson(authParams) if (hasCreateIdentityCallback == true) preCreateIdentityCallbackDeferred = CompletableDeferred() @@ -240,21 +242,21 @@ class XMTPModule : Module() { preCreateIdentityCallback.takeIf { hasCreateIdentityCallback == true } val preEnableIdentityCallback: PreEventCallback? = preEnableIdentityCallback.takeIf { hasEnableIdentityCallback == true } - val context = if (enableV3 == true) context else null + val context = if (authOptions.enableV3) context else null val encryptionKeyBytes = dbEncryptionKey?.foldIndexed(ByteArray(dbEncryptionKey.size)) { i, a, v -> a.apply { set(i, v.toByte()) } } val options = ClientOptions( - api = apiEnvironments(environment, appVersion), + api = apiEnvironments(authOptions.environment, authOptions.appVersion), preCreateIdentityCallback = preCreateIdentityCallback, preEnableIdentityCallback = preEnableIdentityCallback, - enableV3 = enableV3 == true, + enableV3 = authOptions.enableV3, appContext = context, dbEncryptionKey = encryptionKeyBytes, - dbDirectory = dbDirectory, - historySyncUrl = historySyncUrl + dbDirectory = authOptions.dbDirectory, + historySyncUrl = authOptions.historySyncUrl ) val client = Client().create(account = reactSigner, options = options) clients[client.inboxId] = client @@ -270,7 +272,7 @@ class XMTPModule : Module() { } // Generate a random wallet and set the client to that - AsyncFunction("createRandom") { environment: String, appVersion: String?, hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, enableV3: Boolean?, dbEncryptionKey: List?, dbDirectory: String?, historySyncUrl: String? -> + AsyncFunction("createRandom") { hasCreateIdentityCallback: Boolean?, hasEnableIdentityCallback: Boolean?, dbEncryptionKey: List?, authParams: String -> logV("createRandom") val privateKey = PrivateKeyBuilder() @@ -282,21 +284,23 @@ class XMTPModule : Module() { preCreateIdentityCallback.takeIf { hasCreateIdentityCallback == true } val preEnableIdentityCallback: PreEventCallback? = preEnableIdentityCallback.takeIf { hasEnableIdentityCallback == true } - val context = if (enableV3 == true) context else null + + val authOptions = AuthParamsWrapper.authParamsFromJson(authParams) + val context = if (authOptions.enableV3) context else null val encryptionKeyBytes = dbEncryptionKey?.foldIndexed(ByteArray(dbEncryptionKey.size)) { i, a, v -> a.apply { set(i, v.toByte()) } } val options = ClientOptions( - api = apiEnvironments(environment, appVersion), + api = apiEnvironments(authOptions.environment, authOptions.appVersion), preCreateIdentityCallback = preCreateIdentityCallback, preEnableIdentityCallback = preEnableIdentityCallback, - enableV3 = enableV3 == true, + enableV3 = authOptions.enableV3, appContext = context, dbEncryptionKey = encryptionKeyBytes, - dbDirectory = dbDirectory, - historySyncUrl = historySyncUrl + dbDirectory = authOptions.dbDirectory, + historySyncUrl = authOptions.historySyncUrl ) val randomClient = Client().create(account = privateKey, options = options) @@ -306,22 +310,22 @@ class XMTPModule : Module() { ClientWrapper.encodeToObj(randomClient) } - AsyncFunction("createFromKeyBundle") { keyBundle: String, environment: String, appVersion: String?, enableV3: Boolean?, dbEncryptionKey: List?, dbDirectory: String?, historySyncUrl: String? -> + AsyncFunction("createFromKeyBundle") { keyBundle: String, dbEncryptionKey: List?, authParams: String -> logV("createFromKeyBundle") - + val authOptions = AuthParamsWrapper.authParamsFromJson(authParams) try { - val context = if (enableV3 == true) context else null + val context = if (authOptions.enableV3) context else null val encryptionKeyBytes = dbEncryptionKey?.foldIndexed(ByteArray(dbEncryptionKey.size)) { i, a, v -> a.apply { set(i, v.toByte()) } } val options = ClientOptions( - api = apiEnvironments(environment, appVersion), - enableV3 = enableV3 == true, + api = apiEnvironments(authOptions.environment, authOptions.appVersion), + enableV3 = authOptions.enableV3, appContext = context, dbEncryptionKey = encryptionKeyBytes, - dbDirectory = dbDirectory, - historySyncUrl = historySyncUrl + dbDirectory = authOptions.dbDirectory, + historySyncUrl = authOptions.historySyncUrl ) val bundle = PrivateKeyOuterClass.PrivateKeyBundle.parseFrom( diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/AuthParamsWrapper.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/AuthParamsWrapper.kt new file mode 100644 index 000000000..99146a715 --- /dev/null +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/AuthParamsWrapper.kt @@ -0,0 +1,24 @@ +package expo.modules.xmtpreactnativesdk.wrappers + +import com.google.gson.JsonParser + +class AuthParamsWrapper( + val environment: String, + val appVersion: String?, + val enableV3: Boolean = false, + val dbDirectory: String?, + val historySyncUrl: String?, +) { + companion object { + fun authParamsFromJson(authParams: String): AuthParamsWrapper { + val jsonOptions = JsonParser.parseString(authParams).asJsonObject + return AuthParamsWrapper( + jsonOptions.get("environment").asString, + if (jsonOptions.has("appVersion")) jsonOptions.get("appVersion").asString else null, + if (jsonOptions.has("enableV3")) jsonOptions.get("enableV3").asBoolean else false, + if (jsonOptions.has("dbDirectory")) jsonOptions.get("dbDirectory").asString else null, + if (jsonOptions.has("historySyncUrl")) jsonOptions.get("historySyncUrl").asString else null + ) + } + } +} diff --git a/ios/Wrappers/AuthParamsWrapper.swift b/ios/Wrappers/AuthParamsWrapper.swift new file mode 100644 index 000000000..47f1c48f1 --- /dev/null +++ b/ios/Wrappers/AuthParamsWrapper.swift @@ -0,0 +1,46 @@ +// +// AuthParamsWrapper.swift +// XMTPReactNative +// +// Created by Naomi Plasterer on 6/24/24. +// + +import Foundation +import XMTP + +class AuthParamsWrapper { + let environment: String + let appVersion: String? + let enableV3: Bool + let dbDirectory: String? + let historySyncUrl: String? + + init(environment: String, appVersion: String?, enableV3: Bool, dbDirectory: String?, historySyncUrl: String?) { + self.environment = environment + self.appVersion = appVersion + self.enableV3 = enableV3 + self.dbDirectory = dbDirectory + self.historySyncUrl = historySyncUrl + } + + static func authParamsFromJson(_ authParams: String) -> AuthParamsWrapper? { + guard let data = authParams.data(using: .utf8), + let jsonOptions = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { + return nil + } + + let environment = jsonOptions["environment"] as? String ?? "" + let appVersion = jsonOptions["appVersion"] as? String + let enableV3 = jsonOptions["enableV3"] as? Bool ?? false + let dbDirectory = jsonOptions["dbDirectory"] as? String + let historySyncUrl = jsonOptions["historySyncUrl"] as? String + + return AuthParamsWrapper( + environment: environment, + appVersion: appVersion, + enableV3: enableV3, + dbDirectory: dbDirectory, + historySyncUrl: historySyncUrl + ) + } +} diff --git a/ios/XMTPModule.swift b/ios/XMTPModule.swift index c222e2dcb..37a33171e 100644 --- a/ios/XMTPModule.swift +++ b/ios/XMTPModule.swift @@ -137,27 +137,25 @@ public class XMTPModule: Module { // // Auth functions // - AsyncFunction("auth") { - { (address: String, environment: String, appVersion: String?, hasCreateIdentityCallback: Bool?, hasEnableIdentityCallback: Bool?, enableV3: Bool?, dbEncryptionKey: [UInt8]?, dbDirectory: String?, historySyncUrl: String?) in - - let signer = ReactNativeSigner(module: self, address: address) - self.signer = signer - if(hasCreateIdentityCallback ?? false) { - self.preCreateIdentityCallbackDeferred = DispatchSemaphore(value: 0) - } - if(hasEnableIdentityCallback ?? false) { - self.preEnableIdentityCallbackDeferred = DispatchSemaphore(value: 0) - } - let preCreateIdentityCallback: PreEventCallback? = hasCreateIdentityCallback ?? false ? self.preCreateIdentityCallback : nil - let preEnableIdentityCallback: PreEventCallback? = hasEnableIdentityCallback ?? false ? self.preEnableIdentityCallback : nil - let encryptionKeyData = dbEncryptionKey == nil ? nil : Data(dbEncryptionKey!) - - let options = self.createClientConfig(env: environment, appVersion: appVersion, preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, enableV3: enableV3 == true, dbEncryptionKey: encryptionKeyData, dbDirectory: dbDirectory, historySyncUrl: historySyncUrl) - let client = try await XMTP.Client.create(account: signer, options: options) - await self.clientsManager.updateClient(key: client.inboxID, client: client) - self.signer = nil - self.sendEvent("authed", try ClientWrapper.encodeToObj(client)) + AsyncFunction("auth") { (address: String, hasCreateIdentityCallback: Bool?, hasEnableIdentityCallback: Bool?, dbEncryptionKey: [UInt8]?, authParams: String) in + let signer = ReactNativeSigner(module: self, address: address) + self.signer = signer + if(hasCreateIdentityCallback ?? false) { + self.preCreateIdentityCallbackDeferred = DispatchSemaphore(value: 0) + } + if(hasEnableIdentityCallback ?? false) { + self.preEnableIdentityCallbackDeferred = DispatchSemaphore(value: 0) } + let preCreateIdentityCallback: PreEventCallback? = hasCreateIdentityCallback ?? false ? self.preCreateIdentityCallback : nil + let preEnableIdentityCallback: PreEventCallback? = hasEnableIdentityCallback ?? false ? self.preEnableIdentityCallback : nil + let encryptionKeyData = dbEncryptionKey == nil ? nil : Data(dbEncryptionKey!) + let authOptions = AuthParamsWrapper.authParamsFromJson(authParams) + + let options = self.createClientConfig(env: authOptions.environment, appVersion: authOptions?.appVersion, preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, enableV3: authOptions.enableV3, dbEncryptionKey: encryptionKeyData, dbDirectory: authOptions?.dbDirectory, historySyncUrl: authOptions?.historySyncUrl) + let client = try await XMTP.Client.create(account: signer, options: options) + await self.clientsManager.updateClient(key: client.inboxID, client: client) + self.signer = nil + self.sendEvent("authed", try ClientWrapper.encodeToObj(client)) } Function("receiveSignature") { (requestID: String, signature: String) in @@ -165,7 +163,7 @@ public class XMTPModule: Module { } // Generate a random wallet and set the client to that - AsyncFunction("createRandom") { (environment: String, appVersion: String?, hasCreateIdentityCallback: Bool?, hasEnableIdentityCallback: Bool?, enableV3: Bool?, dbEncryptionKey: [UInt8]?, dbDirectory: String?, historySyncUrl: String?) -> [String: String] in + AsyncFunction("createRandom") { (hasCreateIdentityCallback: Bool?, hasEnableIdentityCallback: Bool?, dbEncryptionKey: [UInt8]?, authParams: String) -> [String: String] in let privateKey = try PrivateKey.generate() if(hasCreateIdentityCallback ?? false) { @@ -177,8 +175,9 @@ public class XMTPModule: Module { let preCreateIdentityCallback: PreEventCallback? = hasCreateIdentityCallback ?? false ? self.preCreateIdentityCallback : nil let preEnableIdentityCallback: PreEventCallback? = hasEnableIdentityCallback ?? false ? self.preEnableIdentityCallback : nil let encryptionKeyData = dbEncryptionKey == nil ? nil : Data(dbEncryptionKey!) + let authOptions = AuthParamsWrapper.authParamsFromJson(authParams) - let options = createClientConfig(env: environment, appVersion: appVersion, preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, enableV3: enableV3 == true, dbEncryptionKey: encryptionKeyData, dbDirectory: dbDirectory, historySyncUrl: historySyncUrl) + let options = createClientConfig(env: authOptions.environment, appVersion: authOptions.appVersion, preEnableIdentityCallback: preEnableIdentityCallback, preCreateIdentityCallback: preCreateIdentityCallback, enableV3: authOptions.enableV3, dbEncryptionKey: encryptionKeyData, dbDirectory: authOptions.dbDirectory, historySyncUrl: authOptions.historySyncUrl) let client = try await Client.create(account: privateKey, options: options) await clientsManager.updateClient(key: client.inboxID, client: client) @@ -186,7 +185,7 @@ public class XMTPModule: Module { } // Create a client using its serialized key bundle. - AsyncFunction("createFromKeyBundle") { (keyBundle: String, environment: String, appVersion: String?, enableV3: Bool?, dbEncryptionKey: [UInt8]?, dbDirectory: String?, historySyncUrl: String?) -> [String: String] in + AsyncFunction("createFromKeyBundle") { (keyBundle: String, dbEncryptionKey: [UInt8]?, authParams: String) -> [String: String] in do { guard let keyBundleData = Data(base64Encoded: keyBundle), @@ -195,7 +194,9 @@ public class XMTPModule: Module { throw Error.invalidKeyBundle } let encryptionKeyData = dbEncryptionKey == nil ? nil : Data(dbEncryptionKey!) - let options = createClientConfig(env: environment, appVersion: appVersion, enableV3: enableV3 == true, dbEncryptionKey: encryptionKeyData, dbDirectory: dbDirectory, historySyncUrl: historySyncUrl) + let authOptions = AuthParamsWrapper.authParamsFromJson(authParams) + + let options = createClientConfig(env: authOptions.environment, appVersion: authOptions.appVersion, enableV3: authOptions.enableV3, dbEncryptionKey: encryptionKeyData, dbDirectory: authOptions.dbDirectory, historySyncUrl: authOptions.historySyncUrl) let client = try await Client.from(bundle: bundle, options: options) await clientsManager.updateClient(key: client.inboxID, client: client) return try ClientWrapper.encodeToObj(client) diff --git a/src/index.ts b/src/index.ts index 0ef3de8ba..26b2cab1a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -70,7 +70,7 @@ export async function requestMessageHistorySync(inboxId: string) { } export async function auth( - inboxId: string, + address: string, environment: 'local' | 'dev' | 'production', appVersion?: string | undefined, hasCreateIdentityCallback?: boolean | undefined, @@ -80,16 +80,23 @@ export async function auth( dbDirectory?: string | undefined, historySyncUrl?: string | undefined ) { - return await XMTPModule.auth( - inboxId, + const encryptionKey = dbEncryptionKey + ? Array.from(dbEncryptionKey) + : undefined + + const authParams: AuthParams = { environment, appVersion, - hasCreateIdentityCallback, - hasEnableIdentityCallback, enableV3, - dbEncryptionKey ? Array.from(dbEncryptionKey) : undefined, dbDirectory, - historySyncUrl + historySyncUrl, + } + return await XMTPModule.auth( + address, + hasCreateIdentityCallback, + hasEnableIdentityCallback, + encryptionKey, + JSON.stringify(authParams) ) } @@ -107,15 +114,22 @@ export async function createRandom( dbDirectory?: string | undefined, historySyncUrl?: string | undefined ): Promise { - return await XMTPModule.createRandom( + const encryptionKey = dbEncryptionKey + ? Array.from(dbEncryptionKey) + : undefined + + const authParams: AuthParams = { environment, appVersion, - hasCreateIdentityCallback, - hasEnableIdentityCallback, enableV3, - dbEncryptionKey ? Array.from(dbEncryptionKey) : undefined, dbDirectory, - historySyncUrl + historySyncUrl, + } + return await XMTPModule.createRandom( + hasCreateIdentityCallback, + hasEnableIdentityCallback, + encryptionKey, + JSON.stringify(authParams) ) } @@ -128,14 +142,21 @@ export async function createFromKeyBundle( dbDirectory?: string | undefined, historySyncUrl?: string | undefined ): Promise { - return await XMTPModule.createFromKeyBundle( - keyBundle, + const encryptionKey = dbEncryptionKey + ? Array.from(dbEncryptionKey) + : undefined + + const authParams: AuthParams = { environment, appVersion, enableV3, - dbEncryptionKey ? Array.from(dbEncryptionKey) : undefined, dbDirectory, - historySyncUrl + historySyncUrl, + } + return await XMTPModule.createFromKeyBundle( + keyBundle, + encryptionKey, + JSON.stringify(authParams) ) } @@ -927,6 +948,14 @@ export async function processWelcomeMessage< export const emitter = new EventEmitter(XMTPModule ?? NativeModulesProxy.XMTP) +interface AuthParams { + environment: string + appVersion?: string + enableV3?: boolean + dbDirectory?: string + historySyncUrl?: string +} + export * from './XMTP.types' export { Client } from './lib/Client' export * from './lib/ContentCodec'