Credential Handler API (CHAPI) implementation for TrustWeave.
Overview
CHAPI is a browser-based API that enables credential wallet interactions through the browser’s credential management system. It provides a standardized way for web applications to interact with credential wallets.
importorg.trustweave.credential.exchange.*importorg.trustweave.credential.exchange.request.ExchangeRequestimportorg.trustweave.credential.exchange.result.ExchangeResultimportorg.trustweave.credential.identifiers.*importorg.trustweave.did.identifiers.DidvalofferResult=exchangeService.offer(ExchangeRequest.Offer(protocolName="chapi".requireExchangeProtocolName(),issuerDid=Did("did:key:issuer"),holderDid=Did("did:key:holder"),credentialPreview=CredentialPreview(attributes=listOf(CredentialAttribute("name","Alice"),CredentialAttribute("email","alice@example.com"))),options=ExchangeOptions.Empty))valoffer=when(offerResult){isExchangeResult.Success->offerResult.valueelse->throwIllegalStateException("Offer failed: $offerResult")}// The offer contains a CHAPI message that can be used in the browservalchapiMessage=offer.messageEnvelope.messageDataasJsonObject
Browser Integration
In a browser environment, use the CHAPI messages with the Credential Handler API:
1
2
3
4
5
6
7
8
9
10
11
12
// Store credential offerconstoffer={"@context":["https://www.w3.org/2018/credentials/v1","https://w3id.org/credential-handler/v1"],"type":["VerifiableCredential","CredentialOffer"],// ... offer data from offer.offerData};navigator.credentials.store(newCredential({id:"credential-offer",type:"web",data:offer}));
importorg.trustweave.credential.model.vc.VerifiableCredentialimportorg.trustweave.credential.model.vc.CredentialSubjectimportorg.trustweave.credential.model.vc.Issuerimportorg.trustweave.credential.model.CredentialTypeimportorg.trustweave.core.identifiers.Iriimportkotlinx.serialization.json.JsonPrimitiveimportkotlinx.datetime.Clockvalcredential=VerifiableCredential(type=listOf(CredentialType.fromString("VerifiableCredential"),CredentialType.fromString("PersonCredential")),issuer=Issuer.IriIssuer(Iri("did:key:issuer")),issuanceDate=Clock.System.now(),credentialSubject=CredentialSubject(id=Did("did:key:holder"),claims=mapOf("name"toJsonPrimitive("Alice"),"email"toJsonPrimitive("alice@example.com"))))valissueResult=exchangeService.issue(ExchangeRequest.Issue(protocolName="chapi".requireExchangeProtocolName(),issuerDid=Did("did:key:issuer"),holderDid=Did("did:key:holder"),credential=credential,requestId=RequestId("request-id"),options=ExchangeOptions.Empty))valissue=when(issueResult){isExchangeResult.Success->issueResult.valueelse->throwIllegalStateException("Issue failed: $issueResult")}// The issue result contains a CHAPI message for browser storagevalchapiMessage=issue.messageEnvelope.messageDataasJsonObject
importorg.trustweave.credential.exchange.request.ProofExchangeRequestimportorg.trustweave.credential.exchange.request.ProofRequestimportorg.trustweave.credential.exchange.request.AttributeRequestimportorg.trustweave.credential.exchange.request.AttributeRestrictionimportorg.trustweave.credential.exchange.result.ExchangeResultvalproofRequestResult=exchangeService.requestProof(ProofExchangeRequest.Request(protocolName="chapi".requireExchangeProtocolName(),verifierDid=Did("did:key:verifier"),proverDid=Did("did:key:prover"),proofRequest=ProofRequest(name="Proof of Identity",requestedAttributes=mapOf("name"toAttributeRequest(name="name",restrictions=listOf(AttributeRestriction(issuerDid=Did("did:key:issuer")))))),options=ExchangeOptions.Empty))valproofRequest=when(proofRequestResult){isExchangeResult.Success->proofRequestResult.valueelse->throwIllegalStateException("Proof request failed: $proofRequestResult")}// The proof request contains a CHAPI message for browser usevalchapiMessage=proofRequest.messageEnvelope.messageDataasJsonObject
// Request proofconstproofRequest={"@context":["https://www.w3.org/2018/credentials/v1","https://w3id.org/credential-handler/v1"],"type":["VerifiablePresentationRequest"],// ... proof request data from proofRequest.requestData};navigator.credentials.get({publicKey:{challenge:newUint8Array(32),rpId:window.location.hostname,userVerification:"preferred"},web:{data:proofRequest}}).then(credential=>{// Handle the presentation responseconstpresentation=credential.data;});
importorg.trustweave.credential.model.vc.VerifiablePresentationimportorg.trustweave.credential.model.CredentialTypevalpresentation=VerifiablePresentation(type=listOf(CredentialType.fromString("VerifiablePresentation")),verifier=Did("did:key:verifier"),verifiableCredential=listOf(credential))valpresentationResult=exchangeService.presentProof(ProofExchangeRequest.Presentation(protocolName="chapi".requireExchangeProtocolName(),proverDid=Did("did:key:prover"),verifierDid=Did("did:key:verifier"),presentation=presentation,requestId=proofRequest.requestId,options=ExchangeOptions.Empty))valpresentationResponse=when(presentationResult){isExchangeResult.Success->presentationResult.valueelse->throwIllegalStateException("Presentation failed: $presentationResult")}// The presentation result contains a CHAPI messagevalchapiMessage=presentationResponse.messageEnvelope.messageDataasJsonObject
CHAPI Flow
Credential Offer: Issuer creates a CHAPI-compatible offer message
Browser Storage: Offer is stored using navigator.credentials.store()
Wallet Interaction: Wallet processes the offer and stores the credential
Proof Request: Verifier creates a CHAPI-compatible proof request
Browser Retrieval: Request is retrieved using navigator.credentials.get()
Proof Presentation: Wallet presents proof using CHAPI message format
Message Format
CHAPI messages follow the W3C Credential Handler API specification: