Skip to content

Complete Server-Side Verification

While it's true that all authenticity checks can be successfully completed on smartphones and edge devices, they alone might not suffice. There's a potential risk of verification results being intercepted and modified by fraudsters directly on the same device. In the "zero trust to mobile" approach, Regula introduces an additional layer of protection against such fraud through Complete Server-Side Verification.

Let's have a look at it step by step.

Preparation

Enable the backend processing feature on the Mobile side.

let backendProcessingConfig = RGLBackendProcessingConfig()
backendProcessingConfig.url = "https://api.regulaforensics.com"
DocReader.shared.processParams.backendProcessingConfig = backendProcessingConfig
RGLBackendProcessingConfig *backendProcessingConfig = [[RGLBackendProcessingConfig alloc] init];
backendProcessingConfig.serviceURL = @"https://api.regulaforensics.com";
RGLDocReader.shared.processParams.backendProcessingConfig = backendProcessingConfig;
DocumentReader.Instance().processParams().backendProcessingConfig = BackendProcessingConfig("https://api.regulaforensics.com")
DocumentReader.Instance().processParams().backendProcessingConfig = new BackendProcessingConfig("https://api.regulaforensics.com");
DocumentReader.instance.processParams.backendProcessingConfig = BackendProcessingConfig("https://api.regulaforensics.com");
DocumentReader.setProcessParams({
  backendProcessingConfig: {
    url: "https://api.regulaforensics.com"
  }
}, _ => { }, _ => { })
DocumentReader.setProcessParams({
  backendProcessingConfig: {
    url: "https://api.regulaforensics.com"
  }
})
DocumentReader.setProcessParams({
  backendProcessingConfig: {
    url: "https://api.regulaforensics.com"
  }
}, function (m) { }, function (e) { })
DocumentReader.Instance().ProcessParams().BackendProcessingConfig = new BackendProcessingConfig("https://api.regulaforensics.com");

// iOS
RGLBackendProcessingConfig backendProcessingConfig = new()
{
    Url = "https://api.regulaforensics.com"
};
RGLDocReader.Shared.ProcessParams.BackendProcessingConfig = backendProcessingConfig;

For Complete Server-Side Verification, you need to run the Regula Web Service in your environment. Refer to the Web Service section for instructions on how to do that.

HTTP Headers

Since Regula Web Service is engaged when working with Regula Mobile SDK, you may need to adjust HTTP requests’ headers in your application to pass additional information to the Web Service. That can be achieved in the following way:

let backendProcessingConfig = RGLBackendProcessingConfig()
backendProcessingConfig.url = "https://api.regulaforensics.com"
backendProcessingConfig.httpHeaders = ["HttpHeader1": "value1", "HttpHeader2": "value2"]
DocReader.shared.processParams.backendProcessingConfig = backendProcessingConfig
RGLBackendProcessingConfig *backendProcessingConfig = [[RGLBackendProcessingConfig alloc] init];
backendProcessingConfig.serviceURL = @"https://api.regulaforensics.com";
backendProcessingConfig.httpHeaders = @{@"HttpHeader1": @"value1", @"HttpHeader2": @"value2"};
RGLDocReader.shared.processParams.backendProcessingConfig = backendProcessingConfig;
val httpHeaders: MutableMap<String, String> = HashMap()
httpHeaders["header1"] = "key1"
httpHeaders["header2"] = "key2"

val backendProcessingConfig = BackendProcessingConfig("https://api.regulaforensics.com")
backendProcessingConfig.httpHeaders = httpHeaders
DocumentReader.Instance().processParams().backendProcessingConfig = backendProcessingConfig
Map<String, String> httpHeaders = new HashMap<>();
httpHeaders.put("header1", "key1");
httpHeaders.put("header2", "key2");

BackendProcessingConfig backendProcessingConfig = new BackendProcessingConfig("https://api.regulaforensics.com");
backendProcessingConfig.setHttpHeaders(httpHeaders);
DocumentReader.Instance().processParams().backendProcessingConfig = backendProcessingConfig;
Map<String, String> httpHeaders = {};
httpHeaders["header1"] = "key1";
httpHeaders["header2"] = "key2";

BackendProcessingConfig backendProcessingConfig = BackendProcessingConfig(
    "https://api.regulaforensics.com",
    httpHeaders: httpHeaders
    );
DocumentReader.instance.processParams.backendProcessingConfig = backendProcessingConfig;
const httpHeaders = {
  header1: 'key1',
  header2: 'key2',
};

DocumentReader.setProcessParams({
  backendProcessingConfig: {
    url: "https://api.regulaforensics.com",
    httpHeaders: httpHeaders
  }
}, _ => { }, _ => { })
const httpHeaders: Record<string, string> = {
  header1: 'key1',
  header2: 'key2',
};

DocumentReader.setProcessParams({
  backendProcessingConfig: {
    url: "https://api.regulaforensics.com",
    httpHeaders: httpHeaders
  }
})
const httpHeaders = {
  header1: 'key1',
  header2: 'key2',
};

DocumentReader.setProcessParams({
  backendProcessingConfig: {
    url: "https://api.regulaforensics.com",
    httpHeaders: httpHeaders
  }
}, function (m) { }, function (e) { })
/// Android
var httpHeaders = new Dictionary<string, string> 
{
    ["header1"] = "key1",
    ["header2"] = "key2"
};
var backendProcessingConfig = new BackendProcessingConfig("https://api.regulaforensics.com")
{
    HttpHeaders = httpHeaders
};
DocumentReader.Instance().ProcessParams().BackendProcessingConfig = backendProcessingConfig;

// iOS
var httpHeaders = new NSDictionary 
{
    ["header1"] = new NSString("key1"),
    ["header2"] = new NSString("key1")
};
RGLBackendProcessingConfig backendProcessingConfig = new()
{
    Url = "https://api.regulaforensics.com",
    HttpHeaders = httpHeaders
};
RGLDocReader.Shared.ProcessParams.BackendProcessingConfig = backendProcessingConfig;

Step 1: Optical Processing

The first step is optical processing that is performed on the mobile side.

To initiate optical processing, follow the Start Processing guide.

Step 2: RFID Chip Reading (Optional)

Once the optical processing is completed, encrypted results are generated. The next step is to perform RFID chip reading.

To start the processing, follow the RFID Chip Processing guide.

While reading is performed on the mobile via NFC, the Regula Web Service becomes engaged in generating session keys and challenges, which are subsequently saved to Storage and Database.

As soon as NFC chip processing is completed on the mobile side, the results will contain not only the optical outcomes but also the results of RFID chip processing.

To enable RFID reprocessing on the Regula Web Service, refer to the RFID Processing page.

Step 3: Finalize Package

The next step is to finalize results. It means that they are encrypted on the mobile side and then are transmitted to the Regula Web Service via the secure communication channel.

During transmission, Storage and Database are become engaged in the saving transaction info, metadata, results and so on.

Once the package is sent to the Regula Web Service, you need to handle the transaction ID that is returned in the callback and prepare it for sending to your service.

DocReader.shared.finalizePackage { action, transactionInfo, error in
    if error != nil {
        guard let error = error else {
            return
        }
        print("Finalize failed. Error: \(error)")
    } else {
        if action == .complete, let transactionId = transactionInfo?.transactionId {
            print("Finalize done. Transaction ID: \(transactionId)")
        }
    }
}
[[RGLDocReader shared] finalizePackageWithCompletion:^(RGLDocReaderAction action, RGLTransactionInfo * _Nullable info, NSError * _Nullable error) {
    if (error != nil) {
        NSLog(@"Finalize failed. Error: %@", error);
    } else {
        if (action == RGLDocReaderActionComplete) {
            NSLog(@"Finalize done. Transaction ID: %@", info.transactionId);
        }
    }
}];
DocumentReader.Instance().finalizePackage(IDocumentReaderFinalizePackage { action, transactionInfo, error ->
    error?.let {
        Log.d("Activity", "Finalize failed. Error: " + error.message)
        return@IDocumentReaderFinalizePackage
    }
    transactionInfo?.let {
        if (action == DocReaderAction.COMPLETE) {
            Log.d(
                "Activity",
                "Finalize done. Transaction ID: " + transactionInfo.transactionId
            )
        }
    }
})
DocumentReader.Instance().finalizePackage(new IDocumentReaderFinalizePackage() {
    @Override
    public void onFinalizePackage(int action, @Nullable TransactionInfo transactionInfo, @Nullable DocumentReaderException error) {
        if (error != null) {
            Log.d("Activity","Finalize failed. Error: " + error.getMessage());
            return;
        }

        if (action == DocReaderAction.COMPLETE && transactionInfo != null) {
            Log.d("Activity","Finalize done. Transaction ID: " + transactionInfo.transactionId);
        }
    }
});
var (action, info, error) = await DocumentReader.instance.finalizePackage();
if (error != null) {
  print("Finalize failed. Error: " + error.message);
} else if (action == DocReaderAction.COMPLETE && info != null) {
  print("Finalize done. Transaction ID: " + info.transactionId!);
}
DocumentReader.finalizePackage(response => {
  response = JSON.parse(response)
  if (response["error"] != null) {
    console.log("Finalize failed. Error: " + response["error"])
  } else if (response["action"] == DocReaderAction.COMPLETE && response["info"] != null) {
    console.log(("Finalize done. Transaction ID: " + response["info"]))
  }
}, _ => { })
DocumentReader.finalizePackage().then(response => {
  response = JSON.parse(response)
  if (response["error"] != null) {
    console.log("Finalize failed. Error: " + response["error"])
  } else if (response["action"] == DocReaderAction.COMPLETE && response["info"] != null) {
    console.log(("Finalize done. Transaction ID: " + response["info"]))
  }
})
DocumentReader.finalizePackage(function (response) {
  response = JSON.parse(response)
  if (response["error"] != null) {
    console.log("Finalize failed. Error: " + response["error"])
  } else if (response["action"] == DocReaderAction.COMPLETE && response["info"] != null) {
    console.log(("Finalize done. Transaction ID: " + response["info"]))
  }
}, function (e) { })
// Android
DocumentReader.Instance().FinalizePackage(this);

public void OnFinalizePackage(int action, TransactionInfo info, DocumentReaderException error)
{
    if (error != null)
    {
        Console.WriteLine("Activity", "Finalize failed. Error: " + error.Message);
        return;
    }
    if (action == DocReaderAction.Complete && info != null)
    {
        Console.WriteLine("Activity", "Finalize done. Transaction ID: " + info.TransactionId);
    }
}

// iOS
RGLDocReader.Shared.FinalizePackageWithCompletion((action, info, error) => {
    if (error != null)
        Console.WriteLine("Finalize failed. Error: " + error.Description);
    else if (action == RGLDocReaderAction.Complete)
        Console.WriteLine("Finalize done. Transaction ID: " + info.TransactionId);
});

Step 4: Sending Transaction ID to Backend

Send the transaction ID to your backend to prepare the request that will be sent to the Regula Web Service for reprocessing later.

Step 5: Reprocessing on Web Service

Now that you have the encrypted results, you can start making up the request to do reprocessing on the Regula Web Service.

The request body should include at least the scenario and other settings depending on your needs:

{
    "processParam": {
        "scenario": "FullAuth"
    }
}

Send the request to Regula Web Service to the following endpoint:

POST /api/v2/transaction/{transaction_id}/process

where {transaction_id} is what to get in Step 3.

Request example using cURL

curl --request POST \
--url "http://localhost:8088/api/v2/transaction/76fa69d1-5ba6-4aa0-8dd5-477c8f81df91/process" \
--header "Content-Type: application/json" \
--data '{ "processParam": { "scenario": "FullAuth" } }'

Info

Please note that the request demonstrated above doesn't contain any data, so you won't receive any results from the Regula Web Service if you run it.

The request demonstrates only the structure that should be followed.

During the reprocessing, the Regula Web Service fetches data from Storage and Database.

This marks the final step of Complete Server-Side Verification. Once the reprocessing is completed, you can then determine whether you should trust the mobile results or not.

Congrats! 🎉

You've successfully set up the Complete Server-Side Verification!

Examples

See the sample projects, demonstrating how to set up the Complete Server-Side Verification on the Mobile side:

Next Steps