mDL Server-Side Verification
The Mobile Driver's License Data Server-Side Verification flow is similar to the Server-Side Verification used for the physical ID documents, but it applies to mDL.
- Sequence Diagram
- Preparation
- Step 1: mDL Reading
- Step 2: Finalize Package
- Step 3: Reprocessing on Web Service
- Step 4: Retrieve Processed Data
- Examples
Sequence Diagram
See the sequence diagram of the entire mDL document processing (click to enlarge).
Review the process 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.instance.processParams.backendProcessingConfig = new BackendProcessingConfig("https://api.regulaforensics.com")
// Android
DocumentReader.Instance().ProcessParams().BackendProcessingConfig = new BackendProcessingConfig("https://api.regulaforensics.com");
// iOS
RGLBackendProcessingConfig backendProcessingConfig = new()
{
Url = "https://api.regulaforensics.com"
};
RGLDocReader.Shared.ProcessParams.BackendProcessingConfig = backendProcessingConfig;
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) { })
For mDL 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 request 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" }
var backendProcessingConfig = new BackendProcessingConfig("https://api.regulaforensics.com", { httpHeaders: httpHeaders })
DocumentReader.instance.processParams.backendProcessingConfig = backendProcessingConfig
/// 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 NSMutableDictionary
{
["header1"] = new NSString("key1"),
["header2"] = new NSString("key1")
};
RGLBackendProcessingConfig backendProcessingConfig = new()
{
Url = "https://api.regulaforensics.com",
HttpHeaders = httpHeaders
};
RGLDocReader.Shared.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) { })
Step 1: mDL Reading
The first step is the mDL reading that is performed on the mobile side (click image to enlarge).
To start the processing, follow the mDL Processing guide.
While reading is performed on the mobile via NFC or Bluetooth, Regula Web Service is engaged in generating session keys and challenges, which are subsequently saved to the Data Store (comprising Storage and Database).
For complete details about the backend configuration, see the Web Service Complete Server-Side Verification page.
Step 2: 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 (click image to enlarge).
During transmission, the Data Store saves transaction info, metadata, results, and other related information.
Once the package is sent to Regula Web Service, you need to handle the transactionId that is returned in the callback and prepare it for sending to your service. See the code samples below.
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!);
}
var [action, info, error] = await DocumentReader.instance.finalizePackage()
if (error) {
console.log("Finalize failed. Error: " + error.message)
} else if (action == DocReaderAction.COMPLETE && info) {
console.log("Finalize done. Transaction ID: " + info.transactionId)
}
// Android
DocumentReader.Instance().FinalizePackage(this);
public void OnFinalizePackage(int action, TransactionInfo info, DocumentReaderException error)
{
if (error != null)
{
Console.WriteLine("Finalize failed. Error: " + error.Message);
return;
}
if (action == DocReaderAction.Complete && info != null)
{
Console.WriteLine("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);
});
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) { })
The last step of finalizing the data package is to send the obtained transactionId to your backend (Customer Service). Retain the transactionId on your Customer Service to request Regula Web Service for reprocessing later.
Step 3: Reprocessing on Web Service
After you receive the encrypted results, prepare the reprocessing request to Regula Web Service (click image to enlarge).
The request body should include at least the mDL processing scenario and other settings depending on your needs:
{
"processParam": {
"scenario": "mDL"
}
}
Send the request to Regula Web Service to the following endpoint:
POST /api/v2/transaction/{transaction_id}/process
where {transaction_id} is the value returned in the Step 2.
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": "mDL" } }'
Note
The request above demonstrates only the structure that should be followed, but doesn't contain any data, so you won't receive any results from the Regula Web Service if you run it.
Regula Web Service fetches raw data from the Data Store, processes it and then saves the reprocessed results back. This step may be time-consuming, so the results of the document's reprocessing are not returned in the same HTTP response. To get the processed data, follow the next Step 4.
Step 4: Retrieve Processed Data
After the data has been processed on the Regula Web Service side and saved to the Data Store, you can retrieve the results (click image to enlarge).
Send the request to Regula Web Service to the following endpoint:
GET /api/v2/transaction/{transaction_id}/results
where {transaction_id} is the value returned in the Step 2 and used in the reprocessing request, see the Step 3.
Request example using cURL
curl --request GET \
--url "http://localhost:8088/api/v2/transaction/76fa69d1-5ba6-4aa0-8dd5-477c8f81df91/results" \
--header "Content-Type: application/json"
This marks the final step of the mDL Server-Side Verification. Once the server-side reprocessing is completed, you can then handle the results appropriately and determine whether you should trust the Mobile SDK outcome.
Examples
See the sample projects that demonstrate how to set up the Complete Server-Side Verification on the Mobile side: