diff --git a/CHANGELOG.md b/CHANGELOG.md index d822305..a0b123a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 1.7.0 + +- Updated iOS SDK + to [4.5.1](https://github.com/Keyri-Co/keyri-ios-whitelabel-sdk/releases/tag/4.5.1) +- Updated Android SDK + to [4.2.3](https://github.com/Keyri-Co/keyri-android-whitelabel-sdk/releases/tag/4.2.3) +- Added new `createFingerprint` method +- Fixed timestamp length on Android +- Updated events structure and dded metadata field + ## 1.6.2 - Updated plugin_platform_interface and bump example dependencies @@ -7,7 +17,7 @@ - Updated iOS SDK to [4.4.1](https://github.com/Keyri-Co/keyri-ios-whitelabel-sdk/releases/tag/4.4.1) - Updated Android SDK - to [4.1.1](https://github.com/Keyri-Co/keyri-android-whitelabel-sdk-source/releases/tag/4.1.1) + to [4.1.1](https://github.com/Keyri-Co/keyri-android-whitelabel-sdk/releases/tag/4.1.1) - Fixed nullable publicUserId in `login` and `register` methods on Android - Updated proguard-rules to keep `LoginObject` and `RegisterObject` on Android - Fixed passing `appKey` on iOS initialize diff --git a/android/build.gradle b/android/build.gradle index eb2016c..b855028 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -48,8 +48,8 @@ android { dependencies { // Keyri - implementation 'com.keyri:keyrisdk:4.1.1' - implementation 'com.keyri:scanner:4.1.1' + implementation 'com.keyri:keyrisdk:4.2.3' + implementation 'com.keyri:scanner:4.2.3' // Gson implementation 'com.google.code.gson:gson:2.10.1' diff --git a/android/src/main/kotlin/com/keyrico/keyri/KeyriPlugin.kt b/android/src/main/kotlin/com/keyrico/keyri/KeyriPlugin.kt index a92faae..2b2ecc9 100644 --- a/android/src/main/kotlin/com/keyrico/keyri/KeyriPlugin.kt +++ b/android/src/main/kotlin/com/keyrico/keyri/KeyriPlugin.kt @@ -6,10 +6,11 @@ import android.util.Log import android.app.Activity import android.content.Intent import com.google.gson.Gson +import org.json.JSONObject import com.keyrico.keyrisdk.Keyri import com.keyrico.scanner.easyKeyriAuth import com.keyrico.keyrisdk.exception.DenialException -import com.keyrico.keyrisdk.sec.fraud.enums.EventType +import com.keyrico.keyrisdk.sec.fraud.event.EventType import androidx.fragment.app.FragmentActivity import com.keyrico.keyrisdk.entity.session.Session import io.flutter.embedding.android.FlutterFragmentActivity @@ -128,10 +129,16 @@ class KeyriPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, "sendEvent" -> { val publicUserId = arguments?.get("publicUserId") val eventType = arguments?.get("eventType") + val metadata = arguments?.get("metadata") val success = arguments?.get("success")?.toBoolean() logMessage("Keyri: sendEvent called") - sendEvent(publicUserId, eventType, success, result) + sendEvent(publicUserId, eventType, metadata, success, result) + } + + "createFingerprint" -> { + logMessage("Keyri: createFingerprint called") + createFingerprint(result) } "initiateQrSession" -> { @@ -357,11 +364,13 @@ class KeyriPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, private fun sendEvent( publicUserId: String?, eventType: String?, + metadata: String?, success: Boolean?, result: MethodChannel.Result ) { keyriCoroutineScope("sendEvent", result::error).launch { - val type = EventType.values().firstOrNull { it.type == eventType } + val jsonMetadata = metadata?.let(::JSONObject) + val type = eventType?.let { EventType.custom(it, jsonMetadata) } if (success == null) { logMessage("Keyri sendEvent: success must not be null") @@ -385,6 +394,20 @@ class KeyriPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, } } + private fun createFingerprint(result: MethodChannel.Result) { + keyriCoroutineScope("createFingerprint", result::error).launch { + keyri.createFingerprint().onSuccess { fingerprint -> + val fingerprintResponse = Gson().toJson(fingerprint) + + logMessage("Keyri createFingerprint: $fingerprintResponse") + result.success(fingerprintResponse) + }.onFailure { + logMessage("Keyri createFingerprint: ${it.message}") + result.error("createFingerprint", it.message, null) + } + } + } + private fun initiateQrSession( sessionId: String?, publicUserId: String?, diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 9625e10..7c56964 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 27fb470..8fba1d3 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -154,7 +154,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a6b826d..5e31d3d 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ { - EventType? _eventType = EventType.visits; + final EventType _eventType = EventType.visits(); @override Widget build(BuildContext context) { @@ -91,21 +91,6 @@ class _KeyriHomePageState extends State { ), ), ), - DropdownButton( - items: EventType.values - .map>((EventType value) { - return DropdownMenuItem( - value: value, - child: Text(value.name), - ); - }).toList(), - onChanged: (value) { - setState(() { - _eventType = value; - }); - }, - value: _eventType, - ), button(_sendEvent, 'Send event'), button(_login, 'Login'), button(_register, 'Register'), @@ -290,7 +275,7 @@ class _KeyriHomePageState extends State { keyri .sendEvent( publicUserId: usernameController.text, - eventType: _eventType ?? EventType.visits, + eventType: _eventType, success: true) .then((fingerprintEventResponse) => _showMessage("Event sent")) .catchError((error, stackTrace) => _processError(error)); diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 85dbe6d..250dcd4 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,5 +1,5 @@ name: keyri_example -version: 1.6.2 +version: 1.7.0 description: Demonstrates how to use the Keyri plugin. publish_to: 'none' @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - http: ^1.2.0 + http: ^1.2.1 keyri_v3: path: ../ cupertino_icons: ^1.0.6 diff --git a/ios/Classes/KeyriPlugin.m b/ios/Classes/KeyriPlugin.m index 0468138..ff8e1db 100644 --- a/ios/Classes/KeyriPlugin.m +++ b/ios/Classes/KeyriPlugin.m @@ -45,6 +45,8 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { [self removeAssociationKey:call result:result]; } else if ([@"sendEvent" isEqualToString:call.method]) { [self sendEvent:call result:result]; + } else if ([@"createFingerprint" isEqualToString:call.method]) { + [self sendEvent:call result:result]; } else if ([@"initiateQrSession" isEqualToString:call.method]) { [self initiateQrSession:call result:result]; } else if ([@"login" isEqualToString:call.method]) { @@ -177,6 +179,7 @@ - (void)removeAssociationKey:(FlutterMethodCall*)call result:(FlutterResult)resu - (void)sendEvent:(FlutterMethodCall*)call result:(FlutterResult)result { id publicUserIdValue = call.arguments[@"publicUserId"]; id eventType = call.arguments[@"eventType"]; + id metadata = call.arguments[@"metadata"]; id successValue = call.arguments[@"success"]; NSString *publicUserId = [publicUserIdValue isKindOfClass:[NSString class]] ? publicUserIdValue : nil; @@ -193,8 +196,15 @@ - (void)sendEvent:(FlutterMethodCall*)call result:(FlutterResult)result { return [self sendErrorResult:result errorMessage:@"You need to provide eventType"]; } + NSData *jsonData = [metadata dataUsingEncoding:NSUTF8StringEncoding]; + + NSError *error = nil; + NSDictionary *metadataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error]; + + EventType *event = [EventType customWithName:eventType metadata:metadataDictionary]; + __weak typeof (self) weakSelf = self; - [self.keyri sendEventWithPublicUserId:publicUserId eventType:eventType success:success completion:^(FingerprintResponse * _Nullable fingerprintResponse, NSError * _Nullable error) { + [self.keyri sendEventWithPublicUserId:publicUserId eventType:event success:success completion:^(FingerprintResponse * _Nullable fingerprintResponse, NSError * _Nullable error) { typeof (self) strongSelf = weakSelf; if (error != nil) { @@ -209,6 +219,20 @@ - (void)sendEvent:(FlutterMethodCall*)call result:(FlutterResult)result { }]; } +- (void)createFingerprint:(FlutterMethodCall*)call result:(FlutterResult)result { + [self.keyri createFingerprintWithCompletion:^(FingerprintRequest * _Nullable fingerprint, NSError * _Nullable error) { + if (error != nil) { + return result([FlutterError errorWithCode:@"1" message:error.localizedDescription details:nil]); + } + + if (fingerprint != nil) { + return result([self dictionaryWithPropertiesOfObject:fingerprint]); + } else { + return [self sendErrorResult:result errorMessage:@"FingerprintRequest is nil"]; + } + }]; +} + - (void)initiateQrSession:(FlutterMethodCall*)call result:(FlutterResult)result { id sessionId = call.arguments[@"sessionId"]; id publicUserIdValue = call.arguments[@"publicUserId"]; diff --git a/ios/keyri_v3.podspec b/ios/keyri_v3.podspec index ae45a53..39fa7dd 100644 --- a/ios/keyri_v3.podspec +++ b/ios/keyri_v3.podspec @@ -18,7 +18,7 @@ Pod::Spec.new do |spec| spec.public_header_files = 'Classes/**/*.h' spec.dependency 'Flutter' - spec.dependency 'keyri-pod', '~> 4.4.1' + spec.dependency 'keyri-pod', '~> 4.5.1' # Flutter.framework does not contain a i386 slice. spec.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } diff --git a/lib/fingerprint_request.dart b/lib/fingerprint_request.dart new file mode 100644 index 0000000..4319761 --- /dev/null +++ b/lib/fingerprint_request.dart @@ -0,0 +1,30 @@ +import 'dart:convert'; + +/// Class which represents fingerprint request object. +class FingerprintRequest { + FingerprintRequest( + this.clientEncryptionKey, this.encryptedPayload, this.salt, this.iv); + + final String clientEncryptionKey; + final String encryptedPayload; + final String salt; + final String iv; + + /// Conversion helper method. + static FingerprintRequest fromJson(dynamic json) { + dynamic jsonData = json; + + try { + String jsonDataString = json.toString(); + jsonData = jsonDecode(jsonDataString); + } catch (e) { + jsonData = json; + } + + return FingerprintRequest( + jsonData['clientEncryptionKey'] as String, + jsonData['encryptedPayload'] as String, + jsonData['salt'] as String, + jsonData['iv'] as String); + } +} diff --git a/lib/keyri.dart b/lib/keyri.dart index 69c8f83..c706aa7 100644 --- a/lib/keyri.dart +++ b/lib/keyri.dart @@ -1,4 +1,5 @@ import 'package:keyri_v3/fingerprint_event_response.dart'; +import 'package:keyri_v3/fingerprint_request.dart'; import 'package:keyri_v3/register_object.dart'; import 'package:keyri_v3/session.dart'; import 'keyri_fingerprint_event.dart'; @@ -78,7 +79,7 @@ class Keyri { } /// Sends fingerprint event and event result for specified publicUserId's. - /// Return [FingerprintEventResponse] or error. + /// Returns [FingerprintEventResponse] or error. Future sendEvent( {String? publicUserId, required EventType eventType, @@ -86,6 +87,12 @@ class Keyri { return KeyriPlatform.instance.sendEvent(publicUserId, eventType, success); } + /// Creates and returns fingerprint event object. + /// Returns [FingerprintRequest] or error. + Future createFingerprint() { + return KeyriPlatform.instance.createFingerprint(); + } + /// Call it after obtaining the sessionId from QR code or deep link. /// Returns Future of [Session] object with Risk attributes (needed to show confirmation screen) or error. Future initiateQrSession(String sessionId, {String? publicUserId}) { diff --git a/lib/keyri_fingerprint_event.dart b/lib/keyri_fingerprint_event.dart index 7d40fee..d170f2d 100644 --- a/lib/keyri_fingerprint_event.dart +++ b/lib/keyri_fingerprint_event.dart @@ -1,14 +1,51 @@ -// ignore_for_file: constant_identifier_names -/// Fingerprint event type. -enum EventType { - visits, - login, - signup, - attach_new_device, - email_change, - profile_update, - password_reset, - withdrawal, - deposit, - purchase; +/// Class which represents fingerprint event type. +class EventType { + EventType(this.name, {this.metadata}); + + final String name; + Map? metadata; + + /// Returns Visits event. + static EventType visits({Map? metadata}) => + EventType("visits", metadata: metadata); + + /// Returns Login event. + static EventType login({Map? metadata}) => + EventType("login", metadata: metadata); + + /// Returns signup event. + static EventType signup({Map? metadata}) => + EventType("signup", metadata: metadata); + + /// Returns Attach new device event. + static EventType attachNewDevice({Map? metadata}) => + EventType("attach_new_device", metadata: metadata); + + /// Returns Email change event. + static EventType emailChange({Map? metadata}) => + EventType("emailChange", metadata: metadata); + + /// Returns Profile update event. + static EventType profileUpdate({Map? metadata}) => + EventType("profile_update", metadata: metadata); + + /// Returns Password reset event. + static EventType passwordReset({Map? metadata}) => + EventType("password_reset", metadata: metadata); + + /// Returns Withdrawal event. + static EventType withdrawal({Map? metadata}) => + EventType("withdrawal", metadata: metadata); + + /// Returns Deposit event. + static EventType deposit({Map? metadata}) => + EventType("deposit", metadata: metadata); + + /// Returns Purchase event. + static EventType purchase({Map? metadata}) => + EventType("purchase", metadata: metadata); + + /// Returns Custom event. + static EventType custom(String name, {Map? metadata}) => + EventType(name, metadata: metadata); } diff --git a/lib/src/keyri_method_channel.dart b/lib/src/keyri_method_channel.dart index 3454b14..5dc7bed 100644 --- a/lib/src/keyri_method_channel.dart +++ b/lib/src/keyri_method_channel.dart @@ -1,6 +1,9 @@ +import 'dart:convert'; + import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:keyri_v3/fingerprint_event_response.dart'; +import 'package:keyri_v3/fingerprint_request.dart'; import 'package:keyri_v3/session.dart'; import '../keyri_fingerprint_event.dart'; import '../login_object.dart'; @@ -108,12 +111,21 @@ class MethodChannelKeyri extends KeyriPlatform { await methodChannel.invokeMethod('sendEvent', { 'publicUserId': publicUserId, 'eventType': eventType.name, + 'metadata': json.encode(eventType.metadata), 'success': success.toString() }); return FingerprintEventResponse.fromJson(fingerprintEventResponseObject); } + @override + Future createFingerprint() async { + dynamic fingerprintRequestObject = + await methodChannel.invokeMethod('createFingerprint'); + + return FingerprintRequest.fromJson(fingerprintRequestObject); + } + @override Future initiateQrSession( String sessionId, String? publicUserId) async { diff --git a/lib/src/keyri_platform_interface.dart b/lib/src/keyri_platform_interface.dart index 24d2d43..1125fe9 100644 --- a/lib/src/keyri_platform_interface.dart +++ b/lib/src/keyri_platform_interface.dart @@ -1,3 +1,4 @@ +import 'package:keyri_v3/fingerprint_request.dart'; import 'package:keyri_v3/session.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; @@ -75,6 +76,10 @@ abstract class KeyriPlatform extends PlatformInterface { throw UnimplementedError('sendEvent() has not been implemented.'); } + Future createFingerprint() { + throw UnimplementedError('createFingerprint() has not been implemented.'); + } + Future initiateQrSession(String sessionId, String? publicUserId) { throw UnimplementedError('initiateQrSession() has not been implemented.'); } diff --git a/pubspec.yaml b/pubspec.yaml index a7f90c5..f75f0d7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,11 +1,11 @@ name: keyri_v3 description: Keyri QR Login plugin for Flutter. Provides Keyri SDK capabilities for secure and passwordless login. -version: 1.6.2 +version: 1.7.0 homepage: https://keyri.com environment: sdk: '>=2.18.0 <4.0.0' - flutter: '>=2.5.0' + flutter: '>=2.8.0' dependencies: flutter: