From 0052e8101096016edfc8633051ebb675b45c3ff4 Mon Sep 17 00:00:00 2001 From: Ricky Padilla Date: Wed, 29 Nov 2023 15:58:45 -0700 Subject: [PATCH 1/2] Add `identifierExists` method. --- android/build.gradle | 2 +- .../PassageReactNativeModule.kt | 24 ++++++++---- ios/PassageReactNative.m | 4 ++ ios/PassageReactNative.swift | 12 ++++++ ios/passage-react-native.podspec | 38 ------------------- package.json | 2 +- passage-react-native.podspec | 2 +- src/index.tsx | 20 ++++++++++ 8 files changed, 56 insertions(+), 48 deletions(-) delete mode 100644 ios/passage-react-native.podspec diff --git a/android/build.gradle b/android/build.gradle index fed1ee2..7a3490a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -75,7 +75,7 @@ dependencies { implementation "com.facebook.react:react-native:+" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "id.passage.android:passage:1.2.0" + implementation "id.passage.android:passage:1.5.1" implementation "com.google.code.gson:gson:2.9.0" } diff --git a/android/src/main/java/com/passagereactnative/PassageReactNativeModule.kt b/android/src/main/java/com/passagereactnative/PassageReactNativeModule.kt index b4ce5ff..f0f35d5 100644 --- a/android/src/main/java/com/passagereactnative/PassageReactNativeModule.kt +++ b/android/src/main/java/com/passagereactnative/PassageReactNativeModule.kt @@ -12,9 +12,6 @@ import com.google.gson.Gson import id.passage.android.Passage import id.passage.android.PassageToken import id.passage.android.exceptions.AddDevicePasskeyCancellationException -import id.passage.android.exceptions.AppInfoException -import id.passage.android.exceptions.GetMagicLinkStatusInvalidException -import id.passage.android.exceptions.GetMagicLinkStatusNotFoundException import id.passage.android.exceptions.LoginWithPasskeyCancellationException import id.passage.android.exceptions.PassageUserUnauthorizedException import id.passage.android.exceptions.RegisterWithPasskeyCancellationException @@ -68,7 +65,7 @@ class PassageReactNativeModule(reactContext: ReactApplicationContext) : fun loginWithPasskey(promise: Promise) { CoroutineScope(Dispatchers.IO).launch { try { - val authResult = passage.loginWithPasskey("") + val authResult = passage.loginWithPasskey() val jsonString = Gson().toJson(authResult) promise.resolve(jsonString) } catch (e: Exception) { @@ -223,7 +220,7 @@ class PassageReactNativeModule(reactContext: ReactApplicationContext) : fun getAppInfo(promise: Promise) { CoroutineScope(Dispatchers.IO).launch { try { - val appInfo = passage.appInfo() ?: throw AppInfoException("Error getting app info") + val appInfo = passage.appInfo() val jsonString = Gson().toJson(appInfo) promise.resolve(jsonString) } catch (e: Exception) { @@ -232,6 +229,19 @@ class PassageReactNativeModule(reactContext: ReactApplicationContext) : } } + @ReactMethod + fun identifierExists(identifier: String, promise: Promise) { + CoroutineScope(Dispatchers.IO).launch { + try { + val user = passage.identifierExists(identifier) + val jsonString = if (user == null) null else Gson().toJson(user) + promise.resolve(jsonString) + } catch (e: Exception) { + promise.reject("IDENTIFIER_EXISTS_ERROR", e.message, e) + } + } + } + // endregion // region USER METHODS @@ -327,7 +337,7 @@ class PassageReactNativeModule(reactContext: ReactApplicationContext) : CoroutineScope(Dispatchers.IO).launch { try { val user = passage.getCurrentUser() ?: throw PassageUserUnauthorizedException("User is not authorized.") - val magicLinkId = user.changeEmail(newEmail)?.id + val magicLinkId = user.changeEmail(newEmail).id promise.resolve(magicLinkId) } catch (e: Exception) { var errorCode = "CHANGE_EMAIL_ERROR" @@ -346,7 +356,7 @@ class PassageReactNativeModule(reactContext: ReactApplicationContext) : CoroutineScope(Dispatchers.IO).launch { try { val user = passage.getCurrentUser() ?: throw PassageUserUnauthorizedException("User is not authorized.") - val magicLinkId = user.changePhone(newPhone)?.id + val magicLinkId = user.changePhone(newPhone).id promise.resolve(magicLinkId) } catch (e: Exception) { var errorCode = "CHANGE_PHONE_ERROR" diff --git a/ios/PassageReactNative.m b/ios/PassageReactNative.m index 5b1e1a6..1f0a7f4 100644 --- a/ios/PassageReactNative.m +++ b/ios/PassageReactNative.m @@ -64,6 +64,10 @@ @interface RCT_EXTERN_MODULE(PassageReactNative, NSObject) RCT_EXTERN_METHOD(getAppInfo:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject); +RCT_EXTERN_METHOD(identifierExists:(NSString *)identifier + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject); + // MARK: - User Methods RCT_EXTERN_METHOD(getCurrentUser:(RCTPromiseResolveBlock)resolve diff --git a/ios/PassageReactNative.swift b/ios/PassageReactNative.swift index 4d28dd3..c97aa4b 100644 --- a/ios/PassageReactNative.swift +++ b/ios/PassageReactNative.swift @@ -252,6 +252,18 @@ class PassageReactNative: NSObject { } } + @objc(identifierExists:withResolver:withRejecter:) + internal func identifierExists( + identifier: String, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + Task { + let user = try? await PassageAuth.getUser(identifier: identifier) + resolve(user?.toJsonString()) + } + } + // MARK: - User Methods @objc(getCurrentUser:withRejecter:) diff --git a/ios/passage-react-native.podspec b/ios/passage-react-native.podspec deleted file mode 100644 index 1618395..0000000 --- a/ios/passage-react-native.podspec +++ /dev/null @@ -1,38 +0,0 @@ -require "json" - -package = JSON.parse(File.read(File.join(__dir__, "package.json"))) -folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' - -Pod::Spec.new do |s| - s.name = "passage-react-native" - s.version = package["version"] - s.summary = package["description"] - s.homepage = package["homepage"] - s.license = package["license"] - s.authors = package["author"] - - s.platforms = { :ios => "11.0" } - s.source = { :git => "https://github.com/passageidentity/passage-react-native.git", :tag => "#{s.version}" } - - s.source_files = "ios/**/*.{h,m,mm,swift}" - - s.dependency "React-Core" - - s.dependency 'Passage', '1.2.0' - s.platform = :ios, '16.0' - - # Don't install the dependencies when we run `pod install` in the old architecture. - if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then - s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1" - s.pod_target_xcconfig = { - "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"", - "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1", - "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" - } - s.dependency "React-Codegen" - s.dependency "RCT-Folly" - s.dependency "RCTRequired" - s.dependency "RCTTypeSafety" - s.dependency "ReactCommon/turbomodule/core" - end -end diff --git a/package.json b/package.json index 68dca91..ab5775d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@passageidentity/passage-react-native", - "version": "0.3.0", + "version": "0.4.0", "description": "test", "main": "lib/commonjs/index", "module": "lib/module/index", diff --git a/passage-react-native.podspec b/passage-react-native.podspec index 1618395..4a5f8b4 100644 --- a/passage-react-native.podspec +++ b/passage-react-native.podspec @@ -18,7 +18,7 @@ Pod::Spec.new do |s| s.dependency "React-Core" - s.dependency 'Passage', '1.2.0' + s.dependency 'Passage', '1.4.0' s.platform = :ios, '16.0' # Don't install the dependencies when we run `pod install` in the old architecture. diff --git a/src/index.tsx b/src/index.tsx index 74011be..4f213b2 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -148,6 +148,7 @@ type GetAuthToken = () => Promise; type IsAuthTokenValid = (authToken: string) => Promise; type RefreshAuthToken = () => Promise; type GetAppInfo = () => Promise; +type IdentifierExists = (identifier: string) => Promise; type AddPasskey = () => Promise; type DeletePasskey = (passkeyId: string) => Promise; type EditPasskeyName = ( @@ -424,6 +425,25 @@ class Passage { } }; + /** + * Check if a user with a given identifier exists. If so, this method will return user info. + * + * @return {Promise} A data object containing user information. + * @throws {PassageError} + */ + identifierExists: IdentifierExists = async ( + identifier: string + ): Promise => { + try { + const result = await PassageReactNative.identifierExists(identifier); + if (!result) return null; + const parsedResult = JSON.parse(result); + return parsedResult; + } catch (error: any) { + throw new PassageError(error.code, error.message); + } + }; + // USER METHODS /** From 4d5f22c2e8ce4980ccca4713a8b6eb4c6d943723 Mon Sep 17 00:00:00 2001 From: Ricky Padilla Date: Wed, 29 Nov 2023 16:17:35 -0700 Subject: [PATCH 2/2] Handle new otp exceeded attempts error code. --- .../com/passagereactnative/PassageReactNativeModule.kt | 3 +++ example/ios/Podfile.lock | 10 +++++----- ios/PassageReactNative.swift | 6 +++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/android/src/main/java/com/passagereactnative/PassageReactNativeModule.kt b/android/src/main/java/com/passagereactnative/PassageReactNativeModule.kt index f0f35d5..6820804 100644 --- a/android/src/main/java/com/passagereactnative/PassageReactNativeModule.kt +++ b/android/src/main/java/com/passagereactnative/PassageReactNativeModule.kt @@ -13,6 +13,7 @@ import id.passage.android.Passage import id.passage.android.PassageToken import id.passage.android.exceptions.AddDevicePasskeyCancellationException import id.passage.android.exceptions.LoginWithPasskeyCancellationException +import id.passage.android.exceptions.OneTimePasscodeActivateExceededAttemptsException import id.passage.android.exceptions.PassageUserUnauthorizedException import id.passage.android.exceptions.RegisterWithPasskeyCancellationException @@ -121,6 +122,8 @@ class PassageReactNativeModule(reactContext: ReactApplicationContext) : val authResult = passage.oneTimePasscodeActivate(otp, otpId) val jsonString = Gson().toJson(authResult) promise.resolve(jsonString) + } catch (e: OneTimePasscodeActivateExceededAttemptsException) { + promise.reject("OTP_ACTIVATION_EXCEEDED_ATTEMPTS", e.message, e) } catch (e: Exception) { promise.reject("OTP_ERROR", e.message, e) } diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index ffe1172..a4d4499 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -78,10 +78,10 @@ PODS: - hermes-engine/Pre-built (0.71.10) - libevent (2.1.12) - OpenSSL-Universal (1.1.1100) - - Passage (1.2.0): + - Passage (1.4.0): - SwiftKeychainWrapper - - passage-react-native (0.1.4): - - Passage (= 1.2.0) + - passage-react-native (0.4.0): + - Passage (= 1.4.0) - React-Core - RCT-Folly (2021.07.22.00): - boost @@ -598,8 +598,8 @@ SPEC CHECKSUMS: hermes-engine: d27603b55a48402501ad1928c05411dae9cd6b85 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c - Passage: 623d579acf6cf9949f6c16c777b7efe6f6902d93 - passage-react-native: 96089329c8205f9c49013af37f592506ccd74fe6 + Passage: 4b543698ee5cfac577d9fce473f75587315269e5 + passage-react-native: 035fe2564bf099c3aed0543f5c1158dfc4c6b0ea RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 RCTRequired: 8ef706f91e2b643cd32c26a57700b5f24fab0585 RCTTypeSafety: 5fbddd8eb9242b91ac0d901c01da3673f358b1b7 diff --git a/ios/PassageReactNative.swift b/ios/PassageReactNative.swift index c97aa4b..4bd1d6e 100644 --- a/ios/PassageReactNative.swift +++ b/ios/PassageReactNative.swift @@ -125,7 +125,11 @@ class PassageReactNative: NSObject { let authResult = try await passage.oneTimePasscodeActivate(otp: otp, otpId: otpId) resolve(authResult.toJsonString()) } catch { - reject("OTP_ERROR", "\(error)", nil) + var errorCode = "OTP_ERROR" + if case PassageOTPError.exceededAttempts = error { + errorCode = "OTP_ACTIVATION_EXCEEDED_ATTEMPTS" + } + reject(errorCode, "\(error)", nil) } } }