From 12f0971ab7a0f4ca2d6c37a2ae4928f6d5988319 Mon Sep 17 00:00:00 2001 From: Mark Vayngrib Date: Wed, 28 Oct 2020 18:25:38 -0400 Subject: [PATCH 1/4] refactor: support enroll, authenticate --- index.js | 29 ++- ios/AuthenticateProcessor.swift | 176 ++++++++++++++ ios/EnrollmentProcessor.swift | 167 +++++++++++++ ios/Helpers/NetworkingHelpers.swift | 26 ++- ios/Helpers/ThemeHelpers.swift | 66 +++--- ios/LivenessCheckProcessor.swift | 32 +-- ios/ProcesingDelegate.swift | 6 +- ios/RNReactNativeZoomSdk.m | 10 +- .../project.pbxproj | 12 +- ios/ZoomAuth.swift | 220 ++++++++---------- ios/ZoomGlobalState.swift | 8 +- 11 files changed, 550 insertions(+), 202 deletions(-) create mode 100644 ios/AuthenticateProcessor.swift create mode 100644 ios/EnrollmentProcessor.swift diff --git a/index.js b/index.js index 0b04f16..43ff709 100644 --- a/index.js +++ b/index.js @@ -11,7 +11,7 @@ export const status = { const wrapNative = native => { let initialized - let verifying + let processing const initialize = async opts => { initialized = false @@ -28,33 +28,46 @@ const wrapNative = native => { } const getVersion = () => native.getVersion() - const verify = async (opts = {}) => { + const exec = async (operation, opts = {}) => { if (!initialized) { throw new NotInitializedError('initialize me first!') } - if (verifying) { + if (processing) { throw new VerificationPendingError('only one verification can be done at a time') } let result try { - result = await native.verify({ - ...defaults.verify, + result = await native[operation]({ + ...defaults[operation], ...opts, }) result.status = statusToString(result.status) return result } finally { - verifying = false + processing = false } } + const enroll = (opts) => { + if (typeof opts.id !== 'string' || !opts.id) throw new Error('expected string "id"') + + return exec('enroll', opts) + } + + const authenticate = (opts) => { + if (typeof opts.id !== 'string' || !opts.id) throw new Error('expected string "id"') + + return exec('authenticate', opts) + } + return { - preload: native.preload, initialize, - verify, + verify: exec.bind(null, 'verify'), + enroll, + authenticate, getVersion, } } diff --git a/ios/AuthenticateProcessor.swift b/ios/AuthenticateProcessor.swift new file mode 100644 index 0000000..c435aa3 --- /dev/null +++ b/ios/AuthenticateProcessor.swift @@ -0,0 +1,176 @@ +// +// Welcome to the annotated FaceTec Device SDK core code for performing Authentication against a previously enrolled user. +// + +import UIKit +import Foundation +import FaceTecSDK + +// This is an example self-contained class to perform Authentication with the FaceTec SDK. +// You may choose to further componentize parts of this in your own Apps based on your specific requirements. +class AuthenticateProcessor: NSObject, FaceTecFaceScanProcessorDelegate, URLSessionTaskDelegate { + var latestNetworkRequest: URLSessionTask! + var isSuccess = false + var faceScanResultCallback: FaceTecFaceScanResultCallback! + var licenseKey: String! + var delegate: ProcessingDelegate + var latestZoomSessionResult: FaceTecSessionResult? + var options: Dictionary! + + init(sessionToken: String, delegate: ProcessingDelegate, fromVC: UIViewController, options: Dictionary) { + self.isSuccess = false; + self.delegate = delegate; + + self.options = options; + self.licenseKey = options["licenseKey"] as? String + + super.init() + // + // Part 1: Starting the FaceTec Session + // + // Required parameters: + // - delegate: + // - faceScanProcessorDelegate: A class that implements FaceTecFaceScanProcessor, which handles the FaceScan when the User completes a Session. In this example, "self" implements the class. + // - sessionToken: A valid Session Token you just created by calling your API to get a Session Token from the Server SDK. + // + let sessionVC = FaceTec.sdk.createSessionVC(faceScanProcessorDelegate: self, sessionToken: sessionToken) + if (options["useOverlay"] as? Bool ?? false) { + sessionVC.modalPresentationStyle = .overCurrentContext + } + + // In your code, you will be presenting from a UIViewController elsewhere. You may choose to augment this class to pass that UIViewController in. + // In our example code here, to keep the code in this class simple, we will just get the Sample App's UIViewController statically. + fromVC.present(sessionVC, animated: true, completion: nil) + } + + // + // Part 2: Handling the Result of a FaceScan + // + func processSessionWhileFaceTecSDKWaits(sessionResult: FaceTecSessionResult, faceScanResultCallback: FaceTecFaceScanResultCallback) { + // + // DEVELOPER NOTE: These properties are for demonstration purposes only so the Sample App can get information about what is happening in the processor. + // In the code in your own App, you can pass around signals, flags, intermediates, and results however you would like. + // +// fromViewController.setLatestSessionResult(sessionResult: sessionResult) + + self.latestZoomSessionResult = sessionResult + + // + // DEVELOPER NOTE: A reference to the callback is stored as a class variable so that we can have access to it while performing the Upload and updating progress. + // + self.faceScanResultCallback = faceScanResultCallback + + // + // Part 3: Handles early exit scenarios where there is no FaceScan to handle -- i.e. User Cancellation, Timeouts, etc. + // + if sessionResult.status != FaceTecSessionStatus.sessionCompletedSuccessfully { + if latestNetworkRequest != nil { + latestNetworkRequest.cancel() + } + + faceScanResultCallback.onFaceScanResultCancel() + return + } + + // + // Part 4: Get essential data off the FaceTecSessionResult + // + var parameters: [String : Any] = [:] + parameters["faceScan"] = sessionResult.faceScanBase64 + parameters["auditTrailImage"] = sessionResult.auditTrailCompressedBase64![0] + parameters["lowQualityAuditTrailImage"] = sessionResult.lowQualityAuditTrailCompressedBase64![0] + parameters["externalDatabaseRefID"] = options["id"] as? String + + // + // Part 5: Make the Networking Call to Your Servers. Below is just example code, you are free to customize based on how your own API works. + // + let request = NSMutableURLRequest(url: NSURL(string: ZoomGlobalState.ZoomServerBaseURL + "/match-3d-3d")! as URL) + request.httpMethod = "POST" + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + request.httpBody = try! JSONSerialization.data(withJSONObject: parameters, options: JSONSerialization.WritingOptions(rawValue: 0)) + request.addValue(self.licenseKey, forHTTPHeaderField: "X-Device-Key") + request.addValue(FaceTec.sdk.createFaceTecAPIUserAgentString(sessionResult.sessionId), forHTTPHeaderField: "User-Agent") + + let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue.main) + latestNetworkRequest = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in + // + // Part 6: In our Sample, we evaluate a boolean response and treat true as success, false as "User Needs to Retry", + // and handle all other non-nominal responses by cancelling out. You may have different paradigms in your own API and are free to customize based on these. + // + guard let data = data else { + // CASE: UNEXPECTED response from API. Our Sample Code keys of a success boolean on the root of the JSON object --> You define your own API contracts with yourself and may choose to do something different here based on the error. + faceScanResultCallback.onFaceScanResultCancel() + return + } + + guard let responseJSON = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! [String: AnyObject] else { + // CASE: UNEXPECTED response from API. Our Sample Code keys of a success boolean on the root of the JSON object --> You define your own API contracts with yourself and may choose to do something different here based on the error. + faceScanResultCallback.onFaceScanResultCancel() + return + } + + // + // DEVELOPER NOTE: These properties are for demonstration purposes only so the Sample App can get information about what is happening in the processor. + // In the code in your own App, you can pass around signals, flags, intermediates, and results however you would like. + // +// self.fromViewController.setLatestServerResult(responseJSON: responseJSON) + + let didSucceed = responseJSON["success"] as? Bool + + if didSucceed == true { + // CASE: Success! Authentication succeeded. + + // + // DEVELOPER NOTE: These properties are for demonstration purposes only so the Sample App can get information about what is happening in the processor. + // In the code in your own App, you can pass around signals, flags, intermediates, and results however you would like. + // + self.isSuccess = true; + + FaceTecCustomization.setOverrideResultScreenSuccessMessage("Authenticated") + faceScanResultCallback.onFaceScanResultSucceed() + } + else if (didSucceed == false) { + // CASE: In our Sample code, "success" being present and false means that the User Needs to Retry. + // Real Users will likely succeed on subsequent attempts after following on-screen guidance. + // Attackers/Fraudsters will continue to get rejected. + faceScanResultCallback.onFaceScanResultRetry() + } + else { + // CASE: UNEXPECTED response from API. Our Sample Code keys of a success boolean on the root of the JSON object --> You define your own API contracts with yourself and may choose to do something different here based on the error. + faceScanResultCallback.onFaceScanResultCancel() + } + }) + + // + // Part 8: Actually send the request. + // + latestNetworkRequest.resume() + + // + // Part 9: For better UX, update the User if the upload is taking a while. You are free to customize and enhance this behavior to your liking. + // + DispatchQueue.main.asyncAfter(deadline: .now() + 6) { + if self.latestNetworkRequest.state == .completed { return } + + let uploadMessage:NSMutableAttributedString = NSMutableAttributedString.init(string: "Still Uploading...") + faceScanResultCallback.onFaceScanUploadMessageOverride(uploadMessageOverride: uploadMessage) + } + } + + // + // URLSessionTaskDelegate function to get progress while performing the upload to update the UX. + // + func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { + let uploadProgress: Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend) + faceScanResultCallback.onFaceScanUploadProgress(uploadedPercent: uploadProgress) + } + + // + // Part 10: This function gets called after the FaceTec SDK is completely done. There are no parameters because you have already been passed all data in the processSessionWhileFaceTecSDKWaits function and have already handled all of your own results. + // + func onFaceTecSDKCompletelyDone() { + // In your code, you will handle what to do after Authentication is successful here. + // In our example code here, to keep the code in this class simple, we will call a static method on another class to update the Sample App UI. + delegate.onProcessingComplete(isSuccess: isSuccess, facetecSessionResult: latestZoomSessionResult) + } +} diff --git a/ios/EnrollmentProcessor.swift b/ios/EnrollmentProcessor.swift new file mode 100644 index 0000000..9c88226 --- /dev/null +++ b/ios/EnrollmentProcessor.swift @@ -0,0 +1,167 @@ +// +// Welcome to the annotated FaceTec Device SDK core code for performing secure Enrollments! +// + +import UIKit +import Foundation +import FaceTecSDK + +// This is an example self-contained class to perform Enrollments with the FaceTec SDK. +// You may choose to further componentize parts of this in your own Apps based on your specific requirements. +class EnrollmentProcessor: NSObject, FaceTecFaceScanProcessorDelegate, URLSessionTaskDelegate { + var latestNetworkRequest: URLSessionTask! + var licenseKey: String! + var delegate: ProcessingDelegate + var isSuccess: Bool + var latestZoomSessionResult: FaceTecSessionResult? + var faceScanResultCallback: FaceTecFaceScanResultCallback! + var options: Dictionary! + + init(sessionToken: String, delegate: ProcessingDelegate, fromVC: UIViewController, options: Dictionary) { + self.isSuccess = false; + self.delegate = delegate; + + self.options = options; + self.licenseKey = options["licenseKey"] as? String + + super.init() + // + // Part 1: Starting the FaceTec Session + // + // Required parameters: + // - delegate: + // - faceScanProcessorDelegate: A class that implements FaceTecFaceScanProcessor, which handles the FaceScan when the User completes a Session. In this example, "self" implements the class. + // - sessionToken: A valid Session Token you just created by calling your API to get a Session Token from the Server SDK. + // + let sessionVC = FaceTec.sdk.createSessionVC(faceScanProcessorDelegate: self, sessionToken: sessionToken) + if (options["useOverlay"] as? Bool ?? false) { + sessionVC.modalPresentationStyle = .overCurrentContext + } + + // In your code, you will be presenting from a UIViewController elsewhere. You may choose to augment this class to pass that UIViewController in. + // In our example code here, to keep the code in this class simple, we will just get the Sample App's UIViewController statically. + fromVC.present(sessionVC, animated: true, completion: nil) + } + + // + // Part 2: Handling the Result of a FaceScan + // + func processSessionWhileFaceTecSDKWaits(sessionResult: FaceTecSessionResult, faceScanResultCallback: FaceTecFaceScanResultCallback) { + + self.latestZoomSessionResult = sessionResult + + // + // DEVELOPER NOTE: A reference to the callback is stored as a class variable so that we can have access to it while performing the Upload and updating progress. + // + self.faceScanResultCallback = faceScanResultCallback + + // + // Part 3: Handles early exit scenarios where there is no FaceScan to handle -- i.e. User Cancellation, Timeouts, etc. + // + if sessionResult.status != FaceTecSessionStatus.sessionCompletedSuccessfully { + if latestNetworkRequest != nil { + latestNetworkRequest.cancel() + } + + faceScanResultCallback.onFaceScanResultCancel() + return + } + + // + // Part 4: Get essential data off the FaceTecSessionResult + // + var parameters: [String : Any] = [:] + parameters["faceScan"] = sessionResult.faceScanBase64 + parameters["auditTrailImage"] = sessionResult.auditTrailCompressedBase64![0] + parameters["lowQualityAuditTrailImage"] = sessionResult.lowQualityAuditTrailCompressedBase64![0] + parameters["externalDatabaseRefID"] = self.options["id"] as! String; + + // + // Part 5: Make the Networking Call to Your Servers. Below is just example code, you are free to customize based on how your own API works. + // + let request = NSMutableURLRequest(url: NSURL(string: ZoomGlobalState.ZoomServerBaseURL + "/enrollment-3d")! as URL) + request.httpMethod = "POST" + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + request.httpBody = try! JSONSerialization.data(withJSONObject: parameters, options: JSONSerialization.WritingOptions(rawValue: 0)) + request.addValue(licenseKey, forHTTPHeaderField: "X-Device-Key") + request.addValue(FaceTec.sdk.createFaceTecAPIUserAgentString(sessionResult.sessionId), forHTTPHeaderField: "User-Agent") + + let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue.main) + latestNetworkRequest = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in + // + // Part 6: In our Sample, we evaluate a boolean response and treat true as success, false as "User Needs to Retry", + // and handle all other non-nominal responses by cancelling out. You may have different paradigms in your own API and are free to customize based on these. + // + + guard let data = data else { + // CASE: UNEXPECTED response from API. Our Sample Code keys of a success boolean on the root of the JSON object --> You define your own API contracts with yourself and may choose to do something different here based on the error. + faceScanResultCallback.onFaceScanResultCancel() + return + } + + guard let responseJSON = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject] else { + // CASE: UNEXPECTED response from API. Our Sample Code keys of a success boolean on the root of the JSON object --> You define your own API contracts with yourself and may choose to do something different here based on the error. + faceScanResultCallback.onFaceScanResultCancel() + return + } + + // + // DEVELOPER NOTE: These properties are for demonstration purposes only so the Sample App can get information about what is happening in the processor. + // In the code in your own App, you can pass around signals, flags, intermediates, and results however you would like. + // +// self.fromViewController.setLatestServerResult(responseJSON: responseJSON) + + let didSucceed = responseJSON["success"] as? Bool + + if didSucceed == true { + // CASE: Success! User successfully enrolled. + + self.isSuccess = true; + + // TODO: make this customizable + FaceTecCustomization.setOverrideResultScreenSuccessMessage("Enrollment\nSucceeded") + faceScanResultCallback.onFaceScanResultSucceed() + } + else if (didSucceed == false) { + // CASE: In our Sample code, "success" being present and false means that the User Needs to Retry. + // Real Users will likely succeed on subsequent attempts after following on-screen guidance. + // Attackers/Fraudsters will continue to get rejected. + faceScanResultCallback.onFaceScanResultRetry() + } + else { + // CASE: UNEXPECTED response from API. Our Sample Code keys of a success boolean on the root of the JSON object --> You define your own API contracts with yourself and may choose to do something different here based on the error. + faceScanResultCallback.onFaceScanResultCancel() + } + }) + + // + // Part 8: Actually send the request. + // + latestNetworkRequest.resume() + + // + // Part 9: For better UX, update the User if the upload is taking a while. You are free to customize and enhance this behavior to your liking. + // + DispatchQueue.main.asyncAfter(deadline: .now() + 6) { + if self.latestNetworkRequest.state == .completed { return } + + let uploadMessage:NSMutableAttributedString = NSMutableAttributedString.init(string: "Still Uploading...") + faceScanResultCallback.onFaceScanUploadMessageOverride(uploadMessageOverride: uploadMessage) + } + } + + // + // URLSessionTaskDelegate function to get progress while performing the upload to update the UX. + // + func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { + let uploadProgress: Float = Float(totalBytesSent) / Float(totalBytesExpectedToSend) + faceScanResultCallback.onFaceScanUploadProgress(uploadedPercent: uploadProgress) + } + + // + // Part 10: This function gets called after the FaceTec SDK is completely done. There are no parameters because you have already been passed all data in the processSessionWhileFaceTecSDKWaits function and have already handled all of your own results. + // + func onFaceTecSDKCompletelyDone() { + delegate.onProcessingComplete(isSuccess: isSuccess, facetecSessionResult: latestZoomSessionResult) + } +} diff --git a/ios/Helpers/NetworkingHelpers.swift b/ios/Helpers/NetworkingHelpers.swift index 5a98f4d..82951a9 100644 --- a/ios/Helpers/NetworkingHelpers.swift +++ b/ios/Helpers/NetworkingHelpers.swift @@ -2,7 +2,7 @@ import UIKit import Foundation -import ZoomAuthentication +import FaceTecSDK // Possible directives after parsing the result from ZoOm Server enum UXNextStep { @@ -13,14 +13,14 @@ enum UXNextStep { class NetworkingHelpers { // Set up common parameters needed to communicate to the API. - class func getCommonParameters(zoomSessionResult: ZoomSessionResult) -> [String : Any] { - let zoomFaceMapBase64 = zoomSessionResult.faceMetrics?.faceMapBase64; + class func getCommonParameters(zoomSessionResult: FaceTecSessionResult) -> [String : Any] { + let zoomFaceMapBase64 = zoomSessionResult.faceScanBase64; var parameters: [String : Any] = [:] parameters["faceMap"] = zoomFaceMapBase64 parameters["sessionId"] = zoomSessionResult.sessionId - if let auditTrail = zoomSessionResult.faceMetrics?.auditTrailCompressedBase64 { + if let auditTrail = zoomSessionResult.auditTrailCompressedBase64 { parameters["auditTrailImage"] = auditTrail[0] } @@ -76,7 +76,7 @@ class NetworkingHelpers { // Required parameters to interact with the FaceTec Managed Testing API. // request.addValue(ZoomGlobalState.DeviceLicenseKeyIdentifier, forHTTPHeaderField: "X-Device-License-Key") request.addValue(licenseKey, forHTTPHeaderField: "X-Device-License-Key") - request.addValue(Zoom.sdk.createZoomAPIUserAgentString(sessionId), forHTTPHeaderField: "User-Agent") + request.addValue(FaceTec.sdk.createFaceTecAPIUserAgentString(sessionId), forHTTPHeaderField: "User-Agent") let session = URLSession(configuration: URLSessionConfiguration.default, delegate: urlSessionDelegate, delegateQueue: OperationQueue.main) let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in @@ -100,13 +100,17 @@ class NetworkingHelpers { public class func getLivenessCheckResponseFromZoomServer( urlSessionDelegate: URLSessionDelegate, licenseKey: String, - zoomSessionResult: ZoomSessionResult, + zoomSessionResult: FaceTecSessionResult, resultCallback: @escaping (UXNextStep) -> () ) { - let parameters = getCommonParameters(zoomSessionResult: zoomSessionResult) + var parameters: [String : Any] = [:] + parameters["faceScan"] = zoomSessionResult.faceScanBase64 + parameters["auditTrailImage"] = zoomSessionResult.auditTrailCompressedBase64![0] + parameters["lowQualityAuditTrailImage"] = zoomSessionResult.lowQualityAuditTrailCompressedBase64![0] + callToZoomServerForResult( - endpoint: ZoomGlobalState.ZoomServerBaseURL + "/liveness", + endpoint: ZoomGlobalState.ZoomServerBaseURL + "/liveness-3d", parameters: parameters, sessionId: zoomSessionResult.sessionId, urlSessionDelegate: urlSessionDelegate, @@ -189,10 +193,12 @@ class NetworkingHelpers { class ServerResultHelpers { // if Liveness was Determined, succeed. Otherwise fail. Unexpected responses cancel. public class func getLivenessNextStep(responseJSONObj: [String: AnyObject]) -> UXNextStep { - if (responseJSONObj["data"] as? [String : Any])?["livenessStatus"] as? Int == 0 { + let didSucceed = responseJSONObj["success"] as? Bool + + if didSucceed == true { return .Succeed } - else if (responseJSONObj["data"] as? [String : Any])?["livenessStatus"] as? Int == 1 { + else if (didSucceed == false) { return .Retry } else { diff --git a/ios/Helpers/ThemeHelpers.swift b/ios/Helpers/ThemeHelpers.swift index cb9cbe7..9d78b47 100644 --- a/ios/Helpers/ThemeHelpers.swift +++ b/ios/Helpers/ThemeHelpers.swift @@ -8,13 +8,13 @@ import Foundation import UIKit -import ZoomAuthentication +import FaceTecSDK class ThemeHelpers { public class func setAppTheme(theme: String) { if theme == "ZoOm Theme" { - ZoomGlobalState.currentCustomization = ZoomCustomization() + ZoomGlobalState.currentCustomization = FaceTecCustomization() } else if theme == "Well-Rounded" { let primaryColor = UIColor(red: 0.035, green: 0.710, blue: 0.639, alpha: 1) // green @@ -26,8 +26,8 @@ class ThemeHelpers { backgroundLayer.endPoint = CGPoint.init(x: 1, y: 0) // Overlay Customization - ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .styleLight - ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0.8 +// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .styleLight +// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0.8 ZoomGlobalState.currentCustomization.overlayCustomization.backgroundColor = UIColor.clear ZoomGlobalState.currentCustomization.overlayCustomization.brandingImage = UIImage.init() // Guidance Customization @@ -42,7 +42,7 @@ class ThemeHelpers { ZoomGlobalState.currentCustomization.guidanceCustomization.buttonCornerRadius = 30 ZoomGlobalState.currentCustomization.guidanceCustomization.readyScreenOvalFillColor = primaryColor.withAlphaComponent(0.2) // Guidance Image Customization - ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_green") +// ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_green") // ID Scan Customization ZoomGlobalState.currentCustomization.idScanCustomization.selectionScreenBrandingImage = UIImage.init() ZoomGlobalState.currentCustomization.idScanCustomization.showSelectionScreenBrandingImage = false @@ -66,8 +66,8 @@ class ThemeHelpers { ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderColor = primaryColor ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderWidth = 2 ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundCornerRadius = 5 - ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_green") - ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_green") +// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_green") +// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_green") // Result Screen Customization ZoomGlobalState.currentCustomization.resultScreenCustomization.backgroundColors = [backgroundColor, backgroundColor] ZoomGlobalState.currentCustomization.resultScreenCustomization.foregroundColor = primaryColor @@ -91,7 +91,7 @@ class ThemeHelpers { ZoomGlobalState.currentCustomization.ovalCustomization.progressColor2 = primaryColor // Cancel Button Customization ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImage = UIImage(named: "cancel_round_green") - ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "cancel_round_green") +// ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "cancel_round_green") } else if theme == "Dark-Side" { let primaryColor = UIColor(red: 0, green: 0.675, blue: 0.929, alpha: 1) // blue @@ -103,8 +103,8 @@ class ThemeHelpers { backgroundLayer.endPoint = CGPoint.init(x: 1, y: 0) // Overlay Customization - ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .off - ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0 +// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .off +// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0 ZoomGlobalState.currentCustomization.overlayCustomization.backgroundColor = UIColor.black ZoomGlobalState.currentCustomization.overlayCustomization.brandingImage = UIImage.init() // Guidance Customization @@ -119,7 +119,7 @@ class ThemeHelpers { ZoomGlobalState.currentCustomization.guidanceCustomization.buttonCornerRadius = 2 ZoomGlobalState.currentCustomization.guidanceCustomization.readyScreenOvalFillColor = primaryColor.withAlphaComponent(0.2) // Guidance Image Customization - ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_blue") +// ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_blue") // ID Scan Customization ZoomGlobalState.currentCustomization.idScanCustomization.selectionScreenBrandingImage = UIImage.init() ZoomGlobalState.currentCustomization.idScanCustomization.showSelectionScreenBrandingImage = false @@ -143,8 +143,8 @@ class ThemeHelpers { ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderColor = primaryColor ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderWidth = 2 ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundCornerRadius = 8 - ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_blue") - ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_blue") +// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_blue") +// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_blue") // Result Screen Customization ZoomGlobalState.currentCustomization.resultScreenCustomization.backgroundColors = [backgroundColor, backgroundColor] ZoomGlobalState.currentCustomization.resultScreenCustomization.foregroundColor = primaryColor @@ -168,7 +168,7 @@ class ThemeHelpers { ZoomGlobalState.currentCustomization.ovalCustomization.progressColor2 = primaryColor.withAlphaComponent(0.5) // Cancel Button Customization ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImage = UIImage(named: "double_chevron_left_blue") - ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "double_chevron_left_blue") +// ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "double_chevron_left_blue") } else if theme == "Bitcoin Exchange" { let primaryColor = UIColor(red: 0.969, green: 0.588, blue: 0.204, alpha: 1) // orange @@ -181,8 +181,8 @@ class ThemeHelpers { backgroundLayer.endPoint = CGPoint.init(x: 1, y: 0) // Overlay Customization - ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .off - ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0 +// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .off +// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0 ZoomGlobalState.currentCustomization.overlayCustomization.backgroundColor = UIColor.white ZoomGlobalState.currentCustomization.overlayCustomization.brandingImage = UIImage(named: "bitcoin_exchange_logo") // Guidance Customization @@ -197,7 +197,7 @@ class ThemeHelpers { ZoomGlobalState.currentCustomization.guidanceCustomization.buttonCornerRadius = 5 ZoomGlobalState.currentCustomization.guidanceCustomization.readyScreenOvalFillColor = primaryColor.withAlphaComponent(0.2) // Guidance Image Customization - ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_orange") +// ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_orange") // ID Scan Customization ZoomGlobalState.currentCustomization.idScanCustomization.selectionScreenBrandingImage = UIImage.init() ZoomGlobalState.currentCustomization.idScanCustomization.showSelectionScreenBrandingImage = false @@ -221,8 +221,8 @@ class ThemeHelpers { ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderColor = primaryColor ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderWidth = 0 ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundCornerRadius = 8 - ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_orange") - ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_orange") +// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_orange") +// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_orange") // Result Screen Customization ZoomGlobalState.currentCustomization.resultScreenCustomization.backgroundColors = [backgroundColor, backgroundColor] ZoomGlobalState.currentCustomization.resultScreenCustomization.foregroundColor = primaryColor @@ -246,7 +246,7 @@ class ThemeHelpers { ZoomGlobalState.currentCustomization.ovalCustomization.progressColor2 = secondaryColor // Cancel Button Customization ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImage = UIImage(named: "back_orange") - ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "back_orange") +// ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "back_orange") } else if theme == "eKYC" { let primaryColor = UIColor(red: 0.929, green: 0.110, blue: 0.141, alpha: 1) // red @@ -259,8 +259,8 @@ class ThemeHelpers { backgroundLayer.endPoint = CGPoint.init(x: 1, y: 0) // Overlay Customization - ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .off - ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0 +// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .off +// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0 ZoomGlobalState.currentCustomization.overlayCustomization.backgroundColor = UIColor.clear ZoomGlobalState.currentCustomization.overlayCustomization.brandingImage = UIImage(named: "ekyc_logo") // Guidance Customization @@ -275,7 +275,7 @@ class ThemeHelpers { ZoomGlobalState.currentCustomization.guidanceCustomization.buttonCornerRadius = 8 ZoomGlobalState.currentCustomization.guidanceCustomization.readyScreenOvalFillColor = primaryColor.withAlphaComponent(0.2) // Guidance Image Customization - ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_red") +// ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_red") // ID Scan Customization ZoomGlobalState.currentCustomization.idScanCustomization.selectionScreenBrandingImage = UIImage(named: "ekyc_logo") ZoomGlobalState.currentCustomization.idScanCustomization.showSelectionScreenBrandingImage = true @@ -299,8 +299,8 @@ class ThemeHelpers { ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderColor = primaryColor ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderWidth = 0 ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundCornerRadius = 2 - ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_red") - ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_red") +// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_red") +// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_red") // Result Screen Customization ZoomGlobalState.currentCustomization.resultScreenCustomization.backgroundColors = [backgroundColor, backgroundColor] ZoomGlobalState.currentCustomization.resultScreenCustomization.foregroundColor = secondaryColor @@ -324,7 +324,7 @@ class ThemeHelpers { ZoomGlobalState.currentCustomization.ovalCustomization.progressColor2 = primaryColor.withAlphaComponent(0.5) // Cancel Button Customization ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImage = UIImage(named: "cancel_box_red") - ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "cancel_box_red") +// ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "cancel_box_red") } else if theme == "Sample Bank" { let primaryColor = UIColor.white @@ -337,8 +337,8 @@ class ThemeHelpers { backgroundLayer.endPoint = CGPoint.init(x: 1, y: 0) // Overlay Customization - ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .off - ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0 +// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectStyle = .off +// ZoomGlobalState.currentCustomization.overlayCustomization.blurEffectOpacity = 0 ZoomGlobalState.currentCustomization.overlayCustomization.backgroundColor = primaryColor ZoomGlobalState.currentCustomization.overlayCustomization.brandingImage = UIImage(named: "sample_bank_logo") // Guidance Customization @@ -353,7 +353,7 @@ class ThemeHelpers { ZoomGlobalState.currentCustomization.guidanceCustomization.buttonCornerRadius = 2 ZoomGlobalState.currentCustomization.guidanceCustomization.readyScreenOvalFillColor = primaryColor.withAlphaComponent(0.2) // Guidance Image Customization - ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_white_navy") +// ZoomGlobalState.currentCustomization.guidanceCustomization.imageCustomization.cameraPermissionsScreenImage = UIImage(named: "camera_white_navy") // ID Scan Customization ZoomGlobalState.currentCustomization.idScanCustomization.selectionScreenBrandingImage = UIImage(named: "sample_bank_logo") ZoomGlobalState.currentCustomization.idScanCustomization.showSelectionScreenBrandingImage = true @@ -377,8 +377,8 @@ class ThemeHelpers { ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderColor = backgroundColor ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundBorderWidth = 2 ZoomGlobalState.currentCustomization.idScanCustomization.reviewScreenTextBackgroundCornerRadius = 2 - ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_navy") - ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_navy") +// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenPassportFrameImage = UIImage(named: "zoom_passport_frame_navy") +// ZoomGlobalState.currentCustomization.idScanCustomization.captureScreenIDCardFrameImage = UIImage(named: "zoom_id_card_frame_navy") // Result Screen Customization ZoomGlobalState.currentCustomization.resultScreenCustomization.backgroundColors = [backgroundColor, backgroundColor] ZoomGlobalState.currentCustomization.resultScreenCustomization.foregroundColor = primaryColor @@ -402,10 +402,10 @@ class ThemeHelpers { ZoomGlobalState.currentCustomization.ovalCustomization.progressColor2 = primaryColor.withAlphaComponent(0.5) // Cancel Button Customization ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImage = UIImage(named: "cancel_white") - ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "cancel_navy") +// ZoomGlobalState.currentCustomization.cancelButtonCustomization.customImageLowLight = UIImage(named: "cancel_navy") } - Zoom.sdk.setCustomization(ZoomGlobalState.currentCustomization) + FaceTec.sdk.setCustomization(ZoomGlobalState.currentCustomization) } } diff --git a/ios/LivenessCheckProcessor.swift b/ios/LivenessCheckProcessor.swift index ee79c81..0cd6e46 100644 --- a/ios/LivenessCheckProcessor.swift +++ b/ios/LivenessCheckProcessor.swift @@ -2,15 +2,15 @@ import UIKit import Foundation -import ZoomAuthentication +import FaceTecSDK -class LivenessCheckProcessor: NSObject, URLSessionDelegate, ZoomFaceMapProcessorDelegate, ZoomSessionDelegate { +class LivenessCheckProcessor: NSObject, URLSessionDelegate, FaceTecFaceScanProcessorDelegate { var licenseKey: String! var delegate: ProcessingDelegate - var latestZoomSessionResult: ZoomSessionResult? + var latestZoomSessionResult: FaceTecSessionResult? var isSuccess: Bool - init(delegate: ProcessingDelegate, fromVC: UIViewController, options: Dictionary) { + init(sessionToken: String, delegate: ProcessingDelegate, fromVC: UIViewController, options: Dictionary) { self.delegate = delegate self.isSuccess = false @@ -19,8 +19,8 @@ class LivenessCheckProcessor: NSObject, URLSessionDelegate, ZoomFaceMapProcessor self.licenseKey = options["licenseKey"] as? String // Launch the ZoOm Session. - let sessionVC = Zoom.sdk.createSessionVC(delegate: self, faceMapProcessorDelegate: self) - if (options["useOverlay"] as? Bool)! { + let sessionVC = FaceTec.sdk.createSessionVC(faceScanProcessorDelegate: self, sessionToken: sessionToken) + if (options["useOverlay"] as? Bool ?? false) { sessionVC.modalPresentationStyle = .overCurrentContext } @@ -28,11 +28,11 @@ class LivenessCheckProcessor: NSObject, URLSessionDelegate, ZoomFaceMapProcessor } // Required function that handles calling ZoOm Server to get result and decides how to continue. - func processZoomSessionResultWhileZoomWaits(zoomSessionResult: ZoomSessionResult, zoomFaceMapResultCallback: ZoomFaceMapResultCallback) { - self.latestZoomSessionResult = zoomSessionResult + func processSessionWhileFaceTecSDKWaits(sessionResult: FaceTecSessionResult, faceScanResultCallback: FaceTecFaceScanResultCallback) { + self.latestZoomSessionResult = sessionResult // cancellation, timeout, etc. - if zoomSessionResult.status != .sessionCompletedSuccessfully || zoomSessionResult.faceMetrics?.faceMap == nil { - zoomFaceMapResultCallback.onFaceMapResultCancel(); + if sessionResult.status != .sessionCompletedSuccessfully { + faceScanResultCallback.onFaceScanResultCancel(); return } @@ -40,19 +40,19 @@ class LivenessCheckProcessor: NSObject, URLSessionDelegate, ZoomFaceMapProcessor NetworkingHelpers.getLivenessCheckResponseFromZoomServer( urlSessionDelegate: self, licenseKey: self.licenseKey, - zoomSessionResult: zoomSessionResult, + zoomSessionResult: sessionResult, resultCallback: { nextStep in if nextStep == .Succeed { // Dynamically set the success message. // ZoomCustomization.setOverrideResultScreenSuccessMessage("Liveness\nConfirmed") - zoomFaceMapResultCallback.onFaceMapResultSucceed() + faceScanResultCallback.onFaceScanResultSucceed() self.isSuccess = true } else if nextStep == .Retry { - zoomFaceMapResultCallback.onFaceMapResultRetry() + faceScanResultCallback.onFaceScanResultRetry() } else { - zoomFaceMapResultCallback.onFaceMapResultCancel() + faceScanResultCallback.onFaceScanResultCancel() } } ) @@ -64,7 +64,7 @@ class LivenessCheckProcessor: NSObject, URLSessionDelegate, ZoomFaceMapProcessor } // The final callback ZoOm SDK calls when done with everything. - func onZoomSessionComplete() { - delegate.onProcessingComplete(isSuccess: isSuccess, zoomSessionResult: latestZoomSessionResult) + func onFaceTecSDKCompletelyDone() { + delegate.onProcessingComplete(isSuccess: isSuccess, facetecSessionResult: latestZoomSessionResult) } } diff --git a/ios/ProcesingDelegate.swift b/ios/ProcesingDelegate.swift index fbdf229..6af707c 100644 --- a/ios/ProcesingDelegate.swift +++ b/ios/ProcesingDelegate.swift @@ -1,8 +1,8 @@ // Helpful interfaces and enums -import ZoomAuthentication +import FaceTecSDK protocol ProcessingDelegate: class { - func onProcessingComplete(isSuccess: Bool, zoomSessionResult: ZoomSessionResult?) - func onProcessingComplete(isSuccess: Bool, zoomSessionResult: ZoomSessionResult?, zoomIDScanResult: ZoomIDScanResult?) + func onProcessingComplete(isSuccess: Bool, facetecSessionResult: FaceTecSessionResult?) + func onProcessingComplete(isSuccess: Bool, facetecSessionResult: FaceTecSessionResult?, facetecIDScanResult: FaceTecIDScanResult?) } diff --git a/ios/RNReactNativeZoomSdk.m b/ios/RNReactNativeZoomSdk.m index 03950e0..68c1d4e 100644 --- a/ios/RNReactNativeZoomSdk.m +++ b/ios/RNReactNativeZoomSdk.m @@ -14,8 +14,6 @@ @interface RCT_EXTERN_REMAP_MODULE(RNReactNativeZoomSdk, ZoomAuth, RCTViewManager) -RCT_EXTERN_METHOD(preload) - RCT_EXTERN_METHOD(getVersion:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) @@ -27,6 +25,14 @@ @interface RCT_EXTERN_REMAP_MODULE(RNReactNativeZoomSdk, ZoomAuth, RCTViewManage resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(enroll:(NSDictionary*) options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(authenticate:(NSDictionary*) options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) + + (BOOL)requiresMainQueueSetup { return YES; diff --git a/ios/RNReactNativeZoomSdk.xcodeproj/project.pbxproj b/ios/RNReactNativeZoomSdk.xcodeproj/project.pbxproj index e2c90b3..1b59c30 100644 --- a/ios/RNReactNativeZoomSdk.xcodeproj/project.pbxproj +++ b/ios/RNReactNativeZoomSdk.xcodeproj/project.pbxproj @@ -8,11 +8,13 @@ /* Begin PBXBuildFile section */ 85B6B0F92018C5EA00A70FDA /* ZoomAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B6B0F82018C5EA00A70FDA /* ZoomAuth.swift */; }; + AE87D795253DFE44007D0024 /* EnrollmentProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE87D794253DFE44007D0024 /* EnrollmentProcessor.swift */; }; AE9D6FBE23C165CF00B12F1C /* LivenessCheckProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9D6FBD23C165CF00B12F1C /* LivenessCheckProcessor.swift */; }; AE9D6FC023C1669C00B12F1C /* ProcesingDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9D6FBF23C1669C00B12F1C /* ProcesingDelegate.swift */; }; AE9D6FC323C16E0A00B12F1C /* ZoomGlobalState.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9D6FC223C16E0A00B12F1C /* ZoomGlobalState.swift */; }; AE9D6FC523C16EA500B12F1C /* ThemeHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9D6FC423C16EA500B12F1C /* ThemeHelpers.swift */; }; AE9D6FC723C16EA600B12F1C /* NetworkingHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE9D6FC623C16EA600B12F1C /* NetworkingHelpers.swift */; }; + AEC3CD0B254A15740087A0CB /* AuthenticateProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEC3CD0A254A15740087A0CB /* AuthenticateProcessor.swift */; }; B3E7B58A1CC2AC0600A0062D /* RNReactNativeZoomSdk.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNReactNativeZoomSdk.m */; }; /* End PBXBuildFile section */ @@ -33,11 +35,13 @@ 85B6B0F72018C5E900A70FDA /* RNReactNativeZoomSdk-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RNReactNativeZoomSdk-Bridging-Header.h"; sourceTree = ""; }; 85B6B0F82018C5EA00A70FDA /* ZoomAuth.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = ZoomAuth.swift; sourceTree = ""; tabWidth = 2; }; 85B6B0FD2018C6A300A70FDA /* ZoomConfigurationForTestApplication.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ZoomConfigurationForTestApplication.xcconfig; sourceTree = ""; }; + AE87D794253DFE44007D0024 /* EnrollmentProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnrollmentProcessor.swift; sourceTree = ""; }; AE9D6FBD23C165CF00B12F1C /* LivenessCheckProcessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LivenessCheckProcessor.swift; sourceTree = ""; }; AE9D6FBF23C1669C00B12F1C /* ProcesingDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProcesingDelegate.swift; sourceTree = ""; }; AE9D6FC223C16E0A00B12F1C /* ZoomGlobalState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZoomGlobalState.swift; sourceTree = ""; }; AE9D6FC423C16EA500B12F1C /* ThemeHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ThemeHelpers.swift; path = Helpers/ThemeHelpers.swift; sourceTree = ""; }; AE9D6FC623C16EA600B12F1C /* NetworkingHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NetworkingHelpers.swift; path = Helpers/NetworkingHelpers.swift; sourceTree = ""; }; + AEC3CD0A254A15740087A0CB /* AuthenticateProcessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticateProcessor.swift; sourceTree = ""; }; B3E7B5881CC2AC0600A0062D /* RNReactNativeZoomSdk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNReactNativeZoomSdk.h; sourceTree = ""; }; B3E7B5891CC2AC0600A0062D /* RNReactNativeZoomSdk.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNReactNativeZoomSdk.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -64,6 +68,9 @@ 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( + AEC3CD0A254A15740087A0CB /* AuthenticateProcessor.swift */, + AE87D794253DFE44007D0024 /* EnrollmentProcessor.swift */, + AE9D6FBD23C165CF00B12F1C /* LivenessCheckProcessor.swift */, 85B6B0F82018C5EA00A70FDA /* ZoomAuth.swift */, AE9D6FC623C16EA600B12F1C /* NetworkingHelpers.swift */, AE9D6FC423C16EA500B12F1C /* ThemeHelpers.swift */, @@ -71,7 +78,6 @@ B3E7B5881CC2AC0600A0062D /* RNReactNativeZoomSdk.h */, B3E7B5891CC2AC0600A0062D /* RNReactNativeZoomSdk.m */, 85B6B0FD2018C6A300A70FDA /* ZoomConfigurationForTestApplication.xcconfig */, - AE9D6FBD23C165CF00B12F1C /* LivenessCheckProcessor.swift */, AE9D6FBF23C1669C00B12F1C /* ProcesingDelegate.swift */, 134814211AA4EA7D00B7C361 /* Products */, 85B6B0F72018C5E900A70FDA /* RNReactNativeZoomSdk-Bridging-Header.h */, @@ -144,10 +150,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + AEC3CD0B254A15740087A0CB /* AuthenticateProcessor.swift in Sources */, AE9D6FC723C16EA600B12F1C /* NetworkingHelpers.swift in Sources */, AE9D6FC323C16E0A00B12F1C /* ZoomGlobalState.swift in Sources */, AE9D6FC523C16EA500B12F1C /* ThemeHelpers.swift in Sources */, 85B6B0F92018C5EA00A70FDA /* ZoomAuth.swift in Sources */, + AE87D795253DFE44007D0024 /* EnrollmentProcessor.swift in Sources */, AE9D6FBE23C165CF00B12F1C /* LivenessCheckProcessor.swift in Sources */, B3E7B58A1CC2AC0600A0062D /* RNReactNativeZoomSdk.m in Sources */, AE9D6FC023C1669C00B12F1C /* ProcesingDelegate.swift in Sources */, @@ -180,7 +188,6 @@ ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; FRAMEWORK_SEARCH_PATHS = ( - "$(SRCROOT)/../../../ios/Frameworks", "$(SRCROOT)/../../../ios", "$(SRCROOT)/../../../iOS", ); @@ -229,7 +236,6 @@ ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ( - "$(SRCROOT)/../../../ios/Frameworks", "$(SRCROOT)/../../../ios", "$(SRCROOT)/../../../iOS", ); diff --git a/ios/ZoomAuth.swift b/ios/ZoomAuth.swift index 3e7ca8e..9f87acb 100644 --- a/ios/ZoomAuth.swift +++ b/ios/ZoomAuth.swift @@ -7,13 +7,14 @@ // import UIKit -import ZoomAuthentication +import FaceTecSDK +import Network @objc(ZoomAuth) -class ZoomAuth: RCTViewManager, ProcessingDelegate { +class ZoomAuth: RCTViewManager, ProcessingDelegate, URLSessionTaskDelegate { - var verifyResolver: RCTPromiseResolveBlock? = nil - var verifyRejecter: RCTPromiseRejectBlock? = nil + var resolver: RCTPromiseResolveBlock? = nil + var rejecter: RCTPromiseRejectBlock? = nil var returnBase64: Bool = false var initialized = false var licenseKey: String! @@ -28,20 +29,54 @@ class ZoomAuth: RCTViewManager, ProcessingDelegate { @objc func verify(_ options: Dictionary, // options not used at the moment resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) -> Void { - self.verifyResolver = resolve - self.verifyRejecter = reject - self.returnBase64 = (options["returnBase64"] as? Bool)! + self.resolver = resolve + self.rejecter = reject + self.returnBase64 = options["returnBase64"] as? Bool ?? false; DispatchQueue.main.async { let root = UIApplication.shared.keyWindow!.rootViewController! var optionsWithKey = options optionsWithKey["licenseKey"] = self.licenseKey - let _ = LivenessCheckProcessor(delegate: self, fromVC: root, options: optionsWithKey) + self.getSessionToken() { sessionToken in + let _ = LivenessCheckProcessor(sessionToken: sessionToken, delegate: self, fromVC: root, options: optionsWithKey) + } + } + } + + // React Method + @objc func enroll(_ options: Dictionary, // options not used at the moment + resolver resolve: @escaping RCTPromiseResolveBlock, + rejecter reject: @escaping RCTPromiseRejectBlock) -> Void { + self.resolver = resolve + self.rejecter = reject + DispatchQueue.main.async { + let root = UIApplication.shared.keyWindow!.rootViewController! + var optionsWithKey = options + optionsWithKey["licenseKey"] = self.licenseKey + self.getSessionToken() { sessionToken in + let _ = EnrollmentProcessor(sessionToken: sessionToken, delegate: self, fromVC: root, options: optionsWithKey) + } + } + } + + // React Method + @objc func authenticate(_ options: Dictionary, // options not used at the moment + resolver resolve: @escaping RCTPromiseResolveBlock, + rejecter reject: @escaping RCTPromiseRejectBlock) -> Void { + self.resolver = resolve + self.rejecter = reject + DispatchQueue.main.async { + let root = UIApplication.shared.keyWindow!.rootViewController! + var optionsWithKey = options + optionsWithKey["licenseKey"] = self.licenseKey + self.getSessionToken() { sessionToken in + let _ = AuthenticateProcessor(sessionToken: sessionToken, delegate: self, fromVC: root, options: optionsWithKey) + } } } // Show the final result and transition back into the main interface. - func onProcessingComplete(isSuccess: Bool, zoomSessionResult: ZoomSessionResult?) { - let statusCode = zoomSessionResult?.status.rawValue ?? -1 + func onProcessingComplete(isSuccess: Bool, facetecSessionResult: FaceTecSessionResult?) { + let statusCode = facetecSessionResult?.status.rawValue ?? -1 var resultJson:[String:Any] = [ "success": isSuccess, "status": statusCode @@ -52,99 +87,41 @@ class ZoomAuth: RCTViewManager, ProcessingDelegate { return } - resultJson["countOfZoomSessionsPerformed"] = zoomSessionResult?.countOfZoomSessionsPerformed ?? 1 - resultJson["sessionId"] = zoomSessionResult?.sessionId ?? "" + resultJson["sessionId"] = facetecSessionResult?.sessionId ?? "" - if zoomSessionResult?.faceMetrics == nil { - self.sendResult(resultJson) - return + if self.returnBase64 && facetecSessionResult?.faceScan != nil { + resultJson["facemapBase64"] = facetecSessionResult!.faceScanBase64 } - let face = zoomSessionResult?.faceMetrics - var faceMetrics: [String:Any] = [:] - if self.returnBase64 && face?.faceMapBase64 != nil { - faceMetrics["facemap"] = face!.faceMapBase64 - } - -// resultJson["faceMetrics"] = faceMetrics - if face?.auditTrail == nil { - self.sendResult(resultJson) - return - } - - let auditTrailImages = face!.auditTrail! - var auditTrail:[String] = [] - if self.returnBase64 { - if let auditTrailBase64 = face?.auditTrailCompressedBase64 { - faceMetrics["auditTrail"] = auditTrailBase64 - } - - resultJson["faceMetrics"] = faceMetrics - self.sendResult(resultJson) - return - } - - var togo = face?.auditTrailCompressedBase64?.count ?? 0 - if let faceMap = face?.faceMap { - togo += 1 - storeDataInImageStore(faceMap) { (tag) in - faceMetrics["facemap"] = tag - togo -= 1 - if togo == 0 { - resultJson["faceMetrics"] = faceMetrics - self.sendResult(resultJson) - } - } - } - - for image in auditTrailImages { - uiImageToImageStoreKey(image) { (tag) in - if (tag != nil) { - auditTrail.append(tag!) - } - - togo -= 1 - if togo == 0 { - faceMetrics["auditTrail"] = auditTrail - resultJson["faceMetrics"] = faceMetrics - self.sendResult(resultJson) - } - } - } - -// EXAMPLE: retrieve facemap -// if let zoomFacemap = result.faceMetrics?.zoomFacemap { -// // handle ZoOm Facemap -// } - + self.sendResult(resultJson) } // Show the final result and transition back into the main interface. - func onProcessingComplete(isSuccess: Bool, zoomSessionResult: ZoomSessionResult?, zoomIDScanResult: ZoomIDScanResult?) { + func onProcessingComplete(isSuccess: Bool, facetecSessionResult: FaceTecSessionResult?, facetecIDScanResult: FaceTecIDScanResult?) { } func sendResult(_ result: [String:Any]) -> Void { - if (self.verifyResolver == nil) { + if (self.resolver == nil) { return } - self.verifyResolver!(result) + self.resolver!(result) self.cleanUp() } // not used at the moment func sendError(_ code: String, message: String, error: Error) -> Void { - if (self.verifyRejecter == nil) { + if (self.rejecter == nil) { return } - self.verifyRejecter!(code, message, error) + self.rejecter!(code, message, error) self.cleanUp() } func cleanUp () -> Void { - self.verifyResolver = nil - self.verifyRejecter = nil + self.resolver = nil + self.rejecter = nil } func uiImageToBase64 (_ image: UIImage) -> String { @@ -164,16 +141,11 @@ class ZoomAuth: RCTViewManager, ProcessingDelegate { imageStore.storeImageData(data, with: completionHandler) } - // React Method - @objc func preload() -> Void { - Zoom.sdk.preload() - } - // React Method @objc func getVersion(_ resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) -> Void { - let result: String = Zoom.sdk.version + let result: String = FaceTec.sdk.version if ( !result.isEmpty ) { resolve([ @@ -190,34 +162,22 @@ class ZoomAuth: RCTViewManager, ProcessingDelegate { @objc func initialize(_ options: Dictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) -> Void { - self.licenseKey = options["licenseKey"] as? String + self.licenseKey = options["deviceKeyIdentifier"] as! String - if (options["facemapEncryptionKey"] != nil) { - let publicKey = options["facemapEncryptionKey"] as! String - Zoom.sdk.setFaceMapEncryptionKey(publicKey: publicKey); - } + let faceMapEncryptionKey = options["facemapEncryptionKey"] as! String - Zoom.sdk.auditTrailType = .height640 // otherwise no auditTrail images + FaceTec.sdk.auditTrailType = .height640 // otherwise no auditTrail images // Create the customization object - let currentCustomization: ZoomCustomization = ZoomCustomization() + let currentCustomization: FaceTecCustomization = FaceTecCustomization() // disable the "Your App Logo" section currentCustomization.overlayCustomization.brandingImage = nil -// currentCustomization.overlayCustomization.blurEffectOpacity = 0 -// currentCustomization.frameCustomization.blurEffectOpacity = 0 -// currentCustomization.guidanceCustomization.showIntroScreenBrandingImage = false - - // Sample UI Customization: vertically center the ZoOm frame within the device's display -// if (options["centerFrame"] as? Bool)! { -// centerZoomFrameCustomization(currentZoomCustomization: currentCustomization) -// } else if (options["topMargin"] != nil) { -// currentCustomization.frameCustomization.topMargin = options["topMargin"] as! Int32 -// } // Apply the customization changes - Zoom.sdk.setCustomization(currentCustomization) - Zoom.sdk.initialize( - licenseKeyIdentifier: options["licenseKey"] as! String, + FaceTec.sdk.setCustomization(currentCustomization) + FaceTec.sdk.initializeInDevelopmentMode( + deviceKeyIdentifier: licenseKey, + faceScanEncryptionKey: faceMapEncryptionKey, completion: { (licenseKeyValidated: Bool) -> Void in // // We want to ensure that licenseKey is valid before enabling verification @@ -231,31 +191,45 @@ class ZoomAuth: RCTViewManager, ProcessingDelegate { ]) } else { - let status = Zoom.sdk.getStatus().rawValue + let status = FaceTec.sdk.getStatus().rawValue resolve([ "success": false, "status": status ]) - -// let errorMsg = "AppToken did not validate. If Zoom ViewController's are launched, user will see an app token error state" -// print(errorMsg) -// let err: NSError = NSError(domain: errorMsg, code: 0, userInfo: nil) -// reject("initialize", errorMsg, err) } } ) } -// func centerZoomFrameCustomization(currentZoomCustomization: ZoomCustomization) { -// let screenHeight: CGFloat = UIScreen.main.fixedCoordinateSpace.bounds.size.height -// var frameHeight: CGFloat = screenHeight * CGFloat(currentZoomCustomization.frameCustomization.sizeRatio) -// // Detect iPhone X and iPad displays -// if UIScreen.main.fixedCoordinateSpace.bounds.size.height >= 812 { -// let screenWidth = UIScreen.main.fixedCoordinateSpace.bounds.size.width -// frameHeight = screenWidth * (16.0/9.0) * CGFloat(currentZoomCustomization.frameCustomization.sizeRatio) -// } -// let topMarginToCenterFrame = (screenHeight - frameHeight)/2.0 -// -// currentZoomCustomization.frameCustomization.topMargin = Int32(topMarginToCenterFrame) -// } + func getSessionToken(sessionTokenCallback: @escaping (String) -> ()) { + let endpoint = ZoomGlobalState.ZoomServerBaseURL + "/session-token" + let request = NSMutableURLRequest(url: NSURL(string: endpoint)! as URL) + request.httpMethod = "GET" + // Required parameters to interact with the FaceTec Managed Testing API. + request.addValue(self.licenseKey, forHTTPHeaderField: "X-Device-Key") + request.addValue(FaceTec.sdk.createFaceTecAPIUserAgentString(""), forHTTPHeaderField: "User-Agent") + + let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue.main) + let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in + // Ensure the data object is not nil otherwise callback with empty dictionary. + guard let data = data else { + print("Exception raised while attempting HTTPS call.") +// self.handleErrorGettingServerSessionToken() + return + } + if let responseJSONObj = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! [String: AnyObject] { + if((responseJSONObj["sessionToken"] as? String) != nil) + { +// self.hideSessionTokenConnectionText() + sessionTokenCallback(responseJSONObj["sessionToken"] as! String) + return + } + else { + print("Exception raised while attempting HTTPS call.") +// self.handleErrorGettingServerSessionToken() + } + } + }) + task.resume() + } } diff --git a/ios/ZoomGlobalState.swift b/ios/ZoomGlobalState.swift index dfb6070..63b643c 100644 --- a/ios/ZoomGlobalState.swift +++ b/ios/ZoomGlobalState.swift @@ -1,12 +1,12 @@ import UIKit import Foundation -import ZoomAuthentication +import FaceTecSDK class ZoomGlobalState { - // "https://api.zoomauth.com/api/v2/biometrics" for FaceTec Managed Testing API. + // "https://api.zoomauth.com/api/v3/biometrics" for FaceTec Managed Testing API. // "http://localhost:8080" if running ZoOm Server SDK (Dockerized) locally. // Otherwise, your webservice URL. - static let ZoomServerBaseURL = "https://api.zoomauth.com/api/v2/biometrics" + static let ZoomServerBaseURL = "https://api.facetec.com/api/v3/biometrics" // The customer-controlled public key used during encryption of FaceMap data. // Please see https://dev.zoomlogin.com/zoomsdk/#/licensing-and-encryption-keys for more information. @@ -26,5 +26,5 @@ class ZoomGlobalState { // static var isRandomUsernameEnrolled = false // this app can modify the customization to demonstrate different look/feel preferences for ZoOm - static var currentCustomization: ZoomCustomization = ZoomCustomization() + static var currentCustomization: FaceTecCustomization = FaceTecCustomization() } From 0b101a4af15b2a337a55d0d00cc825a0965c36b1 Mon Sep 17 00:00:00 2001 From: Mark Vayngrib Date: Wed, 28 Oct 2020 19:23:27 -0400 Subject: [PATCH 2/4] refactor: rename method to verifyLiveness --- index.js | 4 +++- ios/RNReactNativeZoomSdk.m | 2 +- ios/ZoomAuth.swift | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 43ff709..e6f1cb0 100644 --- a/index.js +++ b/index.js @@ -65,7 +65,9 @@ const wrapNative = native => { return { initialize, - verify: exec.bind(null, 'verify'), + verifyLiveness: exec.bind(null, 'verifyLiveness'), + // backwards compat + verify: exec.bind(null, 'verifyLiveness'), enroll, authenticate, getVersion, diff --git a/ios/RNReactNativeZoomSdk.m b/ios/RNReactNativeZoomSdk.m index 68c1d4e..b087344 100644 --- a/ios/RNReactNativeZoomSdk.m +++ b/ios/RNReactNativeZoomSdk.m @@ -21,7 +21,7 @@ @interface RCT_EXTERN_REMAP_MODULE(RNReactNativeZoomSdk, ZoomAuth, RCTViewManage resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(verify:(NSDictionary*) options +RCT_EXTERN_METHOD(verifyLiveness:(NSDictionary*) options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) diff --git a/ios/ZoomAuth.swift b/ios/ZoomAuth.swift index 9f87acb..7899c3b 100644 --- a/ios/ZoomAuth.swift +++ b/ios/ZoomAuth.swift @@ -26,7 +26,7 @@ class ZoomAuth: RCTViewManager, ProcessingDelegate, URLSessionTaskDelegate { } // React Method - @objc func verify(_ options: Dictionary, // options not used at the moment + @objc func verifyLiveness(_ options: Dictionary, // options not used at the moment resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) -> Void { self.resolver = resolve From a90782a3fd5536e2eac7b433df2011a67b476110 Mon Sep 17 00:00:00 2001 From: Mark Vayngrib Date: Wed, 28 Oct 2020 19:48:41 -0400 Subject: [PATCH 3/4] refactor: rm unused facemap encryption key --- ios/AuthenticateProcessor.swift | 15 ++++++++------- ios/EnrollmentProcessor.swift | 2 +- ios/ZoomGlobalState.swift | 17 ----------------- 3 files changed, 9 insertions(+), 25 deletions(-) diff --git a/ios/AuthenticateProcessor.swift b/ios/AuthenticateProcessor.swift index c435aa3..35c0621 100644 --- a/ios/AuthenticateProcessor.swift +++ b/ios/AuthenticateProcessor.swift @@ -79,7 +79,7 @@ class AuthenticateProcessor: NSObject, FaceTecFaceScanProcessorDelegate, URLSess parameters["faceScan"] = sessionResult.faceScanBase64 parameters["auditTrailImage"] = sessionResult.auditTrailCompressedBase64![0] parameters["lowQualityAuditTrailImage"] = sessionResult.lowQualityAuditTrailCompressedBase64![0] - parameters["externalDatabaseRefID"] = options["id"] as? String + parameters["externalDatabaseRefID"] = self.options["id"] as! String // // Part 5: Make the Networking Call to Your Servers. Below is just example code, you are free to customize based on how your own API works. @@ -129,12 +129,13 @@ class AuthenticateProcessor: NSObject, FaceTecFaceScanProcessorDelegate, URLSess FaceTecCustomization.setOverrideResultScreenSuccessMessage("Authenticated") faceScanResultCallback.onFaceScanResultSucceed() } - else if (didSucceed == false) { - // CASE: In our Sample code, "success" being present and false means that the User Needs to Retry. - // Real Users will likely succeed on subsequent attempts after following on-screen guidance. - // Attackers/Fraudsters will continue to get rejected. - faceScanResultCallback.onFaceScanResultRetry() - } + // this includes the case where there was no match, so no point in retrying +// else if (didSucceed == false) { +// // CASE: In our Sample code, "success" being present and false means that the User Needs to Retry. +// // Real Users will likely succeed on subsequent attempts after following on-screen guidance. +// // Attackers/Fraudsters will continue to get rejected. +// faceScanResultCallback.onFaceScanResultRetry() +// } else { // CASE: UNEXPECTED response from API. Our Sample Code keys of a success boolean on the root of the JSON object --> You define your own API contracts with yourself and may choose to do something different here based on the error. faceScanResultCallback.onFaceScanResultCancel() diff --git a/ios/EnrollmentProcessor.swift b/ios/EnrollmentProcessor.swift index 9c88226..be54a28 100644 --- a/ios/EnrollmentProcessor.swift +++ b/ios/EnrollmentProcessor.swift @@ -74,7 +74,7 @@ class EnrollmentProcessor: NSObject, FaceTecFaceScanProcessorDelegate, URLSessio parameters["faceScan"] = sessionResult.faceScanBase64 parameters["auditTrailImage"] = sessionResult.auditTrailCompressedBase64![0] parameters["lowQualityAuditTrailImage"] = sessionResult.lowQualityAuditTrailCompressedBase64![0] - parameters["externalDatabaseRefID"] = self.options["id"] as! String; + parameters["externalDatabaseRefID"] = self.options["id"] as! String // // Part 5: Make the Networking Call to Your Servers. Below is just example code, you are free to customize based on how your own API works. diff --git a/ios/ZoomGlobalState.swift b/ios/ZoomGlobalState.swift index 63b643c..46b0af3 100644 --- a/ios/ZoomGlobalState.swift +++ b/ios/ZoomGlobalState.swift @@ -8,23 +8,6 @@ class ZoomGlobalState { // Otherwise, your webservice URL. static let ZoomServerBaseURL = "https://api.facetec.com/api/v3/biometrics" - // The customer-controlled public key used during encryption of FaceMap data. - // Please see https://dev.zoomlogin.com/zoomsdk/#/licensing-and-encryption-keys for more information. - static let PublicFaceMapEncryptionKey = - "-----BEGIN PUBLIC KEY-----\n" + - "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5PxZ3DLj+zP6T6HFgzzk\n" + - "M77LdzP3fojBoLasw7EfzvLMnJNUlyRb5m8e5QyyJxI+wRjsALHvFgLzGwxM8ehz\n" + - "DqqBZed+f4w33GgQXFZOS4AOvyPbALgCYoLehigLAbbCNTkeY5RDcmmSI/sbp+s6\n" + - "mAiAKKvCdIqe17bltZ/rfEoL3gPKEfLXeN549LTj3XBp0hvG4loQ6eC1E1tRzSkf\n" + - "GJD4GIVvR+j12gXAaftj3ahfYxioBH7F7HQxzmWkwDyn3bqU54eaiB7f0ftsPpWM\n" + - "ceUaqkL2DZUvgN0efEJjnWy5y1/Gkq5GGWCROI9XG/SwXJ30BbVUehTbVcD70+ZF\n" + - "8QIDAQAB\n" + - "-----END PUBLIC KEY-----" - - // Used for bookkeeping around demonstrating enrollment/authentication functionality of ZoOm -// static var randomUsername: String = "" -// static var isRandomUsernameEnrolled = false - // this app can modify the customization to demonstrate different look/feel preferences for ZoOm static var currentCustomization: FaceTecCustomization = FaceTecCustomization() } From a6f01801a6243d110700a9f7199ac80845a78c1e Mon Sep 17 00:00:00 2001 From: Mark Vayngrib Date: Thu, 29 Oct 2020 15:54:28 -0400 Subject: [PATCH 4/4] docs: update readme --- README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 926e592..db5358f 100644 --- a/README.md +++ b/README.md @@ -18,20 +18,20 @@ react-native link react-native-facetec-zoom ### iOS -This library has been tested with version 8.1.0 of the SDK +This library has been tested with version 9.0.1 of the iOS SDK (Android support pending) -First, download `ZoomAuthentication.framework` from one of these sources: +First, download `FaceTecSDK.framework` from one of these sources: -- [Zoom SDK](https://dev.zoomlogin.com/zoomsdk/#/downloads) -- [app.tradle.io](https://s3.amazonaws.com/app.tradle.io/sdk/facetec-zoom/ios/8.1.0/ZoomAuthentication.framework.zip) +- [Zoom SDK](https://dev.facetec.com/downloads) -Unzip the file, locate `ZoomAuthentication.framework`, copy it to your `ios/` directory, and drag it to your project in XCode (Check the `Copy items if needed` option when asked) +Unzip the file, locate `FaceTecSDK.framework`, copy it to your `ios/` directory, and drag it to your project in XCode (Check the `Copy items if needed` option when asked) -Add a Copy File phase to your Xcode project and have `ZoomAuthentication.framework` copied to Destination `Frameworks` +Add a Copy File phase to your Xcode project and have `FaceTecSDK.framework` copied to Destination `Frameworks` If you have an Objective-C project, add a blank Swift file to your project (File -> New -> Swift File), with a bridging header (it will prompt you to auto-create one). add `NSCameraUsageDescription` to your Info.plist, e.g.: + ```xml NSCameraUsageDescription verify liveness with Zoom @@ -41,7 +41,7 @@ add `NSCameraUsageDescription` to your Info.plist, e.g.: In your project's build.gradle (android/build.gradle), add the maven block below: -```gradle +````gradle allprojects { repositories { // ... @@ -58,7 +58,7 @@ ext { zoomSdkVersion = '7.0.11' // <--- whichever version you want to use // ... } -``` +```` ``` @@ -67,3 +67,4 @@ This module depends on [react-native-image-store](https://github.com/tradle/reac ## Usage See example app at https://github.com/tradle/rnzoomexample +```