diff --git a/docs/examples/ConnectionlessOffer.md b/docs/examples/ConnectionlessOffer.md index 66881bd69..4dbb02541 100644 --- a/docs/examples/ConnectionlessOffer.md +++ b/docs/examples/ConnectionlessOffer.md @@ -1,28 +1,153 @@ # Edge SDK Connectionless Credential Offer -## Flow -1. Obtain a Connectionless Credential Offer from an Issuer. +## User Flow -A Connectionless Credential Offer is an Out of Band Invitation with a Credential Offer Attachment. -This should be a URI with a single query parameter `_oob`, which is an encoded JSON. -It should look similar to: +```mermaid +sequenceDiagram + autonumber -``` -https://my.domain.com/path?_oob=eyJpZCI6ImY5NmUzNjk5LTU5MWMtNGFlNy1iNWU2LTZlZmU2ZDI2MjU1YiIsInR5cGUiOiJodHRwczovL2RpZGNvbW0ub3JnL291dC1vZi1iYW5kLzIuMC9pbnZpdGF0aW9uIiwiZnJvbSI6ImRpZDpwZWVyOjIuRXo2TFNmc0tNZTh2U1NXa1lkWkNwbjRZVmlQRVJmZEdBaGRMQUdIZ3gyTEdKd2ZtQS5WejZNa3B3MWtTYWJCTXprQTN2NTl0UUZuaDNGdGtLeTZ4TGhMeGQ5UzZCQW9hQmcyLlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW1oMGRIQTZMeTh4T1RJdU1UWTRMakV1TXpjNk9EQTRNQzlrYVdSamIyMXRJaXdpY2lJNlcxMHNJbUVpT2xzaVpHbGtZMjl0YlM5Mk1pSmRmWDAiLCJib2R5Ijp7ImdvYWxfY29kZSI6Imlzc3VlLXZjIiwiZ29hbCI6IlRlc3QgT09CIiwiYWNjZXB0IjpbImRpZGNvbW0vdjIiXX0sImNyZWF0ZWRfdGltZSI6MTcyNDg1MTEzOSwiZXhwaXJlc190aW1lIjo5OTI0ODUxNDM5LCJhdHRhY2htZW50cyI6W3siaWQiOiIwMGNkYzkwYy05YTk5LTRjZGEtODdmZS00ZjRiMjU5NTExMmEiLCJtZWRpYV90eXBlIjoiYXBwbGljYXRpb24vanNvbiIsImRhdGEiOnsianNvbiI6eyJpZCI6IjY1NWU5YTJjLTQ4ZWQtNDU5Yi1iM2RhLTZiMzY4NjY1NTU2NCIsInR5cGUiOiJodHRwczovL2RpZGNvbW0ub3JnL2lzc3VlLWNyZWRlbnRpYWwvMy4wL29mZmVyLWNyZWRlbnRpYWwiLCJib2R5Ijp7ImdvYWxfY29kZSI6Ik9mZmVyIENyZWRlbnRpYWwiLCJjcmVkZW50aWFsX3ByZXZpZXciOnsidHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvaXNzdWUtY3JlZGVudGlhbC8zLjAvY3JlZGVudGlhbC1jcmVkZW50aWFsIiwiYm9keSI6eyJhdHRyaWJ1dGVzIjpbeyJuYW1lIjoiZmFtaWx5TmFtZSIsInZhbHVlIjoiV29uZGVybGFuZCJ9XX19fSwiYXR0YWNobWVudHMiOlt7ImlkIjoiODQwNDY3OGItOWEzNi00OTg5LWFmMWQtMGY0NDUzNDdlMGUzIiwibWVkaWFfdHlwZSI6ImFwcGxpY2F0aW9uL2pzb24iLCJkYXRhIjp7Impzb24iOnsib3B0aW9ucyI6eyJjaGFsbGVuZ2UiOiJhZDBmNDNhZC04NTM4LTQxZDQtOWNiOC0yMDk2N2JjNjg1YmMiLCJkb21haW4iOiJkb21haW4ifSwicHJlc2VudGF0aW9uX2RlZmluaXRpb24iOnsiaWQiOiI3NDhlZmE1OC0yYmNlLTQ0MGQtOTIxZi0yNTIwYTg0NDY2NjMiLCJpbnB1dF9kZXNjcmlwdG9ycyI6W10sImZvcm1hdCI6eyJqd3QiOnsiYWxnIjpbIkVTMjU2SyJdLCJwcm9vZl90eXBlIjpbXX19fX19LCJmb3JtYXQiOiJwcmlzbS9qd3QifV0sInRoaWQiOiJmOTZlMzY5OS01OTFjLTRhZTctYjVlNi02ZWZlNmQyNjI1NWIiLCJmcm9tIjoiZGlkOnBlZXI6Mi5FejZMU2ZzS01lOHZTU1drWWRaQ3BuNFlWaVBFUmZkR0FoZExBR0hneDJMR0p3Zm1BLlZ6Nk1rcHcxa1NhYkJNemtBM3Y1OXRRRm5oM0Z0a0t5NnhMaEx4ZDlTNkJBb2FCZzIuU2V5SjBJam9pWkcwaUxDSnpJanA3SW5WeWFTSTZJbWgwZEhBNkx5OHhPVEl1TVRZNExqRXVNemM2T0RBNE1DOWthV1JqYjIxdElpd2ljaUk2VzEwc0ltRWlPbHNpWkdsa1kyOXRiUzkyTWlKZGZYMCJ9fX1dfQ== -``` + actor Peer + actor Admin + participant Issuer + Admin->> Issuer: Request a Connectionless Credential Offer Invitation + Issuer->> Admin: Connectionless Credential Offer Invitation -2. Ensure the validity of the Invitation with `Agent.parseInvitation` + Admin->>Peer: Connectionless Credential Offer Invitation (OOB) -`parseInvitation` decodes and validates the encoded Out of Band Invitation, plus attachments, returning an instance of `OutOfBandInvitation` on success. This OutOfBandInvitation will have a single Attachment for the Credential Offer. + Peer ->> Peer: Parse the OOB + + Peer ->> Issuer: Accept the Connectionless Credential Offer Invitation (OOB) + Issuer ->> Peer: Connectionless Credential Offer + Peer ->> Issuer: Accept the Connectionless Credential Offer + Issuer ->> Peer: Verifiable Credential -``` -const oob = await Agent.parseInvitation(rawOob); ``` -3. Use `Agent.acceptInvitation` to handle the OutOfBandInvitation appropriately. -In this case, with an attached Credential Offer, the Credential Offer Message will be stored in Pluto. +## Steps -``` -await Agent.acceptInvitation(oob) -``` +1. The `Admin` requests a connectionless credential offer invitation with the Issuer Agent + + ```bash + curl --location --request POST 'http://localhost:8000/cloud-agent/issue-credentials/credential-offers/invitation' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "claims": { + "emailAddress": "sampleEmail", + "familyName": "", + "dateOfIssuance": "2023-01-01T02:02:02Z", + "drivingLicenseID": "", + "drivingClass": 1 + }, + "goalCode": "issue-vc", + "goal": "Request issuance", + "credentialFormat": "JWT", + "issuingDID": [[publishedPrismDID]], + "automaticIssuance": true + }' + + ``` + +2. The Issuer sends back the `Connectionless Credential Offer Invitation` + - The response should like something like this: + + ```json + { + "recordId": "cf11283a-a9f5-4da0-ae65-be1e57a40710", + "thid": "b78d7221-0149-4c29-a42e-d8b8c4a9f57d", + "credentialFormat": "JWT", + "claims": { + "emailAddress": "sampleEmail", + "familyName": "", + "dateOfIssuance": "2023-01-01T02:02:02Z", + "drivingLicenseID": "", + "drivingClass": 1 + }, + "automaticIssuance": true, + "createdAt": "2024-10-15T01:31:55.303311774Z", + "role": "Issuer", + "protocolState": "InvitationGenerated", + "goalCode": "issue-vc", + "goal": "Request issuance", + "myDid": "did:peer:2.Ez6LS...", + "invitation": { + "id": "b78d7221-0149-4c29-a42e-d8b8c4a9f57d", + "type": "https://didcomm.org/out-of-band/2.0/invitation", + "from": "did:peer:2.Ez6LS...", + "invitationUrl": "https://my.domain.com/path?_oob=eyJpZCI6ImI3OGQ3MjIxLTAxNDktNGMyOS1hNDJlLWQ4YjhjNGE5ZjU3ZCIsInR5cGUiOiJodHRwczovL2RpZGNvbW0ub3JnL291dC1vZi1iYW5kLzIuMC9pbnZpdGF0aW9uIiwiZnJvbSI6ImRpZDpwZWVyOjIuRXo2TFNnbTg4eW9jTWFMNkQxM203NWdHcDNlUVRQMXZCbjZpRTRDR0d6UnNhTUtOUC5WejZNa3JVaG40UlBCdHRWRXNXWGI1MlFOOWZUOW1LYmV3ZzhRQnBBOE5GOEZxUm9CLlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW1oMGRIQTZMeTh4T1RJdU1UWTRMalk0TGpZeU9qZ3dNREF2Wkdsa1kyOXRiU0lzSW5JaU9sdGRMQ0poSWpwYkltUnBaR052YlcwdmRqSWlYWDE5IiwiYm9keSI6eyJnb2FsX2NvZGUiOiJpc3N1ZS12YyIsImdvYWwiOiJSZXF1ZXN0IGlzc3VhbmNlIiwiYWNjZXB0IjpbImRpZGNvbW0vdjIiXX0sImF0dGFjaG1lbnRzIjpbeyJpZCI6IjdiODM3ZTk3LTYzNzktNDJkNy1hNjY4LTA3OTAzYTFlYmIwNCIsIm1lZGlhX3R5cGUiOiJhcHBsaWNhdGlvbi9qc29uIiwiZGF0YSI6eyJqc29uIjp7ImlkIjoiN2VhMmJlZTMtODgxNC00YjhlLTllNGEtNWZhMDU1YWJiZDUwIiwidHlwZSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvaXNzdWUtY3JlZGVudGlhbC8zLjAvb2ZmZXItY3JlZGVudGlhbCIsImJvZHkiOnsiZ29hbF9jb2RlIjoiT2ZmZXIgQ3JlZGVudGlhbCIsImNyZWRlbnRpYWxfcHJldmlldyI6eyJ0eXBlIjoiaHR0cHM6Ly9kaWRjb21tLm9yZy9pc3N1ZS1jcmVkZW50aWFsLzMuMC9jcmVkZW50aWFsLWNyZWRlbnRpYWwiLCJib2R5Ijp7ImF0dHJpYnV0ZXMiOlt7Im5hbWUiOiJtZW1iZXJPZiIsInZhbHVlIjoiZXlKMGNtbGlaVWxrSWpvaUpYa3hhVkZWZEhWSk5uZEZNMFUyWmpneWNpODFPRTl1U1ZGcE4zTnROMWx4VVVFellrZHNWVUowWTI4OUxtTnNiMkZyWldRaUxDSjBjbWxpWlU1aGJXVWlPaUpYYUdGdVoyRnliMkVnVUdGd1lTQklZWEIxSW4wPSIsIm1lZGlhX3R5cGUiOiJhcHBsaWNhdGlvbi9qc29uIn0seyJuYW1lIjoicGVyc29uIiwidmFsdWUiOiJleUptZFd4c1RtRnRaU0k2SWtKbGJpQlVZV2x5WldFaUxDSmtZWFJsVDJaQ2FYSjBhQ0k2SWpFNU9EY3ZXRmd2V0ZnaWZRPT0iLCJtZWRpYV90eXBlIjoiYXBwbGljYXRpb24vanNvbiJ9XX19fSwiYXR0YWNobWVudHMiOlt7ImlkIjoiODJkMjI1NDctY2VmNS00YjkwLTg5MTQtOGQzY2M2ZGM2MjFlIiwibWVkaWFfdHlwZSI6ImFwcGxpY2F0aW9uL2pzb24iLCJkYXRhIjp7Impzb24iOnsib3B0aW9ucyI6eyJjaGFsbGVuZ2UiOiI5Y2Y3ZmE4OC05MGM5LTQyZTMtOTI1MC0xOGZjMzZhYjkzYWMiLCJkb21haW4iOiJkb21haW4ifSwicHJlc2VudGF0aW9uX2RlZmluaXRpb24iOnsiaWQiOiI5YTJhYjgyNi1iOWZjLTQ3ZjEtOGM0Mi1hMGFhMjI1ZjQwMzAiLCJpbnB1dF9kZXNjcmlwdG9ycyI6W10sImZvcm1hdCI6eyJqd3QiOnsiYWxnIjpbIkVTMjU2SyJdfX19fX0sImZvcm1hdCI6InByaXNtL2p3dCJ9XSwidGhpZCI6ImI3OGQ3MjIxLTAxNDktNGMyOS1hNDJlLWQ4YjhjNGE5ZjU3ZCIsImZyb20iOiJkaWQ6cGVlcjoyLkV6NkxTZ204OHlvY01hTDZEMTNtNzVnR3AzZVFUUDF2Qm42aUU0Q0dHelJzYU1LTlAuVno2TWtyVWhuNFJQQnR0VkVzV1hiNTJRTjlmVDltS2Jld2c4UUJwQThORjhGcVJvQi5TZXlKMElqb2laRzBpTENKeklqcDdJblZ5YVNJNkltaDBkSEE2THk4eE9USXVNVFk0TGpZNExqWXlPamd3TURBdlpHbGtZMjl0YlNJc0luSWlPbHRkTENKaElqcGJJbVJwWkdOdmJXMHZkaklpWFgxOSJ9fX1dLCJjcmVhdGVkX3RpbWUiOjE3Mjg5NTU5MTUsImV4cGlyZXNfdGltZSI6MTcyODk1NjIxNX0=" + }, + "metaRetries": 5 + } + ``` + +3. The `Admin` forwards the `invitationUrl` on to the `Peer` + +4. The `Peer` checks the validity of the `invitationUrl` using `agent.parseInvitation` + + ```typescript + const oob = await agent.parseInvitation(invitationUrl) + ``` + +5. The `Peer` accepts the Invitation using `agent.acceptInvitation` + + ```typescript + await agent.acceptInvitation(oob) + ``` + +6. The `Issuer` will then send back the credential offer. +7. The `Peer` listens for the credential offer response from the `Issuer`, and accepts it + + ```typescript + agent.addListener(SDK.ListenerKey.MESSAGE, async (newMessages: SDK.Domain.Message[]) => { + // newMessages can contain any didcomm message that is received, including + // Credential Offers, Issued credentials and Request Presentation Messages + const credentialOffers = newMessages.filter( + (message) => message.piuri === SDK.ProtocolType.DidcommOfferCredential + ) + + if (credentialOffers.length) { + for (const credentialOfferMessage of credentialOffers) { + try { + // Create credential offer object from the message + const credentialOffer = await SDK.OfferCredential.fromMessage(credentialOfferMessage) + + // Prepare the credential request + const requestCredential = await agent.prepareRequestCredentialWithIssuer(credentialOffer) + + // Send the credential request + await agent.sendMessage(requestCredential.makeMessage()) + } catch (err) { + console.error('Error occurred while sending the credential request:', err) + } + } + } + }) + ``` + +8. The `Issuer` sends back the `Verifiable Credential` + +9. The `Peer`listens for the credential and stores it in pluto + + ```typescript + agent.addListener(SDK.ListenerKey.MESSAGE, async (newMessages: SDK.Domain.Message[]) => { + // newMessages can contain any didcomm message that is received, including + // Credential Offers, Issued credentials and Request Presentation Messages + const issuedCredentials = newMessages.filter( + (message) => message.piuri === SDK.ProtocolType.DidcommIssueCredential + ) + + if (issuedCredentials.length) { + for (const issuedCredential of issuedCredentials) { + try { + // Create issue credential object from the message + const issueCredential = await SDK.IssueCredential.fromMessage(issuedCredential) + + // Store the credential in pluto + await agent.processIssuedCredentialMessage(issueCredential) + } catch (err) { + console.error('Error occurred while storing the credential:', err) + } + } + } + }) + ``` diff --git a/docs/examples/ConnectionlessPresentation.md b/docs/examples/ConnectionlessPresentation.md index 819abdda9..f97f9dde7 100644 --- a/docs/examples/ConnectionlessPresentation.md +++ b/docs/examples/ConnectionlessPresentation.md @@ -1,56 +1,175 @@ # Edge SDK Connectionless Presentation -## Requirements -1. A working Identus Mediator and an Identus Cloud Agent. -2. A holder who already has a JWT Credential issued by a known issuer (prism:did) [Holder A] +## User Flow -> NOTE: -> -> Please follow the [Quick started guide](../../docs/quick-start) to complete steps 1, 2 +```mermaid +sequenceDiagram + autonumber + actor Peer + actor Admin + participant Verifier -## Flow -1. The verifier will need to generate an invitation url as follows: + Admin->> Verifier: Request a Connectionless Presentation Invitation + Verifier->> Admin: Connectionless Presentation Invitation -``` -curl --location 'http:///cloud-agent/present-proof/presentations/invitation' \ ---header 'Content-Type: application/json' \ ---data '{ - "goalCode" : "present-vp", - "goal" : "Request proof of vaccination information", - "proofs": [], - "options": { - "challenge": "11c91493-01b3-4c4d-ac36-b336bab5bddf", - "domain": "https://prism-verifier.com" - } -} -``` + Admin->>Peer: Connectionless Presentation Invitation (OOB) -The response will look as follows: + Peer ->> Peer: Parse the OOB + + Peer ->> Verifier: Accept the Connectionless Presentation Invitation (OOB) + Verifier ->> Peer: Connectionless Presentation Request + Peer ->> Verifier: Presentation + Admin ->> Verifier: Get Presentation + Verifier ->> Admin: Verified ``` -{ - "presentationId": "715bc063-cd61-4049-834c-465b3eb19042", - "thid": "71a16da3-97ff-460a-b5f9-25a61fe864f4", - "role": "Verifier", - "status": "InvitationGenerated", - "proofs": [], - "data": [], - "goalCode": "present-vp", - "goal": "Request proof of vaccination information", - "myDid": "did:peer:2.Ez6LSt2C1xviB7nFEUHJAyWqvDF2xyJtQHWafQMxmy1SySoH6.Vz6MkgMChqZVQ47G88EBY6QZoPbBsCqByhtyfA3TQpyBKn7Ej.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly8xOTIuMTY4LjEuNDQ6ODAwMC9kaWRjb21tIiwiciI6W10sImEiOlsiZGlkY29tbS92MiJdfX0", - "invitation": { - "id": "71a16da3-97ff-460a-b5f9-25a61fe864f4", - "type": "https://didcomm.org/out-of-band/2.0/invitation", - "from": "did:peer:2.Ez6LSt2C1xviB7nFEUHJAyWqvDF2xyJtQHWafQMxmy1SySoH6.Vz6MkgMChqZVQ47G88EBY6QZoPbBsCqByhtyfA3TQpyBKn7Ej.SeyJ0IjoiZG0iLCJzIjp7InVyaSI6Imh0dHA6Ly8xOTIuMTY4LjEuNDQ6ODAwMC9kaWRjb21tIiwiciI6W10sImEiOlsiZGlkY29tbS92MiJdfX0", - "invitationUrl": "https://my.domain.com/path?_oob=eyJpZCI6IjcxYTE2ZGEzLTk3ZmYtNDYwYS1iNWY5LTI1YTYxZmU4NjRmNCIsInR5cGUiOiJodHRwczovL2RpZGNvbW0ub3JnL291dC1vZi1iYW5kLzIuMC9pbnZpdGF0aW9uIiwiZnJvbSI6ImRpZDpwZWVyOjIuRXo2TFN0MkMxeHZpQjduRkVVSEpBeVdxdkRGMnh5SnRRSFdhZlFNeG15MVN5U29INi5WejZNa2dNQ2hxWlZRNDdHODhFQlk2UVpvUGJCc0NxQnlodHlmQTNUUXB5QktuN0VqLlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW1oMGRIQTZMeTh4T1RJdU1UWTRMakV1TkRRNk9EQXdNQzlrYVdSamIyMXRJaXdpY2lJNlcxMHNJbUVpT2xzaVpHbGtZMjl0YlM5Mk1pSmRmWDAiLCJib2R5Ijp7ImdvYWxfY29kZSI6InByZXNlbnQtdnAiLCJnb2FsIjoiUmVxdWVzdCBwcm9vZiBvZiB2YWNjaW5hdGlvbiBpbmZvcm1hdGlvbiIsImFjY2VwdCI6W119LCJhdHRhY2htZW50cyI6W3siaWQiOiJjOGMxY2E3Zi05YjJjLTQwOGQtODZkMi0zNWJiYmU2ZTMwNjgiLCJtZWRpYV90eXBlIjoiYXBwbGljYXRpb24vanNvbiIsImRhdGEiOnsianNvbiI6eyJpZCI6Ijc4ZTc5NTFhLWZhOTYtNDg2Mi1hYmVkLWFmYTBhZWQyMzgzNCIsInR5cGUiOiJodHRwczovL2RpZGNvbW0uYXRhbGFwcmlzbS5pby9wcmVzZW50LXByb29mLzMuMC9yZXF1ZXN0LXByZXNlbnRhdGlvbiIsImJvZHkiOnsiZ29hbF9jb2RlIjoiUmVxdWVzdCBQcm9vZiBQcmVzZW50YXRpb24iLCJ3aWxsX2NvbmZpcm0iOmZhbHNlLCJwcm9vZl90eXBlcyI6W119LCJhdHRhY2htZW50cyI6W3siaWQiOiJiNWIzZjBjMC05NWQ2LTRkOTItOWQ0Ni1hNDVmYTdlMzVjYWEiLCJtZWRpYV90eXBlIjoiYXBwbGljYXRpb24vanNvbiIsImRhdGEiOnsianNvbiI6eyJvcHRpb25zIjp7ImNoYWxsZW5nZSI6IjExYzkxNDkzLTAxYjMtNGM0ZC1hYzM2LWIzMzZiYWI1YmRkZiIsImRvbWFpbiI6Imh0dHBzOi8vcHJpc20tdmVyaWZpZXIuY29tIn0sInByZXNlbnRhdGlvbl9kZWZpbml0aW9uIjp7ImlkIjoiZjNmNmQwYTAtYzdhNS00NjAzLWJkZjUtMjU1ZWJkZGU5ODdiIiwiaW5wdXRfZGVzY3JpcHRvcnMiOltdfX19LCJmb3JtYXQiOiJwcmlzbS9qd3QifV0sInRoaWQiOiI3MWExNmRhMy05N2ZmLTQ2MGEtYjVmOS0yNWE2MWZlODY0ZjQiLCJmcm9tIjoiZGlkOnBlZXI6Mi5FejZMU3QyQzF4dmlCN25GRVVISkF5V3F2REYyeHlKdFFIV2FmUU14bXkxU3lTb0g2LlZ6Nk1rZ01DaHFaVlE0N0c4OEVCWTZRWm9QYkJzQ3FCeWh0eWZBM1RRcHlCS243RWouU2V5SjBJam9pWkcwaUxDSnpJanA3SW5WeWFTSTZJbWgwZEhBNkx5OHhPVEl1TVRZNExqRXVORFE2T0RBd01DOWthV1JqYjIxdElpd2ljaUk2VzEwc0ltRWlPbHNpWkdsa1kyOXRiUzkyTWlKZGZYMCJ9fX1dLCJjcmVhdGVkX3RpbWUiOjE3MjQ0MjY1NjgsImV4cGlyZXNfdGltZSI6MTcyNDQyNjg2OH0=" - }, - "metaRetries": 5 -} -``` -The Verifier will make this available to the holder in shape of QA code, link, etc. +## Steps + +1. **Admin** requests a connectionless presentation request invitation from the **Verifier** + + ```bash + curl --location 'http://localhost:9000/cloud-agent/present-proof/presentations/invitation' \ + --header 'Content-Type: application/json' \ + --data '{ + "goalCode" : "present-vp", + "goal" : "Request presentation", + "proofs": [], + "options": { + "challenge": "a6af4ea1-2f11-43de-bd17-e0182e55f919", + "domain": "http://localhost:9000/cloud-agent" + } + }' + ``` + +2. The **Verifier** sends back the `Connectionless Presentation Request Invitation` + - Response example: + + ```json + { + "presentationId": "76b2b571-8456-4413-8dd3-d8f5186fd472", + "thid": "9d99f0b4-25b7-47e6-8816-1687c86dc3f1", + "role": "Verifier", + "status": "InvitationGenerated", + "proofs": [], + "data": [], + "requestData": [ + { + "options": { + "challenge": "a6af4ea1-2f11-43de-bd17-e0182e55f919", + "domain" : "http://localhost:9000/cloud-agent" + }, + "presentation_definition" : { + "id": "070cd2f8-e8cc-4ae4-94bd-3ac886fed6df", + "input_descriptors": [], + "name": null, + "purpose": null, + "format" : null + } + } + ], + "goalCode": "present-vp", + "goal": "Request presentation", + "myDid": "did:peer:2.Ez6LS...", + "invitation": { + "id": "9d99f0b4-25b7-47e6-8816-1687c86dc3f1", + "type": "https://didcomm.org/out-of-band/2.0/invitation", + "from": "did:peer:2.Ez6LS...9", + "invitationUrl": "https://my.domain.com/path?_oob=eyJpZCI6IjlkOTlmMGI0LTI1YjctNDdlNi04ODE2LTE2ODdjODZkYzNmMSIsInR5cGUiOiJodHRwczovL2RpZGNvbW0ub3JnL291dC1vZi1iYW5kLzIuMC9pbnZpdGF0aW9uIiwiZnJvbSI6ImRpZDpwZWVyOjIuRXo2TFNpb2Foa3lncmZ0cnpLUWM2aXNyVngyTEhoSlFpQTF1U0ZCeHBXMW02aE56SC5WejZNa2VvdlE5bUVoVm9TcXJucUhkYVltRjhHRTdvOXlXWTJvVmlrdThFelhLNGloLlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW1oMGRIQTZMeTh4T1RJdU1UWTRMalk0TGpZeU9qa3dNREF2Wkdsa1kyOXRiU0lzSW5JaU9sdGRMQ0poSWpwYkltUnBaR052YlcwdmRqSWlYWDE5IiwiYm9keSI6eyJnb2FsX2NvZGUiOiJwcmVzZW50LXZwIiwiZ29hbCI6IlJlcXVlc3QgcHJlc2VudGF0aW9uIiwiYWNjZXB0IjpbImRpZGNvbW0vdjIiXX0sImF0dGFjaG1lbnRzIjpbeyJpZCI6ImZjMGQ1MWY0LTlhMmItNDVmOS1hM2E0LWJkNmQwZTE2MzZjZCIsIm1lZGlhX3R5cGUiOiJhcHBsaWNhdGlvbi9qc29uIiwiZGF0YSI6eyJqc29uIjp7ImlkIjoiYjgwMDkyZGUtZTU2MS00YzI2LTkzNzYtMTgxMWEwMWNiNWNjIiwidHlwZSI6Imh0dHBzOi8vZGlkY29tbS5hdGFsYXByaXNtLmlvL3ByZXNlbnQtcHJvb2YvMy4wL3JlcXVlc3QtcHJlc2VudGF0aW9uIiwiYm9keSI6eyJnb2FsX2NvZGUiOiJSZXF1ZXN0IFByb29mIFByZXNlbnRhdGlvbiIsIndpbGxfY29uZmlybSI6ZmFsc2UsInByb29mX3R5cGVzIjpbXX0sImF0dGFjaG1lbnRzIjpbeyJpZCI6IjIyOTM0Y2U5LTJmMzMtNDkxYS1hODEyLTAyOGNkMDAzYjQ3NiIsIm1lZGlhX3R5cGUiOiJhcHBsaWNhdGlvbi9qc29uIiwiZGF0YSI6eyJqc29uIjp7Im9wdGlvbnMiOnsiY2hhbGxlbmdlIjoiYTZhZjRlYTEtMmYxMS00M2RlLWJkMTctZTAxODJlNTVmOTE5IiwiZG9tYWluIjoiaHR0cDovL2xvY2FsaG9zdDo5MDAwL2Nsb3VkLWFnZW50In0sInByZXNlbnRhdGlvbl9kZWZpbml0aW9uIjp7ImlkIjoiMDcwY2QyZjgtZThjYy00YWU0LTk0YmQtM2FjODg2ZmVkNmRmIiwiaW5wdXRfZGVzY3JpcHRvcnMiOltdfX19LCJmb3JtYXQiOiJwcmlzbS9qd3QifV0sInRoaWQiOiI5ZDk5ZjBiNC0yNWI3LTQ3ZTYtODgxNi0xNjg3Yzg2ZGMzZjEiLCJmcm9tIjoiZGlkOnBlZXI6Mi5FejZMU2lvYWhreWdyZnRyektRYzZpc3JWeDJMSGhKUWlBMXVTRkJ4cFcxbTZoTnpILlZ6Nk1rZW92UTltRWhWb1Nxcm5xSGRhWW1GOEdFN285eVdZMm9WaWt1OEV6WEs0aWguU2V5SjBJam9pWkcwaUxDSnpJanA3SW5WeWFTSTZJbWgwZEhBNkx5OHhPVEl1TVRZNExqWTRMall5T2prd01EQXZaR2xrWTI5dGJTSXNJbklpT2x0ZExDSmhJanBiSW1ScFpHTnZiVzB2ZGpJaVhYMTkifX19XSwiY3JlYXRlZF90aW1lIjoxNzMwMTY3MTIxLCJleHBpcmVzX3RpbWUiOjE3MzAxNjc0MjF9" + }, + "metaRetries": 5 + } + ``` + +3. The **Admin** forwards the `invitationUrl` to the **Peer**. + +4. The **Peer** validates the `invitationUrl` with `agent.parseInvitation`: + + ```typescript + const oob = await agent.parseInvitation(invitationUrl) + ``` -2. The holder then opens its the Edge Agent Wallet SDK, "Connections tab" and will pase the invitation link in the field. +5. The **Peer** accepts the invitation using `agent.acceptInvitation`: -Once the invitation is parsed, the user will then be able to see a new Verification request, choose one of the available credentials and send the proof as the verifier requested. + ```typescript + await agent.acceptInvitation(oob) + ``` + +6. The **Verifier** sends the presentation request to the **Peer**. + +7. The **Peer** listens for the presentation request and prepares the presentation of the verifiable credential. + + ```typescript + agent.addListener(SDK.ListenerKey.MESSAGE, async (newMessages: SDK.Domain.Message[]) => { + const presentationRequests = newMessages.filter( + (message) => message.piuri === SDK.ProtocolType.DidcommRequestPresentation + ) + + const credentials = await agent.pluto.getAllCredentials() + const lastCredential = lastCredentials.at(-1) + + if (presentationRequests.length) { + for (const requestMessage of presentationRequests) { + try { + const presentationRequest = await SDK.RequestPresentation.fromMessage(requestMessage) + const presentation = await agent.createPresentationForRequestProof(presentationRequest, lastCredential) + await agent.sendMessage(presentation.makeMessage()) + } catch (err) { + console.error('Error presenting the credential:', err) + } + } + } + }) + ``` + +8. The **Admin** requests the presentation from the **Verifier** + + ```bash + curl -X GET "http://localhost:9000/present-proof/presentations/76b2b571-8456-4413-8dd3-d8f5186fd472" \ + -H "accept: application/json" \ + -H "Content-Type: application/json" \ + -H "apikey: [[VERIFIER_API_KEY]]" + ``` + +9. The **Verifier** responds that the Credential is verified + + - Response example: + + ```json + { + "presentationId": "76b2b571-8456-4413-8dd3-d8f5186fd472", + "thid": "9d99f0b4-25b7-47e6-8816-1687c86dc3f1", + "role": "Verifier", + "status": "PresentationVerified", + "proofs": [], + "data": [ + "eyJhbGciOiJFUzI...." + ], + "requestData": [ + { + "options": { + "challenge": "a6af4ea1-2f11-43de-bd17-e0182e55f919", + "domain" : "http://localhost:9000/cloud-agent" + }, + "presentation_definition" : { + "id": "070cd2f8-e8cc-4ae4-94bd-3ac886fed6df", + "input_descriptors": [], + "name": null, + "purpose": null, + "format" : null + } + } + ], + "goalCode": "present-vp", + "goal": "Request presentation", + "myDid": "did:peer:2.Ez6LS...", + "invitation": { + "id": "9d99f0b4-25b7-47e6-8816-1687c86dc3f1", + "type": "https://didcomm.org/out-of-band/2.0/invitation", + "from": "did:peer:2.Ez6LS...9", + "invitationUrl": "https://my.domain.com/path?_oob=eyJpZCI6IjlkOTlmMGI0LTI1YjctNDdlNi04ODE2LTE2ODdjODZkYzNmMSIsInR5cGUiOiJodHRwczovL2RpZGNvbW0ub3JnL291dC1vZi1iYW5kLzIuMC9pbnZpdGF0aW9uIiwiZnJvbSI6ImRpZDpwZWVyOjIuRXo2TFNpb2Foa3lncmZ0cnpLUWM2aXNyVngyTEhoSlFpQTF1U0ZCeHBXMW02aE56SC5WejZNa2VvdlE5bUVoVm9TcXJucUhkYVltRjhHRTdvOXlXWTJvVmlrdThFelhLNGloLlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW1oMGRIQTZMeTh4T1RJdU1UWTRMalk0TGpZeU9qa3dNREF2Wkdsa1kyOXRiU0lzSW5JaU9sdGRMQ0poSWpwYkltUnBaR052YlcwdmRqSWlYWDE5IiwiYm9keSI6eyJnb2FsX2NvZGUiOiJwcmVzZW50LXZwIiwiZ29hbCI6IlJlcXVlc3QgcHJlc2VudGF0aW9uIiwiYWNjZXB0IjpbImRpZGNvbW0vdjIiXX0sImF0dGFjaG1lbnRzIjpbeyJpZCI6ImZjMGQ1MWY0LTlhMmItNDVmOS1hM2E0LWJkNmQwZTE2MzZjZCIsIm1lZGlhX3R5cGUiOiJhcHBsaWNhdGlvbi9qc29uIiwiZGF0YSI6eyJqc29uIjp7ImlkIjoiYjgwMDkyZGUtZTU2MS00YzI2LTkzNzYtMTgxMWEwMWNiNWNjIiwidHlwZSI6Imh0dHBzOi8vZGlkY29tbS5hdGFsYXByaXNtLmlvL3ByZXNlbnQtcHJvb2YvMy4wL3JlcXVlc3QtcHJlc2VudGF0aW9uIiwiYm9keSI6eyJnb2FsX2NvZGUiOiJSZXF1ZXN0IFByb29mIFByZXNlbnRhdGlvbiIsIndpbGxfY29uZmlybSI6ZmFsc2UsInByb29mX3R5cGVzIjpbXX0sImF0dGFjaG1lbnRzIjpbeyJpZCI6IjIyOTM0Y2U5LTJmMzMtNDkxYS1hODEyLTAyOGNkMDAzYjQ3NiIsIm1lZGlhX3R5cGUiOiJhcHBsaWNhdGlvbi9qc29uIiwiZGF0YSI6eyJqc29uIjp7Im9wdGlvbnMiOnsiY2hhbGxlbmdlIjoiYTZhZjRlYTEtMmYxMS00M2RlLWJkMTctZTAxODJlNTVmOTE5IiwiZG9tYWluIjoiaHR0cDovL2xvY2FsaG9zdDo5MDAwL2Nsb3VkLWFnZW50In0sInByZXNlbnRhdGlvbl9kZWZpbml0aW9uIjp7ImlkIjoiMDcwY2QyZjgtZThjYy00YWU0LTk0YmQtM2FjODg2ZmVkNmRmIiwiaW5wdXRfZGVzY3JpcHRvcnMiOltdfX19LCJmb3JtYXQiOiJwcmlzbS9qd3QifV0sInRoaWQiOiI5ZDk5ZjBiNC0yNWI3LTQ3ZTYtODgxNi0xNjg3Yzg2ZGMzZjEiLCJmcm9tIjoiZGlkOnBlZXI6Mi5FejZMU2lvYWhreWdyZnRyektRYzZpc3JWeDJMSGhKUWlBMXVTRkJ4cFcxbTZoTnpILlZ6Nk1rZW92UTltRWhWb1Nxcm5xSGRhWW1GOEdFN285eVdZMm9WaWt1OEV6WEs0aWguU2V5SjBJam9pWkcwaUxDSnpJanA3SW5WeWFTSTZJbWgwZEhBNkx5OHhPVEl1TVRZNExqWTRMall5T2prd01EQXZaR2xrWTI5dGJTSXNJbklpT2x0ZExDSmhJanBiSW1ScFpHTnZiVzB2ZGpJaVhYMTkifX19XSwiY3JlYXRlZF90aW1lIjoxNzMwMTY3MTIxLCJleHBpcmVzX3RpbWUiOjE3MzAxNjc0MjF9" + }, + "metaRetries": 5 + } + ```