Fortface SDK Integration in Your Android App

How does it work?

Our SDK is designed to perform the Capture and/or Upload of the file of the document photo that will be sent to our Fortface API. It is independent of the app’s flow into which it will be integrated. It has its own isolated Activity that can be launched at any time by an Engine. Immediately after processing, the tool is automatically terminated.

Our SDK does not communicate directly with the Fortface API; see the communication flow below:

Communication Flow

Below are the instructions for integrating our SDK, the process flow to correctly access our service, and a step-by-step guide on how to build an Engine.

Installation

Requirement

1. Platform Configuration

Android
Minimum version: Android 8.0 (Oreo) - SDK 26

It is currently possible to use the Fortface SDK in applications that support Android 6 - SDK 24 as the minimum version. However, by doing so, the developer gives up important security features of the tool, thereby assuming risks of attacks during captures.

Kotlin

Minimum version: 1.7.20

Installation

1. Add access to the Maven repository to your project

First you need to configure access to the Maven repository in your project. The artifact is served via CDN, with HTTP header authentication. Include the following block in repositories (for example in the app module’s build.gradle / build.gradle.kts, or in settings.gradle / settings.gradle.kts if your project uses dependencyResolutionManagement).

app/build.gradle
repositories {
maven {
url "https://cdn-fortface-sdk.fortface.com.br"
credentials(HttpHeaderCredentials) {
name = "X-Sdk"
value = "ACCOUNT_NAME"
}
authentication {
header(HttpHeaderAuthentication)
}
}
}

Replace ACCOUNT_NAME with the account identifier provided by our team.

Depending on your project configuration, you may need to declare the same repository in settings.gradle / settings.gradle.kts (for example inside dependencyResolutionManagement { repositories { ... } }).

2. Configure Gradle

With the repository configured, you need to enable viewBinding and add the dependency for our SDK. Below you can find the changes to be added to your app's Gradle file:

app/build.gradle
android {
...
buildFeatures {
viewBinding true
}
}

dependencies {
implementation (group: 'br.com.fortface', name: 'fortface', version:'1.19.2', ext: 'aar', classifier: 'release'){
transitive = true
}
}

Do not forget to synchronize your project with the Gradle files after making these changes.

3. Include Proguard rules

Our SDK already has obfuscation and minification for security assurance; however, some classes from the SDK need to be kept without obfuscation to ensure your app functions correctly. Add the following rules to your proguard-rules.pro file:

-keep class br.com.fortface.sdk.** { *; }

Permission

1. Adjust the AndroidManifest.xml

Our SDK uses the camera, so you must configure camera access permission in your app's manifest. To do so, add the following lines to the file:

app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

<application
...

Our SDK also has a permission for location to collect geolocation coordinates of the device for reinforcement in antifraud mechanisms. If you want to collect the geolocation coordinates of the device, add the location permission to your app's manifest as well.

app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<application
...

2. Ensure Camera Access

For your application to request camera access permission, add the following code in your Activity:

Kotlin Snippet

class YourActivity: AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
// ...
if (!allPermissionsGranted()) {
requestPermission()
}
}

private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(this.baseContext, it) == PackageManager.PERMISSION_GRANTED
}

private fun requestPermission() {
ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
}

companion object {
const val REQUEST_CODE_PERMISSIONS = 10
val REQUIRED_PERMISSIONS = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.ACCESS_FINE_LOCATION, // Optional
Manifest.permission.ACCESS_COARSE_LOCATION // Optional
)
}

// ...
}

After this configuration, your application is ready to access our SDK.

Pre-initialization

Before starting the capture process, it is necessary to establish the device integrity to ensure secure communication with the Fortface API. Therefore, you need to:

  1. Import our SDK into your app;
  2. Instantiate FortfaceSdk.

Establish Device Integrity

Initialize the SDK and obtain the deviceRequestInfo.

import br.com.fortface.sdk.FortfaceSdk

val deviceRequestInfo = FortfaceSdk.start(appContext, false)
println(deviceRequestInfo)
// Q4ezAXfMdAdW1s0xdwCcg0Jzj9kAjuNwzNrWMip4ADAylFQAGCgBOHT7HJYkPkXUPmLZ3XtJYOgTi/WKh+o2Vstk2MGy4qRz4fcBpCu+raS1ghHa4DDmqUjbSMPd3Fdo2zrcw0sb81FDf/Tos2A5UaIqMhzurkm0aYgC/izKhu3H+RQXJBHjFVKUIq8WPVbC/ITKon2T703C6fgdQtcpknaxAvIN2/eDRxBrsSuS3fbIi1TOoGAIi/Ezwj0utZLYUwhnKImaabvvXFdsvVxp6rHLiq1uHqZbm4tfRWJw4mkk47JHVEfUIJMFjS1ORpqoN1dmZzPQHAo0CtPfYGa9OB8NGnmgRRbWHXWrXPoptaRY31yoaT+HGwBwB/pfL8gUxoVppOYBUfIsvSQoaIQ+6g==

In FortfaceSdk, the start method is used with 2 parameters and returns a String:

  • Parameter context: represents the context of your application.
  • Parameter isDebugMode: a boolean that enables or disables debug mode. The default value is false
    • Use true to enable debug mode;
    • Use false in production, so that the device integrity is verified and, if not suitable for transactions, a FortfaceException with the message There is something wrong! will be thrown.
  • Return stored in deviceRequestInfo: a string used to validate the integrity of the device connected to our SDK. A new deviceRequestInfo is generated each time the method is called (the code above shows an example).

With deviceRequestInfo in hand, you can now perform a handshake call between your back-end and the Fortface API.

The handshake is an important security measure to establish reliable communication between your back-end and the Fortface API, allowing verification of device authenticity, sharing of sensitive information (encryption keys and sessions), and establishing a secure channel for data exchange.

Upon successful handshake, you are guaranteed that the device is secure and your app has the necessary information to proceed.

Capture and upload of documents

File Size Limit

The maximum file size (PDF/Image) for upload is 4MB. Larger files will be rejected with error code FileSizeTooBig.

Once device integrity is established, let’s see how to start the session and use the IFortfaceEngine interface to perform the capture.

Establishing the Device Session

Implement a class that extends IFortfaceEngine.

Document Capture

import br.com.fortface.sdk.FortfaceManagerSession
import br.com.fortface.sdk.IFortfaceEngine
import br.com.fortface.sdk.utils.SessionDetails

class YourEngine(
appContext: Context,
sessionKey: String,
sessionId: String,
private val yourViewModel: ViewModel,
listDocumentTypes: List<DocumentTypeEnum>
) : IFortfaceEngine {

init {
val sessionDetails = SessionDetails(
getGeolocation = true // 👈 Optional: define if the SDK should request location permission and collect coordinates for antifraud reinforcement.
)
FortfaceManagerSession.startSessionDocumentEngine(
appContext,
this,
sessionId,
sessionKey,
listDocumentTypes,
null,
sessionDetails // can be omitted if you don't need the additional controls of usability to the session
)
}
}

In this class, you initialize FortfaceManagerSession and its method startSessionDocumentEngine, which has 5 parameters for document capture:

  • context: represents your application's context.
  • engine: represents your Engine class that implements the IFortfaceEngine interface of our SDK. This will be explained later in the Creating an Engine section.
  • sessionId: session id generated by the Fortface API.
  • sessionKey: encryption key generated by the Fortface API.
  • sessionDetails: optional object that allows adding other controls of usability to the session.
    • getGeolocation: define if the SDK should request location permission and collect coordinates for antifraud reinforcement.

The startSessionDocumentEngine method, which currently takes 7 parameters for document capture, will be discontinued. This method is already marked as obsolete (@Deprecated) and will stop functioning in future SDK versions.

It is recommended to update your implementation to use the new version of the method, which takes 5 parameters, without using the documentTypes and previousDocType parameters.

Document Upload

File Size Limit

The maximum file size (PDF/Image) for upload is 4MB. Larger files will be rejected with error code FileSizeTooBig.

import br.com.fortface.sdk.FortfaceManagerSession
import br.com.fortface.sdk.IFortfaceEngine
class YourEngine(appContext: Context, sessionKey: String, sessionId: String, private val yourViewModel: ViewModel, listDocumentTypes:List<DocumentTypeEnum>
) : IFortfaceEngine {
init {
val sessionDetails = SessionDetails(
getGeolocation = true // 👈 Optional: define if the SDK should request location permission and collect coordinates for antifraud reinforcement.
)
FortfaceManagerSession.startUploadDocSessionEngine(appContext, this, sessionId, sessionKey, listDocumentTypes,true, null, sessionDetails)
}
}

In this class, initialize FortfaceManagerSession and its method startUploadDocSessionEngine which has 6 parameters for document capture:

  • context: represents your application's context.
  • engine: represents your Engine class that implements the IFortfaceEngine interface of our SDK. This will be explained later in the Creating an Engine section.
  • sessionId: session id generated by the Fortface API.
  • sessionKey: encryption key generated by the Fortface API.
  • openPhotoGalery: represents whether the SDK will open the photo gallery (true) or the file manager (false).
  • sessionDetails: optional object that allows adding other controls of usability to the session.
    • getGeolocation: define if the SDK should request location permission and collect coordinates for antifraud reinforcement.

The startUploadDocSessionEngine method, which currently takes 8 parameters for document capture, will be discontinued. This method is already marked as obsolete (@Deprecated) and will stop functioning in future SDK versions.

It is recommended to update your implementation to use the new version of the method, which takes 6 parameters, without using the documentTypes and previousDocType parameters.

By initializing the FortfaceManagerSession using the provided parameters, you correctly configure the secure communication session and ensure that the data transmitted between the app and the server is properly encrypted and protected.

Mock data for example:

AppContext:
Your application's context

Engine:
Reference to your Engine that implements IFortfaceEngine

SessionId:
nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2k

SessionKey:
-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2k+o81Oiti4TDezZYD8Q\nzj2RjO5f/YQHGmC1v7CWC4AJpD9+FOXBBO1pImaSg1gb8kOobRFGWbVdiHm35+PF\nTJceH31r+jKnvfc1TshsQVGtYLm7x5E/GFcjpGx9LdSxiXC6cHdCJf3o02/lytBa\nu8NCoH9FpPjyKrdGdSRb2haviAuEV4N1UBd5uZIsacVqZj52i1z+I94gLVKBZ/UV\npaxjLPWWngmaxbyKE0sEbt9JGleiCb2P1kZA9sU4L4Po7WBNk0+QOsvW9QjvS1+6\ntcZwwIDll2LyUceiFgxF6GkuFcHlBZDDtzymDDC2IYrf9/FNBOPeV1JHXON8T6DY\nAwIDAQAB\n-----END PUBLIC KEY-----\n

Handling the sessionKey

The string that represents the sessionKey contains special characters (such as the line break \n) that must be preserved exactly as received from the Fortface API. Be careful with any accidental manipulation that may occur in:

  • Form inputs
  • Logs and monitoring systems
  • String processing in your backend
  • Transfers between services
  • Database storage

Any change to these characters may cause the key validation to fail. Make sure the key is passed to the SDK exactly as received from the Fortface API.

Development facilitation

For local frontend development only, you may reuse the same valid sessionKey as a mock as many times as necessary. The single-use restriction applies only in the context of backend development, when the Fortface API validates the key.

documentTypes: List of DocumentTypeEnum with the desired document options

previousDocType: Reference to the type of document captured previously (DocumentTypeEnum) in case of capturing the second side of the same document.

Understanding the code snippet:

  • The class constructor will call FortfaceManagerSession.start
  • The session is created and the camera is rendered on the screen to capture the user's document.

ATTENTION!!

The flow is executed for each “side” of the document, i.e., front and back. Each must follow the complete flow, going through the Handshake and startSessionDocumentEngine. For capturing the second side, you must provide the document type (document side) recognized in the first capture; this information is available in the response from the Fortface API service. The property in the response JSON referring to the document type is the type property contained in the fortfaceSdk object.

Example response JSON from the Fortface service API for document capture:

{
"api":{
"timestamp":1741712439604,
"version":"v1.14"
},
"handshake":{
"sessionId":"187ffa8d-016d-c0962506f5ab",
"sessionKey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2t6V+LTwyClMN/2Dy5W8\nrJKMAwP45QTq+8e3RcNowUqKEDrsjSkBDYugYCaEuLY66pP3ndm8MfenHxuxAqGC\n3NagAUr83+kJufsizuUJTYC2/1CXeox8U1j4S0Ewt/4rr9SIZCwBisiFiT0THt6QFjE\nmwWMYVT4RjRUK8LV6pF1HJvjFHI6oXF2KjjFwVqXur5LB7sj9f70ddcp3Fiv/3Tn\nVQIDAQAB\n-----END PUBLIC KEY-----\n"
},
"message":"",
"providers":{
"fortfaceSdk":{
"automatic":true,
"confidence":0.73,
"type":"CNH_FRONT"
},
"identify":{
"status":"PENDING"
},
"ocr":{
"status":"PENDING",
"type":"ocr"
}
},
"sdk":{
"brand":"POCO",
"model":"M2012K11AG",
"platform":"android"
},
"title":""
}

Creating an Engine

Through the Engine, you can access the information processed by our SDK. To create an Engine, the class must implement the IFortfaceEngine interface; therefore, you must implement the finishedSessionEngine method. This method will receive the data processed by the SDK, which is a package containing an action and the captured image data.

Below is a complete implementation example of a capture, for instance, the YourEngine class that implements the IFortfaceEngine interface, containing the initialization of the startSessionEngine and/or startUploadDocSessionEngine session and handling of the SessionEngineResult callback.

Kotlin Snippet

import br.com.fortface.sdk.Action
import br.com.fortface.sdk.SessionEngineResult
import br.com.fortface.sdk.FortfaceManagerSession
import br.com.fortface.sdk.IFortfaceEngine

class YourEngine(appContext: Context, sessionKey: String, sessionId: String, private val yourViewModel: ViewModel) : IFortfaceEngine {

init {
FortfaceManagerSession.startSessionDocumentEngine(appContext, this, sessionId, sessionKey)
}

override fun finishedSessionEngine(sessionEngineResult: SessionEngineResult) {
when (sessionEngineResult.action) {
Action.Capture -> yourViewModel.captured(sessionEngineResult) // Here the flow ends and these are the data to be sent
Action.Cancel -> yourViewModel.canceled() // Here the user canceled the capture and you should proceed with your cancellation flow
Action.Timeout -> yourViewModel.timeout() // Here the capture was canceled because time ran out. You should proceed with the timeout flow
Action.Error -> yourViewModel.error() // Here the capture was canceled because an error occurred. You should proceed with the error flow
else -> {}
}
}
}

Note that the finishedSessionEngine method is a callback that receives a SessionEngineResult object. The callback has three pieces of information:

  • action: represents an enumerator containing:

    • Capture: callback for a successful capture by the user
    • Cancel: callback for a capture canceled by the user
    • Timeout: callback for a capture with the time limit reached by the user
      • This action represents the time during which the SDK remains open, whether a capture occurs or not. When time runs out, the SDK is terminated and the camera is closed;
      • The default time is 30 seconds;
    • Error: callback for a capture with an error during the process
      • This action represents an error generated during the capture and/or upload of the document
  • getData: represents the captured data encrypted in a JSONObject. The JSON follows this structure:

{
key: String,
data: String,
imgData: String,
imgPreview: String
}
  • sessionDetails: is a type of data that contains an errorCode
    • errorCode: represents a code that can occur during the capture and/or upload process, the possible values are:
      • DocumentNotFound: returned when the selected file (PDF/Image) cannot be found.
      • FileSizeTooBig: returned when the selected file (PDF/Image) is larger than the allowed limit of 4MB.
      • FileCorrupted: returned when the selected file (PDF/Image) is corrupted.
      • InvalidSessionKey: code returned when the sessionKey provided to the SDK is invalid.
      • CameraAccessPermissionError: code returned when the user does not grant camera access to the application.
      • ImageClosed: code returned when the capture process attempts to handle a frame whose image is unavailable.
      • OutOfMemory: code returned when the device does not have enough available memory for the SDK to continue processing.

Thus, as shown in the example above, it is possible to assign each action to a specific implementation in your application. Additionally, once the information from the getData method is obtained, your application can transport it in the most convenient way to your backend and then to the Fortface API.

It is important to note that our SDK API will include enroll and identify flows, so feel free to architect Engines corresponding to each of these flows, a generic Engine, or any other desired solution.

Using Our SDK

In the SDK usage example, we will use the MVVM architecture. Thus, we have the view model:

class YourViewModel: ViewModel() {
fun captured(sessionEngineResult: SessionEngineResult) {
// Implementation of what to do with the captured data
}

fun canceled() {
// Implementation for SDK cancellation
}

fun timeout() {
// Implementation for SDK timeout
}

fun error() {
// Implementation of error from the SDK
}
}

The SDK is free to be implemented in any project structure. In this documentation, we use the standard architecture pattern adopted in the market.

Below is an example code of the flow that should be implemented in your app to access our SDK:

import br.com.fortface.sdk.FortfaceSdk
import androidx.activity.viewModels
//...

class YourActivity : AppCompatActivity() {
val yourViewModel: YourViewModel by viewModel()
//...

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

if (!allPermissionsGranted()) {
requestPermission()
}

val button = findViewById<Button>(R.id.button)
button.setOnClickListener {
startEngine()
}
}

fun startEngine() {
val deviceRequestInfo = FortfaceSdk.start(this, false)
// Here you send it for the Fortface API handshake via your backend
// ...
// With the response you will have the sessionKey and sessionId
val sessionKey = "" // Response from the Fortface API
val sessionId = "" // Response from the Fortface API
YourEngine(this, sessionKey, sessionId, yourViewModel)
}
}

Versioning

To know which version of our SDK is being used, simply call the function FortfaceSDK.getVersion() which will return a String with its value. Example:

String sdkVersion = FortfaceSDK.getVersion()
System.out.println(sdkVersion);
// 1.17.0

Cancellation and Timeout Callback

When an attempt to capture the user's face is not successfully completed—for example, when the user cancels the capture or the capture does not occur within the specified time—it is important that this event is sent to the backend so that we have correct metrics for capture successes or failures.

To allow us to have data for canceled captures or those that timed out, our SDK includes callbacks for capture cancellation and capture timeout actions.

The SessionEngineResult received in the finishedSessionEngine function is an object that has two properties: data and action (the action performed by the SDK). Previously, the data field was only filled upon successful face capture.

As mentioned earlier in the Capture section, the data contains encrypted information and can be converted to JSON. However, unlike a successful capture, in this scenario after conversion to JSON, we will only have the following fields:

{
key: String,
data: String
}

Reminder: The keys “key” and “data” are generated by the SDK, sent to your backend, and then to the Fortface API.

Implementation Example

  1. The SDK detects that the capture was canceled.
  2. An encrypted object is created.
  3. The SDK is terminated and the generated payload along with the performed action is sent.
  4. In the implementation app, within the class that extends IFortfaceEngine, the action is detected.
import br.com.fortface.sdk.Action
import br.com.fortface.sdk.SessionEngineResult
import br.com.fortface.sdk.FortfaceManagerSession
import br.com.fortface.sdk.IFortfaceEngine

class YourEngine(appContext: Context, sessionKey: String, sessionId: String, private val yourViewModel: ViewModel) : IFortfaceEngine {

// ...

override fun finishedSessionEngine(sessionEngineResult: SessionEngineResult) {
when (sessionEngineResult.action) {
Action.Capture -> yourViewModel.captured(sessionEngineResult)

// When the cancel action is detected, the canceled function is called
// which receives the data generated by the SDK.

Action.Cancel -> yourViewModel.canceled(sessionEngineResult)

Action.Timeout -> yourViewModel.timeout(sessionEngineResult)

else -> {}
}
}
}
  1. Within your ViewModel, the canceled() function is called.
class YourViewModel: ViewModel() {
// ...

fun canceled(sessionEngineResult: SessionEngineResult) {
// Indicate that the cancel action was received
cancelAction.postValue(true)
// Create the cancellation payload in Metric format
createPayload(sessionEngineResult)
}

// ...

private fun createPayload(sessionEngineResult: SessionEngineResult) {
val encryptedPayload = sessionEngineResult.getData().toString()
if (encryptedPayload != "null") {
// Parse the JSON to the SDKData object (example below)
// In this case, the imgData field will be null
val sdkData = Gson().fromJson(encryptedPayload, SDKData::class.java)
// Indicate that a metric was received and send the Metrics
sessionEngineResultValue.postValue(Metrics(sdkData))
}
}
}
  1. Metrics Object
@Keep
data class Metrics(
@SerializedName("sdkData")
var sdkData: SDKData
)
  1. SDKData Object
@Keep
data class SDKData(
@SerializedName("key")
val key: String,
@SerializedName("data")
val data: String,
@SerializedName("imgData")
val imgData: String
)
  1. After triggering the metric reception, the Metrics are sent to the API.
class YourFragment : Fragment() {
// ...
// Observer sees that a new metric is available and sends the data to the Metrics API

viewModel.sessionEngineResultValue.observe(viewLifecycleOwner) {
context?.let { context -> viewModel.sendMetricsToServer(context, it) }
}

}

Summary

  1. Verify camera access permission
  2. Capture the deviceRequestInfo
  3. Perform the Handshake with the Fortface API (via your backend)
  4. Start an Engine
  5. Receive the data in the Engine callback
  6. Send the data to the Fortface API (via your backend)
  7. Receive the response from the Fortface API and proceed with your business logic