Exchange Credentials with Multiple Protocols
This guide shows you how to use TrustWeave’s unified API to exchange credentials using any protocol (DIDComm, OIDC4VCI, CHAPI) with the same code. Switch protocols at runtime without changing your application logic.
Prerequisites
Before you begin, ensure you have:
- ✅ TrustWeave dependencies added to your project
- ✅ Understanding of credential issuance and verification
- ✅ Basic knowledge of credential exchange protocols
- ✅ Protocol-specific dependencies (optional, for specific protocols)
Expected Outcome
After completing this guide, you will have:
- ✅ Registered multiple credential exchange protocols
- ✅ Exchanged credentials using different protocols with the same API
- ✅ Understood when to use each protocol
- ✅ Implemented protocol switching at runtime
Quick Example
Here’s a complete example showing unified API for all 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
import com.trustweave.credential.exchange.*
import com.trustweave.credential.didcomm.exchange.DidCommExchangeProtocol
import com.trustweave.credential.oidc4vci.exchange.Oidc4VciExchangeProtocol
import com.trustweave.credential.chapi.exchange.ChapiExchangeProtocol
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
// Step 1: Create registry
val registry = CredentialExchangeProtocolRegistry()
// Step 2: Register protocols
registry.register(DidCommExchangeProtocol(didCommService))
registry.register(Oidc4VciExchangeProtocol(oidc4vciService))
registry.register(ChapiExchangeProtocol(chapiService))
// Step 3: Create offer request (same for all protocols)
val request = CredentialOfferRequest(
issuerDid = "did:key:issuer",
holderDid = "did:key:holder",
credentialPreview = CredentialPreview(...)
)
// Step 4: Use any protocol with identical API
val didCommOffer = registry.offerCredential("didcomm", request)
val oidcOffer = registry.offerCredential("oidc4vci", request)
val chapiOffer = registry.offerCredential("chapi", request)
println("✅ Created offers with all protocols")
}
Expected Output:
1
✅ Created offers with all protocols
Step-by-Step Guide
Step 1: Set Up Protocol Services
First, create the protocol-specific services:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import com.trustweave.credential.didcomm.DidCommFactory
import com.trustweave.credential.oidc4vci.Oidc4VciService
import com.trustweave.credential.chapi.ChapiService
import com.trustweave.kms.KeyManagementService
// Create KMS for cryptographic operations
val kms: KeyManagementService = InMemoryKeyManagementService()
// Create DID resolver function
val resolveDid: suspend (String) -> DidDocument? = { did ->
// Your DID resolution logic
null
}
// Create protocol services
val didCommService = DidCommFactory.createInMemoryService(kms, resolveDid)
val oidc4vciService = Oidc4VciService(...)
val chapiService = ChapiService(...)
What this does:
- ✅ Sets up key management for encryption/signing
- ✅ Configures DID resolution
- ✅ Creates protocol-specific services
Expected Result: Protocol services ready for registration.
Step 2: Create and Register Protocols
Create the registry and register all protocols:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import com.trustweave.credential.exchange.CredentialExchangeProtocolRegistry
import com.trustweave.credential.didcomm.exchange.DidCommExchangeProtocol
import com.trustweave.credential.oidc4vci.exchange.Oidc4VciExchangeProtocol
import com.trustweave.credential.chapi.exchange.ChapiExchangeProtocol
val registry = CredentialExchangeProtocolRegistry()
// Register DIDComm
registry.register(DidCommExchangeProtocol(didCommService))
// Register OIDC4VCI
registry.register(Oidc4VciExchangeProtocol(oidc4vciService))
// Register CHAPI
registry.register(ChapiExchangeProtocol(chapiService))
What this does:
- ✅ Creates a unified registry
- ✅ Registers all available protocols
- ✅ Makes protocols available via unified API
Expected Result: Registry with all protocols registered.
Step 3: Create Credential Offer Request
Create a request that works with all protocols:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import com.trustweave.credential.exchange.CredentialOfferRequest
import com.trustweave.credential.exchange.CredentialPreview
val request = CredentialOfferRequest(
issuerDid = "did:key:issuer",
holderDid = "did:key:holder",
credentialPreview = CredentialPreview(
type = listOf("VerifiableCredential", "EducationCredential"),
claims = mapOf(
"degree" to "Bachelor of Science",
"university" to "Example University"
)
)
)
What this does:
- ✅ Defines issuer and holder DIDs
- ✅ Creates credential preview
- ✅ Works with all protocols
Expected Result: A request object ready for any protocol.
Step 4: Offer Credential with Any Protocol
Use the same API for all protocols:
1
2
3
4
5
6
7
8
// DIDComm
val didCommOffer = registry.offerCredential("didcomm", request)
// OIDC4VCI
val oidcOffer = registry.offerCredential("oidc4vci", request)
// CHAPI
val chapiOffer = registry.offerCredential("chapi", request)
What this does:
- ✅ Creates offers using different protocols
- ✅ Uses identical API for all
- ✅ Returns protocol-specific responses
Expected Result: Credential offers created with all protocols.
Workflow: Multi-Protocol Credential Exchange
The following swimlane diagram shows how different components interact during credential exchange:
sequenceDiagram
participant App as Application
participant Registry as Protocol Registry
participant DIDComm as DIDComm Protocol
participant OIDC4VCI as OIDC4VCI Protocol
participant CHAPI as CHAPI Protocol
participant Holder as Credential Holder
Note over App,Holder: Step 1: Protocol Registration
App->>Registry: register(DIDComm)
App->>Registry: register(OIDC4VCI)
App->>Registry: register(CHAPI)
Note over App,Holder: Step 2: Create Offer Request
App->>App: Create CredentialOfferRequest
Note over App,Holder: Step 3: Offer Credential (Protocol Selection)
App->>Registry: offerCredential("didcomm", request)
Registry->>DIDComm: offerCredential(request)
DIDComm->>DIDComm: Encrypt message
DIDComm-->>Registry: Encrypted offer
Registry-->>App: DIDComm offer response
App->>Registry: offerCredential("oidc4vci", request)
Registry->>OIDC4VCI: offerCredential(request)
OIDC4VCI->>OIDC4VCI: Create OAuth flow
OIDC4VCI-->>Registry: OAuth offer
Registry-->>App: OIDC4VCI offer response
App->>Registry: offerCredential("chapi", request)
Registry->>CHAPI: offerCredential(request)
CHAPI->>CHAPI: Create browser message
CHAPI-->>Registry: CHAPI offer
Registry-->>App: CHAPI offer response
Note over App,Holder: Step 4: Holder Receives Offer
App->>Holder: Send offer (protocol-specific format)
Holder->>Holder: Process offer
Holder-->>App: Accept/Reject
Protocol Comparison
When to Use Each Protocol
| Protocol | Best For | Encryption | Transport |
|---|---|---|---|
| DIDComm | Peer-to-peer, high security | ✅ End-to-end (ECDH-1PU) | Direct messaging |
| OIDC4VCI | Web-based, OAuth integration | Via HTTPS | HTTP/REST |
| CHAPI | Browser wallet interactions | Browser security | Browser API |
Decision Tree
1
2
3
4
5
6
7
8
9
Need credential exchange?
├─ Need peer-to-peer encryption?
│ └─ Yes → Use DIDComm
└─ No
├─ Web-based OAuth integration?
│ └─ Yes → Use OIDC4VCI
└─ No
└─ Browser-based wallet?
└─ Yes → Use CHAPI
Common Patterns
Pattern 1: Protocol Selection at Runtime
Select protocol based on holder capabilities:
1
2
3
4
5
6
7
8
9
10
11
12
fun selectProtocol(holderCapabilities: HolderCapabilities): String {
return when {
holderCapabilities.supportsDidComm -> "didcomm"
holderCapabilities.supportsOidc4vci -> "oidc4vci"
holderCapabilities.supportsChapi -> "chapi"
else -> "didcomm" // Default
}
}
// Use selected protocol
val protocol = selectProtocol(holderCapabilities)
val offer = registry.offerCredential(protocol, request)
Pattern 2: Multi-Protocol Support
Support multiple protocols and let holder choose:
1
2
3
4
5
6
7
8
9
10
11
12
// Create offers with all protocols
val offers = mapOf(
"didcomm" to registry.offerCredential("didcomm", request),
"oidc4vci" to registry.offerCredential("oidc4vci", request),
"chapi" to registry.offerCredential("chapi", request)
)
// Present options to holder
holder.selectProtocol(offers.keys) { selectedProtocol ->
val offer = offers[selectedProtocol]
// Continue with selected protocol
}
Pattern 3: Protocol Fallback
Try protocols in order of preference:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
suspend fun offerWithFallback(
request: CredentialOfferRequest,
preferredProtocols: List<String> = listOf("didcomm", "oidc4vci", "chapi")
): CredentialOfferResponse? {
for (protocol in preferredProtocols) {
try {
return registry.offerCredential(protocol, request)
} catch (e: ExchangeException.ProtocolNotRegistered) {
// Try next protocol
continue
} catch (e: Exception) {
// Protocol error, try next
continue
}
}
return null // All protocols failed
}
Complete Workflow Example
End-to-end credential exchange with protocol abstraction:
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
import com.trustweave.credential.exchange.*
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
// 1. Setup
val registry = CredentialExchangeProtocolRegistry()
registry.register(DidCommExchangeProtocol(didCommService))
registry.register(Oidc4VciExchangeProtocol(oidc4vciService))
// 2. Create offer request
val request = CredentialOfferRequest(
issuerDid = "did:key:issuer",
holderDid = "did:key:holder",
credentialPreview = CredentialPreview(...)
)
// 3. Offer credential (protocol selection)
val protocol = "didcomm" // or select dynamically
val offer = registry.offerCredential(protocol, request)
// 4. Holder requests credential
val credentialRequest = registry.requestCredential(
protocol,
RequestCredentialRequest(
offerId = offer.offerId,
holderDid = "did:key:holder"
)
)
// 5. Issue credential
val issuedCredential = registry.issueCredential(
protocol,
IssueCredentialRequest(
requestId = credentialRequest.requestId,
credential = credential,
issuerDid = "did:key:issuer"
)
)
println("✅ Credential issued via $protocol")
}
Error Handling
Handle protocol-specific errors:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import com.trustweave.credential.exchange.exception.ExchangeException
try {
val offer = registry.offerCredential("didcomm", request)
} catch (error: ExchangeException) {
when (error) {
is ExchangeException.ProtocolNotRegistered -> {
println("Protocol not registered: ${error.protocolName}")
println("Available: ${registry.getRegisteredProtocols()}")
}
is ExchangeException.MissingRequiredOption -> {
println("Missing option: ${error.option}")
}
else -> {
println("Exchange error: ${error.message}")
}
}
}
Benefits of Unified API
Before (Without TrustWeave)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Each protocol requires completely different code
val didCommOffer = didCommService.createOffer(
from = issuerDid,
to = holderDid,
credentialPreview = preview,
encryptionKey = keyAgreementKey,
signingKey = signingKey
)
val oidc4vciOffer = oidc4vciClient.requestCredentialOffer(
issuerUrl = issuerEndpoint,
clientId = oauthClientId,
redirectUri = callbackUrl,
scope = "credential_offer"
)
val chapiOffer = chapiHandler.createOfferMessage(
credentialManifest = manifest,
wallet = browserWallet,
options = chapiOptions
)
Problems:
- ❌ Different APIs for each protocol
- ❌ Hard to switch protocols
- ❌ Code duplication
- ❌ Difficult to maintain
After (With TrustWeave)
1
2
3
4
// One API, any protocol
val didCommOffer = registry.offerCredential("didcomm", request)
val oidc4vciOffer = registry.offerCredential("oidc4vci", request)
val chapiOffer = registry.offerCredential("chapi", request)
Benefits:
- ✅ Same API for all protocols
- ✅ Easy protocol switching
- ✅ No code duplication
- ✅ Easy to maintain
Next Steps
Now that you’ve learned credential exchange, you can:
- Issue Credentials - Learn credential issuance details
- Verify Credentials - Verify exchanged credentials
- Configure TrustWeave - Full configuration options
- Protocol-Specific Guides - Deep dive into each protocol
Related Documentation
- Credential Exchange Protocols - Complete protocol documentation
- API Reference - Complete API documentation
- Core Concepts - Understanding protocol abstraction