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