diff --git a/android/src/main/kotlin/com/authsignal/authsignal_flutter/AuthsignalPlugin.kt b/android/src/main/kotlin/com/authsignal/authsignal_flutter/AuthsignalPlugin.kt index a7bd547..ccfd7bb 100644 --- a/android/src/main/kotlin/com/authsignal/authsignal_flutter/AuthsignalPlugin.kt +++ b/android/src/main/kotlin/com/authsignal/authsignal_flutter/AuthsignalPlugin.kt @@ -9,6 +9,7 @@ import com.authsignal.passkey.AuthsignalPasskey import com.authsignal.push.AuthsignalPush import com.authsignal.sms.AuthsignalSMS import com.authsignal.totp.AuthsignalTOTP +import com.authsignal.whatsapp.AuthsignalWhatsApp import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding @@ -28,6 +29,7 @@ class AuthsignalPlugin: FlutterPlugin, ActivityAware, MethodCallHandler { private lateinit var email: AuthsignalEmail private lateinit var sms: AuthsignalSMS private lateinit var totp: AuthsignalTOTP + private lateinit var whatsapp: AuthsignalWhatsApp private var activity: Activity? = null private var context: Context? = null @@ -54,6 +56,7 @@ class AuthsignalPlugin: FlutterPlugin, ActivityAware, MethodCallHandler { email = AuthsignalEmail(tenantID, baseURL) sms = AuthsignalSMS(tenantID, baseURL) totp = AuthsignalTOTP(tenantID, baseURL) + whatsapp = AuthsignalWhatsApp(tenantID, baseURL) result.success(null) } @@ -309,6 +312,38 @@ class AuthsignalPlugin: FlutterPlugin, ActivityAware, MethodCallHandler { } } + "whatsapp.challenge" -> { + coroutineScope.launch { + val response = whatsapp.challenge() + + handleResponse(response, result)?.let { + val data = mapOf( + "challengeId" to it.challengeId, + ) + + result.success(data) + } + } + } + + "whatsapp.verify" -> { + val code = call.argument("code")!! + + coroutineScope.launch { + val response = whatsapp.verify(code) + + handleResponse(response, result)?.let { + val data = mapOf( + "isVerified" to it.isVerified, + "token" to it.token, + "failureReason" to it.failureReason, + ) + + result.success(data) + } + } + } + "setToken" -> { val token = call.argument("token")!! diff --git a/ios/Classes/AuthsignalPlugin.swift b/ios/Classes/AuthsignalPlugin.swift index 26fa337..007a245 100644 --- a/ios/Classes/AuthsignalPlugin.swift +++ b/ios/Classes/AuthsignalPlugin.swift @@ -8,6 +8,7 @@ public class AuthsignalPlugin: NSObject, FlutterPlugin { var email: AuthsignalEmail? var sms: AuthsignalSMS? var totp: AuthsignalTOTP? + var whatsapp: AuthsignalWhatsApp? public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: "authsignal", binaryMessenger: registrar.messenger()) @@ -29,6 +30,7 @@ public class AuthsignalPlugin: NSObject, FlutterPlugin { self.email = AuthsignalEmail(tenantID: tenantID, baseURL: baseURL) self.sms = AuthsignalSMS(tenantID: tenantID, baseURL: baseURL) self.totp = AuthsignalTOTP(tenantID: tenantID, baseURL: baseURL) + self.whatsapp = AuthsignalWhatsApp(tenantID: tenantID, baseURL: baseURL) result(nil) @@ -341,6 +343,43 @@ public class AuthsignalPlugin: NSObject, FlutterPlugin { result(verifyResponse) } } + + case "whatsapp.challenge": + Task.init { + let response = await self.whatsapp!.challenge() + + if response.error != nil { + let error = FlutterError(code: response.errorCode ?? "unexpected_error", message: response.error, details: ""); + result(error) + } else { + let challengeResponse: [String: Any?] = [ + "challengeId": response.data!.challengeId, + ] + + result(challengeResponse) + } + } + + case "whatsapp.verify": + let arguments = call.arguments as! [String: Any] + let code = arguments["code"] as! String + + Task.init { + let response = await self.whatsapp!.verify(code: code) + + if response.error != nil { + let error = FlutterError(code: response.errorCode ?? "unexpected_error", message: response.error, details: ""); + result(error) + } else { + let verifyResponse: [String: Any?] = [ + "isVerified": response.data!.isVerified, + "token": response.data!.token, + "failureReason": response.data!.failureReason, + ] + + result(verifyResponse) + } + } case "setToken": let arguments = call.arguments as! [String: Any] diff --git a/lib/authsignal_flutter.dart b/lib/authsignal_flutter.dart index 80996b2..b0c4314 100644 --- a/lib/authsignal_flutter.dart +++ b/lib/authsignal_flutter.dart @@ -6,6 +6,7 @@ import 'src/authsignal_push.dart'; import 'src/authsignal_email.dart'; import 'src/authsignal_sms.dart'; import 'src/authsignal_totp.dart'; +import 'src/authsignal_whatsapp.dart'; export 'src/types.dart' show AuthsignalResponse, TokenPayload, ErrorCode; @@ -18,6 +19,7 @@ class Authsignal { late AuthsignalEmail email; late AuthsignalSms sms; late AuthsignalTotp totp; + late AuthsignalWhatsapp whatsapp; bool _initialized = false; @@ -27,6 +29,7 @@ class Authsignal { email = AuthsignalEmail(initCheck: initCheck); sms = AuthsignalSms(initCheck: initCheck); totp = AuthsignalTotp(initCheck: initCheck); + whatsapp = AuthsignalWhatsapp(initCheck: initCheck); } Future initCheck() async { diff --git a/lib/src/authsignal_whatsapp.dart b/lib/src/authsignal_whatsapp.dart new file mode 100644 index 0000000..9740347 --- /dev/null +++ b/lib/src/authsignal_whatsapp.dart @@ -0,0 +1,47 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'types.dart'; + +class AuthsignalWhatsapp { + final AsyncCallback initCheck; + + AuthsignalWhatsapp({required this.initCheck}); + + @visibleForTesting + final methodChannel = const MethodChannel('authsignal'); + + Future> challenge() async { + await initCheck(); + + try { + final data = await methodChannel.invokeMapMethod('whatsapp.challenge'); + + if (data != null) { + return AuthsignalResponse(data: ChallengeResponse.fromMap(data)); + } else { + return AuthsignalResponse(data: null); + } + } on PlatformException catch (ex) { + return AuthsignalResponse.fromError(ex); + } + } + + Future> verify(String code) async { + await initCheck(); + + var arguments = {'code': code}; + + try { + final data = await methodChannel.invokeMapMethod('whatsapp.verify', arguments); + + if (data != null) { + return AuthsignalResponse(data: VerifyResponse.fromMap(data)); + } else { + return AuthsignalResponse(data: null); + } + } on PlatformException catch (ex) { + return AuthsignalResponse.fromError(ex); + } + } +} diff --git a/pubspec.yaml b/pubspec.yaml index ff13bf6..39930b9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: authsignal_flutter description: "The Authsignal Flutter SDK for Passkeys and Push Auth" -version: 1.1.3 +version: 1.2.0 homepage: "https://github.com/authsignal/authsignal-flutter" environment: