Face authentication
This documentation only describes how to configure the SDK for our face authentication feature. If you want to learn about the feature and the full set of implementation requirements, see the main feature documentation:
Configure the SDK
To use face authentication, you need to incorporate the FaceTec SDKs.
The FaceTec SDK is available for download from our repository. The repository is gated, therefore you require login credentials to access it.
- Request your Nexus repository login credentials from us by creating a support ticket in the Signicat Dashboard.
- Navigate to our repository, then enter your Nexus repository login credentials.
- Download the FaceTec SDK.
- Drag and drop the
FaceTecSDK.xcframeworkfile into your project.NoteEnsure that you select the Copy items if needed checkbox.
- In your project settings, navigate to Target > General. Set the FaceTec framework to Embed & Sign.
- Open the
Info.plistfile, then add the new entry Privacy - Camera Usage Description.
How to implement face authentication
Overview
To implement face authentication, you must typically use the following flow:
- Start the process for either Add or update or for Authentication. You will then receive a
serverSideFaceInitobject. - Initialise the FaceTec SDK with the authorisations received from the
serverSideFaceInitobject. - The end-user performs the FaceScan, which provides a
sessionResult. - Provide the
FaceScanDataobject with thesessionResult, then finish the Add or update or Authentication.
For further details specific to the operation, see the relevant sub-chapters.
Activation
Activation will be supported in a future release. This means that currently, you must:
- Activate with PIN and/or biometrics.
- Use the Add or update operation to add face authentication.
AddOrUpdate
Start call
For an example of startAddOrUpdate, click to expand the collapsible section:
Click to expand
import FaceTecSDK
class ViewController: UIViewController {
var encapController = EncapController.shared
private func startAddOrUpdateOfAuthMethod(clientData: String?) {
let startParam = StartAddOrUpdateParameter(selectedAuthMethod: .serverSideFace(faceScanData: nil))
self.encapController.startAddOrUpdate(startAddOrUpdateParameter: startParam, onCompletion: { result in
switch result {
case .success(let successResult):
if let serverSideFaceInit = successResult.serverSideFaceInit {
self.initializeFacetecSDK(serverSideFaceInitResult: serverSideFaceInit)
}
case .failure(let errorResult):
self.handleError(errorResult, in: .stateStartAddOrUpdate)
}
})
}
}
extension ViewController: FaceTecFaceScanProcessorDelegate {
func initializeFacetecSDK(serverSideFaceInitResult: EncapSwiftAPI.ServerSideFaceInitResult) {
if let deviceKeyIdentifier = serverSideFaceInitResult.deviceKeyIdentifier, let encryptionKey = serverSideFaceInitResult.encryptionKey, let sessionToken = serverSideFaceInitResult.sessionToken, let productionKeyText = serverSideFaceInitResult.productionKeyText {
FaceTec.sdk.initializeInProductionMode(productionKeyText: productionKeyText, deviceKeyIdentifier: deviceKeyIdentifier, faceScanEncryptionKey: encryptionKey, completion: { initializationSuccessful in
if(initializationSuccessful) {
let verificationVC = FaceTec.sdk.createSessionVC(faceScanProcessorDelegate: self, sessionToken: sessionToken)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.present(verificationVC, animated: true, completion: nil)
}
} else {
// Initialization of Facetec SDK failed
}
})
} else {
// Something missing
}
}
// MARK: FaceTec Delegate
func processSessionWhileFaceTecSDKWaits(sessionResult: FaceTecSessionResult, faceScanResultCallback: FaceTecFaceScanResultCallback) {
switch sessionResult.status {
case .sessionCompletedSuccessfully:
faceScanResultCallback.onFaceScanResultCancel()
let faceScanData = FaceScanData(faceScanBase64: sessionResult.faceScanBase64 ?? "", auditTrailImageBase64: sessionResult.auditTrailCompressedBase64?.first ?? "", lowQualityAuditTrailImageBase64: sessionResult.lowQualityAuditTrailCompressedBase64?.first ?? "")
self.handleFinishLogicForFacetec(faceScanData: faceScanData)
case .userCancelled:
faceScanResultCallback.onFaceScanResultCancel()
default:
faceScanResultCallback.onFaceScanResultCancel()
self.showAlert(title: "Error", message: errorHandler(facetecSessionResult: sessionResult))
}
}
}
Finish call
For an example of finishAddOrUpdate, click to expand the collapsible section:
Click to expand
private func handleFinishLogicForFacetec(faceScanData: FaceScanData?) {
let toAuthenticate: AuthMethod = .pin(value: "1111")
// You can also authenticate using other methods e.g.
// let toAuthenticate: AuthMethod = .faceID
// let toAuthenticate: AuthMethod = .strongTouchID(prompt: nil)
self.finishAddOrUpdateOfAuthMethod(withAuthMethod: .serverSideFace(faceScanData: faceScanData),toAuthenticate: toAuthenticate)
}
func finishAddOrUpdateOfAuthMethod(withAuthMethod toActivate: AuthMethod, toAuthenticate: AuthMethod) {
self.animate()
self.activeController.finishAddOrUpdate(authMethodToActivate: toActivate, authMethodToAuthenticate: toAuthenticate,onCompletion: { result in
self.stopAnimate()
switch result {
case .success(let finishAddOrUpdateResult):
// Done
case .failure(let errorResult):
// Handle error
}
})
}
Authentication
To be able to conduct a face authentication, you must start the process using the REST API.
To do this, start a new authentication, where the request has the authentication_method field defined with the value DEVICE_SERVER_SIDE_FACE.
Start call
For an example of startAuthentication, click to expand the collapsible section:
Click to expand
import FaceTecSDK
class ViewController: UIViewController {
var encapController = EncapController.shared
private func startAuthentication(clientData: String?) {
self.encapController.startAuthentication(clientOnly: false, onCompletion: { result in
switch result {
case .success(let successResult):
if let serverSideFaceInit = successResult.serverSideFaceInit {
self.initializeFacetecSDK(serverSideFaceInitResult: serverSideFaceInit)
}
case .failure(let errorResult):
// handle Error
}
})
}
}
extension ViewController: FaceTecFaceScanProcessorDelegate {
func initializeFacetecSDK(serverSideFaceInitResult: EncapSwiftAPI.ServerSideFaceInitResult) {
if let deviceKeyIdentifier = serverSideFaceInitResult.deviceKeyIdentifier, let encryptionKey = serverSideFaceInitResult.encryptionKey, let sessionToken = serverSideFaceInitResult.sessionToken, let productionKeyText = serverSideFaceInitResult.productionKeyText {
FaceTec.sdk.initializeInProductionMode(productionKeyText: productionKeyText, deviceKeyIdentifier: deviceKeyIdentifier, faceScanEncryptionKey: encryptionKey, completion: { initializationSuccessful in
if(initializationSuccessful) {
self.setCustomization()
let verificationVC = FaceTec.sdk.createSessionVC(faceScanProcessorDelegate: self, sessionToken: sessionToken)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.present(verificationVC, animated: true, completion: nil)
}
} else {
// Initialization of Facetec SDK failed
}
})
} else {
// Something missing
}
}
// MARK: FaceTec Delegate
func processSessionWhileFaceTecSDKWaits(sessionResult: FaceTecSessionResult, faceScanResultCallback: FaceTecFaceScanResultCallback) {
switch sessionResult.status {
case .sessionCompletedSuccessfully:
faceScanResultCallback.onFaceScanResultCancel()
let faceScanData = FaceScanData(faceScanBase64: sessionResult.faceScanBase64 ?? "", auditTrailImageBase64: sessionResult.auditTrailCompressedBase64?.first ?? "", lowQualityAuditTrailImageBase64: sessionResult.lowQualityAuditTrailCompressedBase64?.first ?? "")
self.handleFinishLogicForFacetec(faceScanData: faceScanData)
case .userCancelled:
faceScanResultCallback.onFaceScanResultCancel()
default:
faceScanResultCallback.onFaceScanResultCancel()
self.showAlert(title: "Error", message: errorHandler(facetecSessionResult: sessionResult))
}
}
}
Finish call
For an example of finishAuthentication, click to expand the collapsible section:
Click to expand
private func handleFinishLogicForFacetec(faceScanData: FaceScanData?) {
self.finishAuthentication(withAuthMethod: .serverSideFace(faceScanData: faceScanData))
}
func finishAuthentication(with authMethod: AuthMethod, tokenPurpose: TokenPurpose = .none) {
self.encapController.finishAuthentication(withAuthMethod: authMethod, tokenPurpose: tokenPurpose, onCompletion: { result in
switch result {
case .success(let finishAuthResult):
// Done
case .failure(let errorResult):
// handle Error
}
})
}
Error handling
Errors relating to FaceScan are propagated as follows:
- Using the FaceTec delegate in your class, you must have the function
processSessionWhileFaceTecSDKWaits(sessionResult: FaceTecSessionResult, faceScanResultCallback: FaceTecFaceScanResultCallback). - The callback of delegate of the process will be available in sessionResult (
FaceTecSessionResult). - You can check the status, which will indicate whether the session was either:
- Completed successfully.
- The cause of the failure.
Example: Code sample for error handling
Click to expand
func processSessionWhileFaceTecSDKWaits(sessionResult: FaceTecSessionResult, faceScanResultCallback: FaceTecFaceScanResultCallback) {
switch sessionResult.status {
case .sessionCompletedSuccessfully:
// Successfully
case .userCancelled:
faceScanResultCallback.onFaceScanResultCancel() // Dismiss FaceTec screen
default:
faceScanResultCallback.onFaceScanResultCancel() // Dismiss FaceTec screen
self.showAlert(title: "Error", message: errorHandler(facetecSessionResult: sessionResult))
}
}
private func errorHandler(facetecSessionResult: FaceTecSessionResult) -> String {
var subTitle = ""
switch facetecSessionResult.status {
case .sessionUnsuccessful:
subTitle = "The Facetec Session was not performed successfully and a FaceMap was not generated."
case .nonProductionModeKeyInvalid:
subTitle = "Your license is invalid or network connectivity issues occur during a session"
case .cameraPermissionDenied:
subTitle = "Camera Permission Denied"
case .contextSwitch:
subTitle = "Session was canceled due to the app being terminated, put to sleep, an OS notification, or the appwas placed in the background"
case .landscapeModeNotAllowed:
subTitle = "Session was canceled because device is in landscape mode"
case .reversePortraitNotAllowed:
subTitle = "Session was canceled because device is in reverse portrait mode"
case .timeout:
subTitle = "Session was canceled because the user was unable to complete a Facetec Session in the defaultallotted time"
case .lowMemory:
subTitle = "Session was canceled due to memory pressure"
case .nonProductionModeNetworkRequired:
subTitle = "Session was canceled because your App is not in production and requires a network connection"
case .gracePeriodExceeded:
subTitle = "Session was canceled because your License needs to be validated again"
case .encryptionKeyInvalid:
subTitle = "Session was canceled because the developer-configured encryption key was not valid"
case .missingGuidanceImages:
subTitle = "Session was canceled because not all guidance images were configured"
case .cameraInitializationIssue:
subTitle = "Session was canceled because Facetec was unable to start the camera on this device"
case .lockedOut:
subTitle = "Session was canceled because the user was in a locked out state"
case .unknownInternalError:
subTitle = "Session was canceled because of an unknown and unexpected error"
default:
subTitle = "\(facetecSessionResult.status.rawValue)"
}
return subTitle
}
Localisation
The FaceTecSDK includes language resource strings prefixed with FaceTec_. It supports the following languages:
- Afrikaans (af)
- Arabic (ar)
- German (de)
- Greek (el)
- English (en)
- Spanish (es)
- French (fr)
- Kazakh (kk)
- Norwegian (nb)
- Portuguese (pt-BR)
- Russian (ru)
For further details, see the FaceTecSDK.zip file.
Branding and styling
What elements can be customised?
The FaceTec SDK allows you to customise various branding and styling elements, such as:
- Interface
- Colours
- Fonts
- Borders
To learn more about what you can customise, see the If you want to know more about what you can customise see the UX, Themes & Branding page in the FaceTec documentation.
How to customise the configuration
To make changes, you have to create a FaceTecCustomization object, where you define each field that you want to change.
Example: Facetec customisation configuration
let blueLeft = UIColor.init(red: 97/255, green: 210/255, blue: 250/255, alpha: 1)
let blueRight = UIColor.init(red: 28/255, green: 122, blue: 247/255, alpha: 1)
let gradientCgColor: [CGColor] = [blueLeft.cgColor, blueRight.cgColor]
let fontBody = UIFont.systemFont(ofSize: 16, weight: .regular)
let customization = FaceTecCustomization()
customization.overlayCustomization.brandingImage = UIImage(named: "brandLogo")
customization.frameCustomization.borderColor = blueLeft
let backgroundLayer = CAGradientLayer.init()
backgroundLayer.colors = gradientCgColor
backgroundLayer.locations = [0,1]
backgroundLayer.startPoint = CGPoint.init(x: 0, y: 0.5)
backgroundLayer.endPoint = CGPoint.init(x: 1, y: 0.5)
customization.feedbackCustomization.backgroundColor = backgroundLayer
customization.feedbackCustomization.textFont = fontBody
customization.ovalCustomization.strokeColor = blueLeft
customization.ovalCustomization.progressColor1 = blueLeft
customization.ovalCustomization.progressColor2 = blueRight
customization.guidanceCustomization.foregroundColor = blueLeft
customization.guidanceCustomization.readyScreenTextBackgroundColor = blueLeft
customization.guidanceCustomization.buttonFont = fontBody
customization.guidanceCustomization.buttonBackgroundNormalColor = blueLeft
customization.guidanceCustomization.readyScreenTextBackgroundColor = blueLeft
customization.resultScreenCustomization.foregroundColor = blueRight
customization.resultScreenCustomization.activityIndicatorColor = blueLeft.withAlphaComponent(0.8)
customization.resultScreenCustomization.uploadProgressTrackColor = blueLeft.withAlphaComponent(0.8)
customization.resultScreenCustomization.messageFont = fontBody
FaceTec.sdk.setCustomization(customization)
Different environmental conditions
You can apply different settings based on environmental conditions, such as low-light or bright-light situations.
To do this, you must set a unique configuration for each of the scenarios using the FaceTecCustomization object.
- Default:
Example: Default configuration
FaceTec.sdk.setCustomization(customization) // Default - Low-light
Example: Low-light configuration
FaceTec.sdk.setLowLightCustomization(customization) // low-light - Bright-light
Example: Bright-light configuration
FaceTec.sdk.setDynamicDimmingCustomization(customization) // bright-light
Multiple controller scenario
For a multiple controller scenario, face authentication must be activated for each controller.