From f6faae595e0ddc290b8565454b26921511842b16 Mon Sep 17 00:00:00 2001 From: Sina <65910646+SinaSeylani@users.noreply.github.com> Date: Mon, 15 Jul 2024 11:28:15 -0600 Subject: [PATCH] Clean up - new functions on ios side --- .../passage/passage_flutter/PassageFlutter.kt | 19 ++++++- .../passage_flutter/PassageFlutterPlugin.kt | 1 + ios/Classes/PassageFlutter.swift | 37 ++++++------- ios/Classes/PassageFlutterPlugin.swift | 8 +-- lib/passage_flutter.dart | 30 ++++++++++- .../auth_result_with_id_token.dart | 8 +++ .../passage_error_code.dart | 3 ++ .../passage_flutter_method_channel.dart | 52 ++++++++++++++++--- .../passage_flutter_platform_interface.dart | 13 ++++- 9 files changed, 135 insertions(+), 36 deletions(-) create mode 100644 lib/passage_flutter_models/auth_result_with_id_token.dart 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 41bf604c..4ce995c5 100644 --- a/android/src/main/kotlin/id/passage/passage_flutter/PassageFlutter.kt +++ b/android/src/main/kotlin/id/passage/passage_flutter/PassageFlutter.kt @@ -456,8 +456,8 @@ internal class PassageFlutter(private val activity: Activity, appId: String? = n ?: return invalidArgumentError(result) CoroutineScope(Dispatchers.IO).launch { try { - val user = passage.hostedAuthFinish(code, clientSecret, state) - result.success(null) + val AuthResultWithIdToken = passage.hostedAuthFinish(code, clientSecret, state) + result.success(AuthResultWithIdToken) } catch (e: Exception) { val error = PassageFlutterError.FINISH_HOSTED_AUTH_ERROR result.error(error.name, e.message, e.toString()) @@ -476,6 +476,21 @@ internal class PassageFlutter(private val activity: Activity, appId: String? = n } } } + + + fun hostedLogout(call: MethodCall, result: MethodChannel.Result) { + val idToken = call.argument("idToken") + ?: return invalidArgumentError(result) + CoroutineScope(Dispatchers.IO).launch { + try { + passage.hostedLogout(idToken) + result.success(null) + } catch (e: Exception) { + val error = PassageFlutterError.LOGOUT_HOSTED_AUTH_ERROR + result.error(error.name, e.message, e.toString()) + } + } + } // endregion diff --git a/android/src/main/kotlin/id/passage/passage_flutter/PassageFlutterPlugin.kt b/android/src/main/kotlin/id/passage/passage_flutter/PassageFlutterPlugin.kt index 9d821aaf..7c5591b6 100644 --- a/android/src/main/kotlin/id/passage/passage_flutter/PassageFlutterPlugin.kt +++ b/android/src/main/kotlin/id/passage/passage_flutter/PassageFlutterPlugin.kt @@ -74,6 +74,7 @@ class PassageFlutterPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { "hostedAuthStart" -> passageFlutter?.hostedAuthStart(result) "hostedAuthFinish" -> passageFlutter?.hostedAuthFinish(call, result) "hostedLogout" -> passageFlutter?.hostedLogout(result) + "hostedLogoutWithIdToken" -> passageFlutter?.hostedLogout(call, result) else -> { result.notImplemented() } diff --git a/ios/Classes/PassageFlutter.swift b/ios/Classes/PassageFlutter.swift index 6fbd124e..9dffb1c4 100644 --- a/ios/Classes/PassageFlutter.swift +++ b/ios/Classes/PassageFlutter.swift @@ -527,11 +527,16 @@ internal class PassageFlutter { } } - internal func hostedAuthStart(result: @escaping FlutterResult) { + internal func hostedAuth(result: @escaping FlutterResult) { + guard let clientSecret = (arguments as? [String: String])?["clientSecret"] else { + let error = PassageFlutterError.INVALID_ARGUMENT.defaultFlutterError + result(error) + return + } Task { do { - try await passage.hostedAuthStart() - result(nil) + val AuthResultWithIdToken = try await passage.hostedAuth(clientSecret) + result(AuthResultWithIdToken) } catch { let error = FlutterError( code: PassageFlutterError.START_HOSTED_AUTH_ERROR.rawValue, @@ -543,23 +548,14 @@ internal class PassageFlutter { } } - internal func hostedAuthFinish(arguments: Any?, result: @escaping FlutterResult) { - guard let args = arguments as? [String: Any], - let code = args["code"] as? String, - let clientSecret = args["clientSecret"] as? String, - let state = args["state"] as? String else { - let error = PassageFlutterError.INVALID_ARGUMENT.defaultFlutterError - result(error) - return - } - + internal func hostedLogout(result: @escaping FlutterResult) { Task { do { - let user = try await passage.hostedAuthFinish(code: code, clientSecret: clientSecret, state: state) - result(nil) // Successfully authenticated + try await passage.hostedLogout() + result(nil) } catch { let error = FlutterError( - code: PassageFlutterError.FINISH_HOSTED_AUTH_ERROR.rawValue, + code: PassageFlutterError.LOGOUT_HOSTED_AUTH_ERROR.rawValue, message: error.localizedDescription, details: nil ) @@ -568,10 +564,15 @@ internal class PassageFlutter { } } - internal func hostedLogout(result: @escaping FlutterResult) { + internal func hostedLogout(arguments: Any?, result: @escaping FlutterResult) { + guard let idToken = (arguments as? [String: String])?["idToken"] else { + let error = PassageFlutterError.INVALID_ARGUMENT.defaultFlutterError + result(error) + return + } Task { do { - try await passage.hostedLogout() + try await passage.hostedLogout(idToken) result(nil) } catch { let error = FlutterError( diff --git a/ios/Classes/PassageFlutterPlugin.swift b/ios/Classes/PassageFlutterPlugin.swift index 24570bc9..653df8bf 100644 --- a/ios/Classes/PassageFlutterPlugin.swift +++ b/ios/Classes/PassageFlutterPlugin.swift @@ -76,12 +76,12 @@ public class PassageFlutterPlugin: NSObject, FlutterPlugin { passageFlutter.changePhone(arguments: call.arguments, result: result) case "identifierExists": passageFlutter.identifierExists(arguments: call.arguments, result: result) - case "hostedAuthStart": - passageFlutter.hostedAuthStart(result: result) - case "hostedAuthFinish": - passageFlutter.hostedAuthFinish(arguments: call.arguments, result: result) + case "hostedAuth": + passageFlutter.hostedAuth(arguments: call.arguments,result: result) case "hostedLogout": passageFlutter.hostedLogout(result: result) + case "hostedLogoutWithIdToken": + passageFlutter.hostedLogout(arguments: call.arguments, result: result) default: result(FlutterMethodNotImplemented) } diff --git a/lib/passage_flutter.dart b/lib/passage_flutter.dart index 7894e0d5..b69b3c90 100644 --- a/lib/passage_flutter.dart +++ b/lib/passage_flutter.dart @@ -1,4 +1,5 @@ import 'passage_flutter_models/auth_result.dart'; +import 'passage_flutter_models/auth_result_with_id_token.dart'; import 'passage_flutter_models/authenticator_attachment.dart'; import 'passage_flutter_models/passage_app_info.dart'; import 'passage_flutter_models/passage_social_connection.dart'; @@ -354,7 +355,7 @@ class PassageFlutter { return PassageFlutterPlatform.instance.changePhone(newPhone); } - // OIDC Methods + // Hosted Auth Methods /// Authentication Method for Hosted Apps /// @@ -366,6 +367,19 @@ class PassageFlutter { return PassageFlutterPlatform.instance.hostedAuthStart(); } + // Hosted Auth Methods + + /// Authentication Method for Hosted Apps + /// ONLY FOR iOS + /// If your Passage app is Hosted, use this method to register and log in your user. This method will open up a Passage login experience + /// + /// Throws: + /// `PassageError` + + Future hostedAuthIOS(String clientSecret) { + return PassageFlutterPlatform.instance.hostedAuthIOS(clientSecret); + } + /// Finish Hosted Auth /// /// Finishes a Hosted login/sign up by exchanging the auth code for Passage tokens. @@ -376,7 +390,7 @@ class PassageFlutter { /// Throws: /// `PassageError` - Future hostedAuthFinish(String code, String clientSecret, String state) { + Future hostedAuthFinish(String code, String clientSecret, String state) { return PassageFlutterPlatform.instance.hostedAuthFinish(code, clientSecret, state); } @@ -390,4 +404,16 @@ class PassageFlutter { return PassageFlutterPlatform.instance.hostedLogout(); } + // Logout Method for Hosted Apps + /// + /// If your Passage app is Hosted, use this method to log out your user. This method will briefly open up a web view where it will log out the + /// Parameters: + /// idToken: The auth id token, used to log the user our of any remaining web sessions. + /// Throws: + /// `PassageError` + + Future hostedLogoutWithIdToken(String idToken) { + return PassageFlutterPlatform.instance.hostedLogoutWithIdToken(idToken); + } + } diff --git a/lib/passage_flutter_models/auth_result_with_id_token.dart b/lib/passage_flutter_models/auth_result_with_id_token.dart new file mode 100644 index 00000000..ee41f708 --- /dev/null +++ b/lib/passage_flutter_models/auth_result_with_id_token.dart @@ -0,0 +1,8 @@ +import 'auth_result.dart'; + +class AuthResultWithIdToken { + final AuthResult authResult; + final String idToken; + + AuthResultWithIdToken(this.authResult, this.idToken); +} diff --git a/lib/passage_flutter_models/passage_error_code.dart b/lib/passage_flutter_models/passage_error_code.dart index bfa6f20e..67139a94 100644 --- a/lib/passage_flutter_models/passage_error_code.dart +++ b/lib/passage_flutter_models/passage_error_code.dart @@ -11,6 +11,9 @@ class PassageErrorCode { static const appInfoError = 'APP_INFO_ERROR'; static const changeEmailError = 'CHANGE_EMAIL_ERROR'; static const changePhoneError = 'CHANGE_PHONE_ERROR'; + static const hostedAuthStart = 'HOSTED_AUTH_START'; + static const hostedAuthFinish = 'HOSTED_AUTH_FINISH'; + static const hostedAuthIOS = 'HOSTED_AUTH_IOS'; static const identifierExistsError = 'IDENTIFIER_EXISTS_ERROR'; static const otpActivationExceededAttempts = 'OTP_ACTIVATION_EXCEEDED_ATTEMPTS'; diff --git a/lib/passage_flutter_platform/passage_flutter_method_channel.dart b/lib/passage_flutter_platform/passage_flutter_method_channel.dart index cbfa0b2d..28b8eb27 100644 --- a/lib/passage_flutter_platform/passage_flutter_method_channel.dart +++ b/lib/passage_flutter_platform/passage_flutter_method_channel.dart @@ -3,6 +3,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:passage_flutter/passage_flutter_models/passage_error_code.dart'; +import '../passage_flutter_models/auth_result_with_id_token.dart'; import '../passage_flutter_models/passage_social_connection.dart'; import '/passage_flutter_models/auth_result.dart'; import '/passage_flutter_models/authenticator_attachment.dart'; @@ -329,22 +330,46 @@ class MethodChannelPassageFlutter extends PassageFlutterPlatform { } @override - Future hostedAuthStart() async { + Future hostedAuthStart() async { + if (Platform.isIOS) { + throw PassageError( + code: PassageErrorCode.hostedAuthStart, + message: 'Not supported on iOS. Use hostedAuthIOS instead.'); + } try { - final magicLinkId = await methodChannel - .invokeMethod('hostedAuthStart'); - return magicLinkId!; + await methodChannel.invokeMethod('hostedAuthStart'); } catch (e) { throw PassageError.fromObject(object: e); } } @override - Future hostedAuthFinish(String code, String clientSecret, String state) async { + Future hostedAuthIOS(String clientSecret) async { + if (!Platform.isIOS) { + throw PassageError( + code: PassageErrorCode.hostedAuthFinish, + message: 'Only supported on iOS. Use hostedAuthStart instead.'); + } try { - final magicLinkId = await methodChannel - .invokeMethod('hostedAuthFinish', {'code': code, 'clientSecret': clientSecret, 'state': state}); - return magicLinkId!; + final authResultWithIdToken = await methodChannel + .invokeMethod('hostedAuth', {'clientSecret': clientSecret}); + return authResultWithIdToken!; + } catch (e) { + throw PassageError.fromObject(object: e); + } + } + + @override + Future hostedAuthFinish(String code, String clientSecret, String state) async { + if (Platform.isIOS) { + throw PassageError( + code: PassageErrorCode.hostedAuthFinish, + message: 'Not supported on iOS. Use hostedAuthIOS instead.'); + } + try { + final authResultWithIdToken = await methodChannel + .invokeMethod('hostedAuthFinish', {'code': code, 'clientSecret': clientSecret, 'state': state}); + return authResultWithIdToken!; } catch (e) { throw PassageError.fromObject(object: e); } @@ -361,4 +386,15 @@ class MethodChannelPassageFlutter extends PassageFlutterPlatform { } } + @override + Future hostedLogoutWithIdToken(String idToken) async { + try { + final magicLinkId = await methodChannel + .invokeMethod('hostedLogoutWithIdToken', {'idToken': idToken}); + return 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 9c756585..961d567b 100644 --- a/lib/passage_flutter_platform/passage_flutter_platform_interface.dart +++ b/lib/passage_flutter_platform/passage_flutter_platform_interface.dart @@ -1,5 +1,6 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import '../passage_flutter_models/auth_result_with_id_token.dart'; import '/passage_flutter_models/auth_result.dart'; import '/passage_flutter_models/authenticator_attachment.dart'; import '/passage_flutter_models/passage_app_info.dart'; @@ -158,15 +159,23 @@ abstract class PassageFlutterPlatform extends PlatformInterface { throw UnimplementedError('changePhone() has not been implemented.'); } - Future hostedAuthStart() { + Future hostedAuthStart() { throw UnimplementedError('hostedAuthStart() has not been implemented.'); } - Future hostedAuthFinish(String code, String clientSecret, String state) { + Future hostedAuthIOS(String clientSecret) { + throw UnimplementedError('hostedAuthStart() has not been implemented.'); + } + + Future hostedAuthFinish(String code, String clientSecret, String state) { throw UnimplementedError('hostedAuthFinish() has not been implemented.'); } Future hostedLogout() { throw UnimplementedError('hostedLogout() has not been implemented.'); } + + Future hostedLogoutWithIdToken(String idToken) { + throw UnimplementedError('hostedLogoutWithIdToken() has not been implemented.'); + } }