diff --git a/android/src/main/kotlin/id/passage/passage_flutter/PassageFlutter.kt b/android/src/main/kotlin/id/passage/passage_flutter/PassageFlutter.kt index dd5c8a8..e9a6f8e 100644 --- a/android/src/main/kotlin/id/passage/passage_flutter/PassageFlutter.kt +++ b/android/src/main/kotlin/id/passage/passage_flutter/PassageFlutter.kt @@ -103,9 +103,10 @@ internal class PassageFlutter(private val activity: Activity, appId: String) { fun newRegisterOneTimePasscode(call: MethodCall, result: MethodChannel.Result) { val identifier = call.argument("identifier") ?: return invalidArgumentError(result) + val language = call.argument("language") CoroutineScope(Dispatchers.IO).launch { try { - val otpId = passage.oneTimePasscode.register(identifier).otpId + val otpId = passage.oneTimePasscode.register(identifier, language).otpId result.success(otpId) } catch (e: Exception) { result.error(PassageFlutterError.OTP_ERROR.name, e.message, e.toString()) @@ -115,9 +116,10 @@ internal class PassageFlutter(private val activity: Activity, appId: String) { fun newLoginOneTimePasscode(call: MethodCall, result: MethodChannel.Result) { val identifier = call.argument("identifier") ?: return invalidArgumentError(result) + val language = call.argument("language") CoroutineScope(Dispatchers.IO).launch { try { - val otpId = passage.oneTimePasscode.login(identifier).otpId + val otpId = passage.oneTimePasscode.login(identifier, language).otpId result.success(otpId) } catch (e: Exception) { result.error(PassageFlutterError.OTP_ERROR.name, e.message, e.toString()) @@ -151,9 +153,10 @@ internal class PassageFlutter(private val activity: Activity, appId: String) { fun newRegisterMagicLink(call: MethodCall, result: MethodChannel.Result) { val identifier = call.argument("identifier") ?: return invalidArgumentError(result) + val language = call.argument("language") CoroutineScope(Dispatchers.IO).launch { try { - val magicLinkId = passage.magicLink.register(identifier).id + val magicLinkId = passage.magicLink.register(identifier, language).id result.success(magicLinkId) } catch (e: Exception) { result.error(PassageFlutterError.MAGIC_LINK_ERROR.name, e.message, e.toString()) @@ -163,9 +166,10 @@ internal class PassageFlutter(private val activity: Activity, appId: String) { fun newLoginMagicLink(call: MethodCall, result: MethodChannel.Result) { val identifier = call.argument("identifier") ?: return invalidArgumentError(result) + val language = call.argument("language") CoroutineScope(Dispatchers.IO).launch { try { - val magicLinkId = passage.magicLink.login(identifier).id + val magicLinkId = passage.magicLink.login(identifier, language).id result.success(magicLinkId) } catch (e: Exception) { result.error(PassageFlutterError.MAGIC_LINK_ERROR.name, e.message, e.toString()) @@ -419,12 +423,13 @@ internal class PassageFlutter(private val activity: Activity, appId: String) { fun changeEmail(call: MethodCall, result: MethodChannel.Result) { val newEmail = call.argument("newEmail") ?: return invalidArgumentError(result) + val language = call.argument("language") CoroutineScope(Dispatchers.IO).launch { try { val user = passage.currentUser.userInfo() ?: throw PassageUserUnauthorizedException( "User is not authorized." ) - val magicLinkId = passage.currentUser.changeEmail(newEmail)?.id + val magicLinkId = passage.currentUser.changeEmail(newEmail, language)?.id result.success(magicLinkId) } catch (e: Exception) { val error = when (e) { @@ -441,10 +446,11 @@ internal class PassageFlutter(private val activity: Activity, appId: String) { fun changePhone(call: MethodCall, result: MethodChannel.Result) { val newPhone = call.argument("newPhone") ?: return invalidArgumentError(result) + val language = call.argument("language") CoroutineScope(Dispatchers.IO).launch { try { val user = passage.currentUser.userInfo() - val magicLinkId = passage.currentUser.changePhone(newPhone)?.id + val magicLinkId = passage.currentUser.changePhone(newPhone, language)?.id result.success(magicLinkId) } catch (e: Exception) { val error = when (e) { diff --git a/integrationtestapp/integration_test/change_user_info_test.dart b/integrationtestapp/integration_test/change_user_info_test.dart index 7f6911d..1c4150e 100644 --- a/integrationtestapp/integration_test/change_user_info_test.dart +++ b/integrationtestapp/integration_test/change_user_info_test.dart @@ -47,7 +47,7 @@ void main() { await loginWithMagicLink(); final date = DateTime.now().millisecondsSinceEpoch; final identifier = 'authentigator+$date@passage.id'; - final response = await passage.currentUser.changeEmail(identifier); + final response = await passage.currentUser.changeEmail(identifier, language: "es"); expect(response, isNotNull); } catch (e) { fail('Test failed due to unexpected exception: $e'); diff --git a/integrationtestapp/integration_test/magic_link_test.dart b/integrationtestapp/integration_test/magic_link_test.dart index 555ea25..e909ba3 100644 --- a/integrationtestapp/integration_test/magic_link_test.dart +++ b/integrationtestapp/integration_test/magic_link_test.dart @@ -109,7 +109,7 @@ void main() { test('testActivateLoginMagicLink', () async { try { await passage.magiclink.login( - IntegrationTestConfig.existingUserEmailMagicLink); + IntegrationTestConfig.existingUserEmailMagicLink, language:"es"); await Future.delayed(const Duration( milliseconds: IntegrationTestConfig.waitTimeMilliseconds)); final magicLinkStr = await MailosaurAPIClient.getMostRecentMagicLink(); diff --git a/integrationtestapp/integration_test/otp_test.dart b/integrationtestapp/integration_test/otp_test.dart index 435903d..8417ed3 100644 --- a/integrationtestapp/integration_test/otp_test.dart +++ b/integrationtestapp/integration_test/otp_test.dart @@ -71,7 +71,7 @@ void main() { final identifier = "authentigator+$date@${MailosaurAPIClient.serverId}.mailosaur.net"; try { - final oneTimePasscode = (await passage.oneTimePasscode.register(identifier)); + final oneTimePasscode = (await passage.oneTimePasscode.register(identifier, language: 'es')); await Future.delayed(const Duration( milliseconds: IntegrationTestConfig.waitTimeMilliseconds)); final otp = await MailosaurAPIClient.getMostRecentOneTimePasscode(); diff --git a/integrationtestapp/ios/Podfile.lock b/integrationtestapp/ios/Podfile.lock index 53f60e8..bfd9449 100644 --- a/integrationtestapp/ios/Podfile.lock +++ b/integrationtestapp/ios/Podfile.lock @@ -3,7 +3,7 @@ PODS: - Flutter (1.0.0) - integration_test (0.0.1): - Flutter - - passage_flutter (1.0.0): + - passage_flutter (1.1.1): - Flutter - PassageSwift (= 1.0.2) - PassageSwift (1.0.2): @@ -30,8 +30,8 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: AnyCodable-FlightSchool: 9d48ed579c898378e189bb0a89faebf9eb8a46a7 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4 - passage_flutter: 60c06e8730750f0516d86fc14c4fb25087b827db + integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573 + passage_flutter: 46098def9413ab67515fd61d485a878ea1550dda PassageSwift: 0874d7abdd8e57e92c1ae6ead0d59c6024f8867b PODFILE CHECKSUM: 6a26cacf19c7fcde87d9c59e8f24986138e82f3b diff --git a/integrationtestapp/pubspec.lock b/integrationtestapp/pubspec.lock index 66a9f3c..56eae35 100644 --- a/integrationtestapp/pubspec.lock +++ b/integrationtestapp/pubspec.lock @@ -405,7 +405,7 @@ packages: path: ".." relative: true source: path - version: "1.1.0" + version: "1.1.1" path: dependency: transitive description: diff --git a/ios/Classes/PassageFlutter.swift b/ios/Classes/PassageFlutter.swift index 793ce4f..fa57e7a 100644 --- a/ios/Classes/PassageFlutter.swift +++ b/ios/Classes/PassageFlutter.swift @@ -85,9 +85,10 @@ internal class PassageFlutter { result(error) return } + let language = (arguments as? [String: Any])?["language"] as? String Task { do { - let otp = try await passage.oneTimePasscode.register(identifier: identifier) + let otp = try await passage.oneTimePasscode.register(identifier: identifier, language: language) result(otp.otpId) } catch { let error = FlutterError( @@ -106,9 +107,10 @@ internal class PassageFlutter { result(error) return } + let language = (arguments as? [String: Any])?["language"] as? String Task { do { - let otp = try await passage.oneTimePasscode.login(identifier: identifier) + let otp = try await passage.oneTimePasscode.login(identifier: identifier, language: language) result(otp.otpId) } catch { let error = FlutterError( @@ -154,9 +156,10 @@ internal class PassageFlutter { result(error) return } + let language = (arguments as? [String: Any])?["language"] as? String Task { do { - let ml = try await passage.magicLink.register(identifier: identifier) + let ml = try await passage.magicLink.register(identifier: identifier, language: language) result(ml.id) } catch { let error = FlutterError( @@ -175,9 +178,10 @@ internal class PassageFlutter { result(error) return } + let language = (arguments as? [String: Any])?["language"] as? String Task { do { - let ml = try await passage.magicLink.login(identifier: identifier) + let ml = try await passage.magicLink.login(identifier: identifier, language: language) result(ml.id) } catch { let error = FlutterError( @@ -421,14 +425,15 @@ internal class PassageFlutter { } internal func changeEmail(arguments: Any?, result: @escaping FlutterResult) { - guard let newEmail = (arguments as? [String: String])?["newEmail"] else { + guard let newEmail = (arguments as? [String: Any])?["newEmail"] as? String else { let error = PassageFlutterError.INVALID_ARGUMENT.defaultFlutterError result(error) return } + let language = (arguments as? [String: Any])?["language"] as? String Task { do { - let magicLink = try await passage.currentUser.changeEmail(newEmail: newEmail) + let magicLink = try await passage.currentUser.changeEmail(newEmail: newEmail, language: language) result(magicLink.id) } catch let error as CurrentUserError { result(handleCurrentUserError(error)) @@ -444,14 +449,15 @@ internal class PassageFlutter { } internal func changePhone(arguments: Any?, result: @escaping FlutterResult) { - guard let newPhone = (arguments as? [String: String])?["newPhone"] else { + guard let newPhone = (arguments as? [String: Any])?["newPhone"] as? String else { let error = PassageFlutterError.INVALID_ARGUMENT.defaultFlutterError result(error) return } + let language = (arguments as? [String: Any])?["language"] as? String Task { do { - let magicLink = try await passage.currentUser.changePhone(newPhone: newPhone) + let magicLink = try await passage.currentUser.changePhone(newPhone: newPhone, language: language) result(magicLink.id) } catch let error as CurrentUserError { result(handleCurrentUserError(error)) diff --git a/lib/passage_current_user.dart b/lib/passage_current_user.dart index 858efc1..05cdab8 100644 --- a/lib/passage_current_user.dart +++ b/lib/passage_current_user.dart @@ -22,6 +22,7 @@ class PassageCurrentUser { /// /// Parameters: /// - `newEmail`: The user's new email. + /// - `language`: The language code for the email change. /// /// Returns: /// A `Future` representing the magic link @@ -29,7 +30,7 @@ class PassageCurrentUser { /// Throws: /// `PassageError` Future changeEmail(String newEmail, {String? language}) { - return PassageFlutterPlatform.instance.changeEmail(newEmail); + return PassageFlutterPlatform.instance.changeEmail(newEmail, language); } /// Initiates a phone number change for the authenticated user. A phone change requires verification, @@ -37,6 +38,7 @@ class PassageCurrentUser { /// /// Parameters: /// - `newPhone`: The user's new phone number. + /// - The language code for the phone change. /// /// Returns: /// A `Future` representing the magic link @@ -44,7 +46,7 @@ class PassageCurrentUser { /// Throws: /// `PassageError` Future changePhone(String newPhone, {String? language}) { - return PassageFlutterPlatform.instance.changePhone(newPhone); + return PassageFlutterPlatform.instance.changePhone(newPhone, language); } /// Attempts to create and register a new passkey for the authenticated user. diff --git a/lib/passage_flutter_platform/passage_flutter_method_channel.dart b/lib/passage_flutter_platform/passage_flutter_method_channel.dart index be0c232..0d16217 100644 --- a/lib/passage_flutter_platform/passage_flutter_method_channel.dart +++ b/lib/passage_flutter_platform/passage_flutter_method_channel.dart @@ -63,10 +63,10 @@ class MethodChannelPassageFlutter extends PassageFlutterPlatform { // OTP METHODS @override - Future newRegisterOneTimePasscode(String identifier) async { + Future newRegisterOneTimePasscode(String identifier, String? language) async { try { final result = await methodChannel.invokeMethod( - 'newRegisterOneTimePasscode', {'identifier': identifier}); + 'newRegisterOneTimePasscode', {'identifier': identifier, 'language': language}); return result!; } catch (e) { throw PassageError.fromObject(object: e); @@ -74,10 +74,10 @@ class MethodChannelPassageFlutter extends PassageFlutterPlatform { } @override - Future newLoginOneTimePasscode(String identifier) async { + Future newLoginOneTimePasscode(String identifier, String? language) async { try { final result = await methodChannel.invokeMethod( - 'newLoginOneTimePasscode', {'identifier': identifier}); + 'newLoginOneTimePasscode', {'identifier': identifier, 'language': language}); return result!; } catch (e) { throw PassageError.fromObject(object: e); @@ -98,10 +98,10 @@ class MethodChannelPassageFlutter extends PassageFlutterPlatform { // MAGIC LINK METHODS @override - Future newRegisterMagicLink(String identifier) async { + Future newRegisterMagicLink(String identifier, String? language) async { try { final result = await methodChannel.invokeMethod( - 'newRegisterMagicLink', {'identifier': identifier}); + 'newRegisterMagicLink', {'identifier': identifier, 'language': language}); return result!; } catch (e) { throw PassageError.fromObject(object: e); @@ -109,10 +109,10 @@ class MethodChannelPassageFlutter extends PassageFlutterPlatform { } @override - Future newLoginMagicLink(String identifier) async { + Future newLoginMagicLink(String identifier, String? language) async { try { final result = await methodChannel.invokeMethod( - 'newLoginMagicLink', {'identifier': identifier}); + 'newLoginMagicLink', {'identifier': identifier, 'language': language}); return result!; } catch (e) { throw PassageError.fromObject(object: e); @@ -316,10 +316,10 @@ class MethodChannelPassageFlutter extends PassageFlutterPlatform { } @override - Future changeEmail(String newEmail) async { + Future changeEmail(String newEmail, String? language) async { try { final magicLinkId = await methodChannel - .invokeMethod('changeEmail', {'newEmail': newEmail}); + .invokeMethod('changeEmail', {'newEmail': newEmail, 'language': language}); return MagicLink(magicLinkId!); } catch (e) { throw PassageError.fromObject(object: e); @@ -327,10 +327,10 @@ class MethodChannelPassageFlutter extends PassageFlutterPlatform { } @override - Future changePhone(String newPhone) async { + Future changePhone(String newPhone, String? language) async { try { final magicLinkId = await methodChannel - .invokeMethod('changePhone', {'newPhone': newPhone}); + .invokeMethod('changePhone', {'newPhone': newPhone, 'language': language}); return MagicLink(magicLinkId!); } catch (e) { throw PassageError.fromObject(object: e); diff --git a/lib/passage_flutter_platform/passage_flutter_platform_interface.dart b/lib/passage_flutter_platform/passage_flutter_platform_interface.dart index dd5e370..876d727 100644 --- a/lib/passage_flutter_platform/passage_flutter_platform_interface.dart +++ b/lib/passage_flutter_platform/passage_flutter_platform_interface.dart @@ -54,12 +54,12 @@ abstract class PassageFlutterPlatform extends PlatformInterface { // OTP METHODS - Future newRegisterOneTimePasscode(String identifier) { + Future newRegisterOneTimePasscode(String identifier, String? language) { throw UnimplementedError( 'newRegisterOneTimePasscode() has not been implemented.'); } - Future newLoginOneTimePasscode(String identifier) { + Future newLoginOneTimePasscode(String identifier, String? language) { throw UnimplementedError( 'newLoginOneTimePasscode() has not been implemented.'); } @@ -71,12 +71,12 @@ abstract class PassageFlutterPlatform extends PlatformInterface { // MAGIC LINK METHODS - Future newRegisterMagicLink(String identifier) { + Future newRegisterMagicLink(String identifier, String? language) { throw UnimplementedError( 'newRegisterMagicLink() has not been implemented.'); } - Future newLoginMagicLink(String identifier) { + Future newLoginMagicLink(String identifier, String? language) { throw UnimplementedError('newLoginMagicLink() has not been implemented.'); } @@ -153,11 +153,11 @@ abstract class PassageFlutterPlatform extends PlatformInterface { throw UnimplementedError('editPasskeyName() has not been implemented.'); } - Future changeEmail(String newEmail) { + Future changeEmail(String newEmail, String? language) { throw UnimplementedError('changeEmail() has not been implemented.'); } - Future changePhone(String newPhone) { + Future changePhone(String newPhone, String? language) { throw UnimplementedError('changePhone() has not been implemented.'); } diff --git a/lib/passage_flutter_web.dart b/lib/passage_flutter_web.dart index 49b24d0..44885a1 100644 --- a/lib/passage_flutter_web.dart +++ b/lib/passage_flutter_web.dart @@ -98,12 +98,12 @@ class PassageFlutterWeb extends PassageFlutterPlatform { // OTP METHODS @override - Future newRegisterOneTimePasscode(String identifier) async { + Future newRegisterOneTimePasscode(String identifier, String? language) async { try { - final resultPromise = passage.oneTimePasscode.register(identifier); + final resultPromise = passage.oneTimePasscode.register(identifier, language); final jsObject = await js_util.promiseToFuture(resultPromise); final resultMap = jsObjectToMap(jsObject); - return resultMap['otp_id']; + return resultMap['otpId']; } catch (e) { throw PassageError.fromObject( object: e, overrideCode: PassageErrorCode.otpError); @@ -111,9 +111,9 @@ class PassageFlutterWeb extends PassageFlutterPlatform { } @override - Future newLoginOneTimePasscode(String identifier) async { + Future newLoginOneTimePasscode(String identifier, String? language) async { try { - final resultPromise = passage.oneTimePasscode.login(identifier); + final resultPromise = passage.oneTimePasscode.login(identifier, language); final jsObject = await js_util.promiseToFuture(resultPromise); final resultMap = jsObjectToMap(jsObject); return resultMap['otpId']; @@ -140,9 +140,9 @@ class PassageFlutterWeb extends PassageFlutterPlatform { // MAGIC LINK METHODS @override - Future newRegisterMagicLink(String identifier) async { + Future newRegisterMagicLink(String identifier, String? language) async { try { - final resultPromise = passage.magicLink.register(identifier); + final resultPromise = passage.magicLink.register(identifier, language); final jsObject = await js_util.promiseToFuture(resultPromise); final resultMap = jsObjectToMap(jsObject); return resultMap['id']; @@ -153,9 +153,9 @@ class PassageFlutterWeb extends PassageFlutterPlatform { } @override - Future newLoginMagicLink(String identifier) async { + Future newLoginMagicLink(String identifier, String? language) async { try { - final resultPromise = passage.magicLink.login(identifier); + final resultPromise = passage.magicLink.login(identifier, language); final jsObject = await js_util.promiseToFuture(resultPromise); final resultMap = jsObjectToMap(jsObject); return resultMap['id']; @@ -372,9 +372,9 @@ class PassageFlutterWeb extends PassageFlutterPlatform { } @override - Future changeEmail(String newEmail) async { + Future changeEmail(String newEmail, String? language) async { try { - final resultPromise = passage.currentUser.changeEmail(newEmail); + final resultPromise = passage.currentUser.changeEmail(newEmail, language); final jsObject = await js_util.promiseToFuture(resultPromise); return MagicLink(jsObject.id); } catch (e) { @@ -384,9 +384,9 @@ class PassageFlutterWeb extends PassageFlutterPlatform { } @override - Future changePhone(String newPhone) async { + Future changePhone(String newPhone, String? language) async { try { - final resultPromise = passage.currentUser.changePhone(newPhone); + final resultPromise = passage.currentUser.changePhone(newPhone, language); final jsObject = await js_util.promiseToFuture(resultPromise); return MagicLink(jsObject.id); } catch (e) { diff --git a/lib/passage_magliclink.dart b/lib/passage_magliclink.dart index 2628d67..d066c42 100644 --- a/lib/passage_magliclink.dart +++ b/lib/passage_magliclink.dart @@ -17,7 +17,7 @@ class PassageMagiclink { /// Throws: /// `PassageError` Future register(String identifier, {String? language}) async { - String magicLinkId = await PassageFlutterPlatform.instance.newRegisterMagicLink(identifier); + String magicLinkId = await PassageFlutterPlatform.instance.newRegisterMagicLink(identifier, language); return MagicLink(magicLinkId); } @@ -35,7 +35,7 @@ class PassageMagiclink { /// Throws: /// `PassageError` Future login(String identifier, {String? language}) async { - String magicLinkId = await PassageFlutterPlatform.instance.newLoginMagicLink(identifier); + String magicLinkId = await PassageFlutterPlatform.instance.newLoginMagicLink(identifier, language); return MagicLink(magicLinkId); } diff --git a/lib/passage_otp.dart b/lib/passage_otp.dart index 3bab59f..0db7912 100644 --- a/lib/passage_otp.dart +++ b/lib/passage_otp.dart @@ -18,7 +18,7 @@ class PassageOneTimePasscode { /// `PassageError` Future register(String identifier, {String? language}) async { String oneTimePasscodeId = await PassageFlutterPlatform.instance - .newRegisterOneTimePasscode(identifier); + .newRegisterOneTimePasscode(identifier, language); return OneTimePasscode(oneTimePasscodeId); } @@ -37,7 +37,7 @@ class PassageOneTimePasscode { /// `PassageError` Future login(String identifier, {String? language}) async { - String oneTimePasscodeId = await PassageFlutterPlatform.instance.newLoginOneTimePasscode(identifier); + String oneTimePasscodeId = await PassageFlutterPlatform.instance.newLoginOneTimePasscode(identifier, language); return OneTimePasscode(oneTimePasscodeId); } diff --git a/lib/passage_social.dart b/lib/passage_social.dart index 3fd02e8..4d48fd7 100644 --- a/lib/passage_social.dart +++ b/lib/passage_social.dart @@ -3,15 +3,45 @@ import 'passage_flutter_models/passage_social_connection.dart'; import 'passage_flutter_platform/passage_flutter_platform_interface.dart'; class PassageSocial { + /// Initiates the authorization process using a third-party social provider. + /// + /// This method opens the social provider's authentication interface. + /// The behavior may vary based on the platform and the underlying implementation. + /// + /// - [connection]: The social connection to use for login (e.g., Google, Apple). + /// + /// Returns a `Future` that completes when the authorization process is initiated. + /// Throws: + /// `PassageError` Future authorize(SocialConnection connection) { - return PassageFlutterPlatform.instance.authorizeWith(connection); + return PassageFlutterPlatform.instance.authorizeWith(connection); } + /// Completes the social authentication process using the provided authorization code. + /// + /// After receiving the authorization code from the social provider, + /// this method exchanges it for authentication credentials. + /// + /// - [code]: The authorization code received from the social provider. + /// + /// Returns a `Future` containing the authentication result. + /// Throws: + /// `PassageError` Future finish(String code) { return PassageFlutterPlatform.instance.finishSocialAuthentication(code); } + /// Initiates the authorization process for iOS-specific social providers (IOS Only). + /// + /// This method is specific to iOS and allows for platform-specific behavior + /// when authorizing with social providers. + /// + /// - [connection]: The social connection to use for login (e.g., Google, Apple). + /// + /// Returns a `Future` containing the authentication result. + /// Throws: + /// `PassageError` Future authorizeIOS(SocialConnection connection) { return PassageFlutterPlatform.instance.authorizeIOSWith(connection); } -} \ No newline at end of file +}