Credential Exchange Protocols - Examples

Complete code examples for credential exchange protocols.

Table of Contents

  1. Basic Examples
  2. Complete Workflows
  3. Error Handling Examples
  4. Protocol Switching Examples
  5. Advanced Examples

Basic Examples

Example 1: Simple Credential Offer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import com.trustweave.credential.exchange.*
import com.trustweave.credential.didcomm.exchange.DidCommExchangeProtocol
import com.trustweave.credential.didcomm.DidCommFactory
import com.trustweave.kms.KeyManagementService
import com.trustweave.testkit.InMemoryKeyManagementService
import com.trustweave.did.DidDocument
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    // Setup
    val kms: KeyManagementService = InMemoryKeyManagementService()
    val resolveDid: suspend (String) -> DidDocument? = { did ->
        DidDocument(id = did, verificationMethod = emptyList())
    }

    val registry = CredentialExchangeProtocolRegistry()
    val didCommService = DidCommFactory.createInMemoryService(kms, resolveDid)
    registry.register(DidCommExchangeProtocol(didCommService))

    // Create offer
    val offer = registry.offerCredential(
        protocolName = "didcomm",
        request = CredentialOfferRequest(
            issuerDid = "did:key:issuer",
            holderDid = "did:key:holder",
            credentialPreview = CredentialPreview(
                attributes = listOf(
                    CredentialAttribute("name", "Alice")
                )
            ),
            options = mapOf(
                "fromKeyId" to "did:key:issuer#key-1",
                "toKeyId" to "did:key:holder#key-1"
            )
        )
    )

    println("Offer ID: ${offer.offerId}")
}

Example 2: Complete Credential Exchange

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import com.trustweave.credential.exchange.*
import com.trustweave.credential.models.VerifiableCredential
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put

fun main() = runBlocking {
    // Setup (same as Example 1)
    val registry = setupRegistry()

    val issuerDid = "did:key:issuer"
    val holderDid = "did:key:holder"

    // Step 1: Offer
    val offer = registry.offerCredential(
        protocolName = "didcomm",
        request = CredentialOfferRequest(
            issuerDid = issuerDid,
            holderDid = holderDid,
            credentialPreview = CredentialPreview(
                attributes = listOf(
                    CredentialAttribute("name", "Alice"),
                    CredentialAttribute("email", "alice@example.com")
                )
            ),
            options = mapOf(
                "fromKeyId" to "$issuerDid#key-1",
                "toKeyId" to "$holderDid#key-1"
            )
        )
    )

    // Step 2: Request
    val request = registry.requestCredential(
        protocolName = "didcomm",
        request = CredentialRequestRequest(
            holderDid = holderDid,
            issuerDid = issuerDid,
            offerId = offer.offerId,
            options = mapOf(
                "fromKeyId" to "$holderDid#key-1",
                "toKeyId" to "$issuerDid#key-1"
            )
        )
    )

    // Step 3: Issue
    val credential = VerifiableCredential(
        type = listOf("VerifiableCredential", "PersonCredential"),
        issuer = issuerDid,
        credentialSubject = buildJsonObject {
            put("id", holderDid)
            put("name", "Alice")
            put("email", "alice@example.com")
        },
        issuanceDate = java.time.Instant.now().toString()
    )

    val issue = registry.issueCredential(
        protocolName = "didcomm",
        request = CredentialIssueRequest(
            issuerDid = issuerDid,
            holderDid = holderDid,
            credential = credential,
            requestId = request.requestId,
            options = mapOf(
                "fromKeyId" to "$issuerDid#key-1",
                "toKeyId" to "$holderDid#key-1"
            )
        )
    )

    println("✅ Credential issued: ${issue.credential.id}")
}

Complete Workflows

Example 3: Proof Request and Presentation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
fun proofWorkflow() = runBlocking {
    val registry = setupRegistry()

    val verifierDid = "did:key:verifier"
    val proverDid = "did:key:prover"

    // Step 1: Request proof
    val proofRequest = registry.requestProof(
        protocolName = "didcomm",
        request = ProofRequestRequest(
            verifierDid = verifierDid,
            proverDid = proverDid,
            name = "Age Verification",
            requestedAttributes = mapOf(
                "name" to RequestedAttribute(
                    name = "name",
                    restrictions = listOf(
                        AttributeRestriction(issuerDid = "did:key:issuer")
                    )
                )
            ),
            requestedPredicates = mapOf(
                "age_verification" to RequestedPredicate(
                    name = "age",
                    pType = ">=",
                    pValue = 18
                )
            ),
            options = mapOf(
                "fromKeyId" to "$verifierDid#key-1",
                "toKeyId" to "$proverDid#key-1"
            )
        )
    )

    // Step 2: Create presentation (prover side)
    val presentation = createPresentation(proofRequest)

    // Step 3: Present proof
    val presentationResponse = registry.presentProof(
        protocolName = "didcomm",
        request = ProofPresentationRequest(
            proverDid = proverDid,
            verifierDid = verifierDid,
            presentation = presentation,
            requestId = proofRequest.requestId,
            options = mapOf(
                "fromKeyId" to "$proverDid#key-1",
                "toKeyId" to "$verifierDid#key-1"
            )
        )
    )

    println("✅ Proof presented: ${presentationResponse.presentationId}")
}

Error Handling Examples

Example 4: Error Handling with Fallback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
suspend fun offerWithErrorHandling(
    registry: CredentialExchangeProtocolRegistry,
    request: CredentialOfferRequest
): CredentialOfferResponse? {
    return try {
        // Try preferred protocol
        registry.offerCredential("didcomm", request)
    } catch (e: IllegalArgumentException) {
        when {
            e.message?.contains("not registered") == true -> {
                println("Protocol not registered. Trying fallback...")
                // Try fallback
                try {
                    registry.offerCredential("oidc4vci", request)
                } catch (e2: Exception) {
                    println("Fallback failed: ${e2.message}")
                    null
                }
            }
            e.message?.contains("Missing required option") == true -> {
                println("Missing required option: ${e.message}")
                null
            }
            else -> {
                println("Invalid argument: ${e.message}")
                null
            }
        }
    } catch (e: UnsupportedOperationException) {
        println("Operation not supported: ${e.message}")
        null
    } catch (e: Exception) {
        println("Unexpected error: ${e.message}")
        null
    }
}

Example 5: Validation Before Operation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
fun validateAndOffer(
    registry: CredentialExchangeProtocolRegistry,
    request: CredentialOfferRequest
): Result<CredentialOfferResponse> {
    // Validate DIDs
    if (!isValidDid(request.issuerDid)) {
        return Result.failure(IllegalArgumentException("Invalid issuer DID"))
    }
    if (!isValidDid(request.holderDid)) {
        return Result.failure(IllegalArgumentException("Invalid holder DID"))
    }

    // Validate preview
    if (request.credentialPreview.attributes.isEmpty()) {
        return Result.failure(IllegalArgumentException("Preview must have attributes"))
    }

    // Validate protocol options
    if (!registry.isRegistered("didcomm")) {
        return Result.failure(IllegalStateException("Protocol not registered"))
    }

    // All valid, proceed
    return runBlocking {
        try {
            Result.success(registry.offerCredential("didcomm", request))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
}

fun isValidDid(did: String): Boolean {
    return did.matches(Regex("^did:[a-z0-9]+:.+$"))
}

Protocol Switching Examples

Example 6: Switch Protocol Based on Context

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
suspend fun offerWithProtocolSelection(
    registry: CredentialExchangeProtocolRegistry,
    request: CredentialOfferRequest,
    context: ExchangeContext
): CredentialOfferResponse {
    val protocol = when {
        context.needsEncryption -> "didcomm"
        context.isWebBased -> "oidc4vci"
        context.isBrowser -> "chapi"
        else -> "didcomm"  // Default
    }

    return registry.offerCredential(protocol, request)
}

data class ExchangeContext(
    val needsEncryption: Boolean = false,
    val isWebBased: Boolean = false,
    val isBrowser: Boolean = false
)

Advanced Examples

Example 7: Multiple Protocols

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
fun multipleProtocolsExample() = runBlocking {
    val registry = CredentialExchangeProtocolRegistry()

    // Register multiple protocols
    val kms = InMemoryKeyManagementService()
    val resolveDid: suspend (String) -> DidDocument? = { did ->
        DidDocument(id = did, verificationMethod = emptyList())
    }

    // DIDComm
    val didCommService = DidCommFactory.createInMemoryService(kms, resolveDid)
    registry.register(DidCommExchangeProtocol(didCommService))

    // OIDC4VCI
    val oidc4vciService = Oidc4VciService(
        credentialIssuerUrl = "https://issuer.example.com",
        kms = kms
    )
    registry.register(Oidc4VciExchangeProtocol(oidc4vciService))

    // CHAPI
    val chapiService = ChapiService()
    registry.register(ChapiExchangeProtocol(chapiService))

    println("Registered protocols: ${registry.getAllProtocolNames()}")
    // Output: [didcomm, oidc4vci, chapi]

    // Use any protocol
    val didCommOffer = registry.offerCredential("didcomm", request)
    val oidcOffer = registry.offerCredential("oidc4vci", request)
    val chapiOffer = registry.offerCredential("chapi", request)
}

Example 8: Protocol Fallback Chain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
suspend fun offerWithFallbackChain(
    registry: CredentialExchangeProtocolRegistry,
    request: CredentialOfferRequest
): CredentialOfferResponse {
    val protocols = listOf("didcomm", "oidc4vci", "chapi")

    for (protocol in protocols) {
        if (registry.isRegistered(protocol)) {
            try {
                return registry.offerCredential(protocol, request)
            } catch (e: Exception) {
                println("Protocol $protocol failed: ${e.message}")
                continue
            }
        }
    }

    throw IllegalStateException("All protocols failed")
}

Helper Functions

Setup Registry

1
2
3
4
5
6
7
8
9
10
11
12
fun setupRegistry(): CredentialExchangeProtocolRegistry {
    val kms: KeyManagementService = InMemoryKeyManagementService()
    val resolveDid: suspend (String) -> DidDocument? = { did ->
        DidDocument(id = did, verificationMethod = emptyList())
    }

    val registry = CredentialExchangeProtocolRegistry()
    val didCommService = DidCommFactory.createInMemoryService(kms, resolveDid)
    registry.register(DidCommExchangeProtocol(didCommService))

    return registry
}