Skip to main content
Version: v0.13

Transfer Manager

The TransferManager handles the data transfer phase of the mDoc protocol, managing the exchange of device requests and responses between holder and verifier.

Obtaining the Transfer Manager​

The transfer manager is obtained by starting an engagement:

val engagementManager = session.component.mdocEngagementManager
val engagement = engagementManager.activeEngagement.value

// Start engagement and get transfer manager
val transferManager: TransferManager = engagement!!.start()

// Or using Try interface for result-based error handling
val result = engagement.tryOps().start()

when (result) {
is IdkResult.Success -> {
val transferManager = result.value
// Use transfer manager
}
is IdkResult.Failure -> {
val error = result.error
// Handle error
}
}

Receiving Device Requests​

Receive and process the verifier's request:

// Receive the device request from verifier
val deviceRequest: DeviceRequest = transferManager.receiveDeviceRequest()

// Examine what's being requested
for (docRequest in deviceRequest.docRequests) {
println("Document type: ${docRequest.docType}")

// Get requested namespaces and elements
val namespaces = docRequest.itemsRequest.nameSpaces
for ((namespace, elements) in namespaces) {
println(" Namespace: $namespace")
for ((elementId, intentToRetain) in elements) {
println(" - $elementId (retain: $intentToRetain)")
}
}
}

Creating Device Responses​

Create a response with the requested data:

// Create response using a document provider
val deviceResponse = transferManager.createResponse(
deviceRequest = deviceRequest,
documentProvider = documentProvider
)

// The response contains signed documents
println("Response has ${deviceResponse.documents?.size ?: 0} documents")

Sending Responses​

Send the response to the verifier:

// Send the device response
transferManager.sendDeviceResponse(deviceResponse)

Complete Transfer Flow​

Here's a complete holder-side transfer flow:

class MdocTransferHandler(
private val engagementManager: MdocEngagementManager,
private val documentProvider: DocumentProvider
) {
suspend fun handleTransfer(engagement: EngagementInstance) {
try {
// 1. Start the transfer
val transferManager = engagement.start()

// 2. Receive device request
val deviceRequest = transferManager.receiveDeviceRequest()

// 3. Show consent UI to user
val userApproved = showConsentDialog(deviceRequest)

if (userApproved) {
// 4. Create response with user-approved data
val deviceResponse = transferManager.createResponse(
deviceRequest = deviceRequest,
documentProvider = documentProvider
)

// 5. Send response to verifier
transferManager.sendDeviceResponse(deviceResponse)
} else {
// User declined - send error response
val errorResponse = DeviceResponse.Builder()
.withStatus(DeviceResponseStatus(20u)) // User cancelled
.build()
transferManager.sendDeviceResponse(errorResponse)
}
} catch (e: Exception) {
handleError(e)
} finally {
// Clean up engagement
engagementManager.closeEngagementByInstance(engagement)
}
}
}

Transfer Instance Tracking​

The engagement manager tracks active transfer instances:

// Track all active transfers
lifecycleScope.launch {
engagementManager.transferInstances.collect { transfers: Map<Uuid, TransferInstance> ->
for ((id, transfer) in transfers) {
println("Active transfer: $id")
}
}
}

DeviceResponse Builder​

Build custom device responses:

// Build a device response manually
val response = DeviceResponse.Builder()
.withVersion("1.0")
.withStatus(DeviceResponseStatus(0u)) // Success
.withDocuments(listOf(document))
.build()

// Build an error response
val errorResponse = DeviceResponse.Builder()
.withStatus(DeviceResponseStatus(10u)) // General error
.build()

Response Status Codes​

Standard device response status codes:

CodeMeaning
0Success
10General error
11CBOR decoding error
20User cancelled

Transfer Lifecycle​

The transfer phase follows this sequence:

mDoc Transfer Lifecycle

Best Practices​

When implementing transfer handling:

  • Always get user consent: Never send data without explicit user approval
  • Use the document provider: Let the IDK handle credential signing
  • Handle errors gracefully: Network interruptions and timeouts can occur during transfer
  • Clean up after transfer: Close the engagement when complete
  • Send appropriate status codes: Use correct status codes for error responses