Aries OpenAPI Demo ¶
What better way to learn about controllers than by actually being one yourself! In this demo, that’s just what happens—you are the controller. You have access to the full set of API endpoints exposed by an ACA-Py instance, and you will see the events coming from ACA-Py as they happen. Using that information, you'll help Alice's and Faber's agents connect, Faber's agent issue an education credential to Alice, and then ask Alice to prove she possesses the credential. Who knows why Faber needs to get the proof, but it lets us show off more protocols.
Contents ¶
- Getting Started
- Running in a Browser
- Start the Faber Agent
- Start the Alice Agent
- Running in Docker
- Start the Faber Agent
- Start the Alice Agent
- Restarting the Docker Containers
- Using the OpenAPI/Swagger User Interface
- Establishing a Connection
- Use the Faber Agent to Create an Invitation
- Copy the Invitation created by the Faber Agent
- Use the Alice Agent to Receive Faber's Invitation
- Tell Alice's Agent to Accept the Invitation
- The Faber Agent Gets the Request
- The Faber Agent Completes the Connection
- Review the Connection Status in Alice's Agent
- Review the Connection Status in Faber's Agent
- Basic Messaging Between Agents
- Sending a message from Alice to Faber
- Receiving a Basic Message (Faber)
- Alice's Agent Verifies that Faber has Received the Message
- Preparing to Issue a Credential
- Confirming your Schema and Credential Definition
- Notes
- Issuing a Credential
- Faber - Preparing to Issue a Credential
- Faber - Issuing the Credential
- Alice Receives Credential
- Alice Stores Credential in her Wallet
- Faber Receives Acknowledgment that the Credential was Received
- Issue Credential Notes
- Bonus Points
- Requesting/Presenting a Proof
- Faber sends a Proof Request
- Alice - Responding to the Proof Request
- Faber - Verifying the Proof
- Present Proof Notes
- Bonus Points
- Conclusion
Getting Started¶
We will get started by opening three browser tabs that will be used throughout the lab. Two will be Swagger UIs for the Faber and Alice agent and one for the public ledger (showing the Hyperledger Indy ledger). As well, we'll keep the terminal sessions where we started the demos handy, as we'll be grabbing information from them as well.
Let's start with the ledger browser. For this demo, we're going to use an open public ledger operated by the BC Government's VON Team. In your first browser tab, go to: http://test.bcovrin.vonx.io. This will be called the "ledger tab" in the instructions below.
For the rest of the set up, you can choose to run the terminal sessions in your browser (no local resources needed), or you can run it in Docker on your local system. Your choice, each is covered in the next two sections.
Note: In the following, when we start the agents we use several special demo settings. The command we use is this:
LEDGER_URL=http://test.bcovrin.vonx.io ./run_demo faber --events --no-auto --bg
. In that:
- The
LEDGER_URL
environment variable informs the agent what ledger to use.- The
--events
option indicates that we want the controller to display the webhook events from ACA-Py in the log displayed on the terminal.- The
--no-auto
option indicates that we don't want the ACA-Py agent to automatically handle some events such as connecting. We want the controller (you!) to handle each step of the protocol.- The
--bg
option indicates that the docker container will run in the background, so accidentally hitting Ctrl-C won't stop the process.
Running in a Browser¶
To run the necessary terminal sessions in your browser, go to the Docker playground service Play with Docker. Don't know about Play with Docker? Check this out to learn more.
Start the Faber Agent¶
In a browser, go to the Play with Docker home page, Login (if necessary) and click "Start." On the next screen, click (in the left menu) "+Add a new instance." That will start up a terminal in your browser. Run the following commands to start the Faber agent.
git clone https://github.com/openwallet-foundation/acapy
cd acapy/demo
LEDGER_URL=http://test.bcovrin.vonx.io ./run_demo faber --events --no-auto --bg
Once you are back at the command prompt, we'll use docker's logging capability to see what's being written to the terminal:
Once the Faber agent has started up (with the invite displayed), click the link near the top of the screen 8021
. That will start an instance of the OpenAPI/Swagger user interface connected to the Faber instance. Note that the URL on the OpenAPI/Swagger instance is: http://ip....8021.direct...
.
Remember that the OpenAPI/Swagger browser tab with an address containing 8021 is the Faber agent.
NOTE: Hit "Ctrl-C" at any time to get back to the command line. When you are done with the command line, you can return to seeing the logs from the Faber agent by running
docker logs -f faber
Show me a screenshot!
Start the Alice Agent¶
Now to start Alice's agent. Click the "+Add a new instance" button again to open another terminal session. Run the following commands to start Alice's agent:
git clone https://github.com/openwallet-foundation/acapy
cd acapy/demo
LEDGER_URL=http://test.bcovrin.vonx.io ./run_demo alice --events --no-auto --bg
Once you are back at the command prompt, we'll use docker's logging capability to see what's being written to the terminal:
You can ignore a message like WARNING: your terminal doesn't support cursor position requests (CPR).
Once the Alice agent has started up (with the invite:
prompt displayed), click the link near the top of the screen 8031
. That will start an instance of the OpenAPI/Swagger User Interface connected to the Alice instance. Note that the URL on the OpenAPI/Swagger instance is: http://ip....8031.direct...
.
NOTE: Hit "Ctrl-C" to get back to the command line. When you are done with the command line, you can return to seeing the logs from the Faber agent by running
docker logs -f faber
Remember that the OpenAPI/Swagger browser tab with an address containing 8031 is Alice's agent.
Show me a screenshot!
You are ready to go. Skip down to the Using the OpenAPI/Swagger User Interface section.
Running in Docker¶
To run the demo on your local system, you must have git, a running Docker installation, and terminal windows running bash. Need more information about getting set up? Click here to learn more.
Start the Faber Agent¶
To begin running the demo in Docker, open up two terminal windows, one each for Faber’s and Alice’s agent.
In the first terminal window, clone the ACA-Py repo, change into the demo folder and start the Faber agent:
git clone https://github.com/openwallet-foundation/acapy
cd acapy/demo
LEDGER_URL=http://test.bcovrin.vonx.io ./run_demo faber --events --no-auto --bg
Once you are back at the command prompt, we'll use docker's logging capability to see what's being written to the terminal:
If all goes well, the agent will show a message indicating it is running. Use the second browser tab to navigate to http://localhost:8021. You should see an OpenAPI/Swagger user interface with a (long-ish) list of API endpoints. These are the endpoints exposed by the Faber agent.
NOTE: Hit "Ctrl-C" to get back to the command line. When you are done with the command line, you can return to seeing the logs from the Faber agent by running
docker logs -f faber
Remember that the OpenAPI/Swagger browser tab with an address containing 8021 is the Faber agent.
Show me a screenshot!
Start the Alice Agent¶
To start Alice's agent, open up a second terminal window and in it, change to the same demo
directory as where Faber's agent was started above. Once there, start Alice's agent:
Once you are back at the command prompt, we'll use docker's logging capability to see what's being written to the terminal:
You can ignore a message like WARNING: your terminal doesn't support cursor position requests (CPR)
that may appear.
If all goes well, the agent will show a message indicating it is running. Open a third browser tab and navigate to http://localhost:8031. Again, you should see the OpenAPI/Swagger user interface with a list of API endpoints, this time the endpoints for Alice’s agent.
NOTE: Hit "Ctrl-C" to get back to the command line. When you are done with the command line, you can return to seeing the logs from the Alice agent by running
docker logs -f alice
Remember that the OpenAPI/Swagger browser tab with an address containing 8031 is Alice's agent.
Show me a screenshot!
Restarting the Docker Containers¶
When you complete the entire demo (not now!!), you can need to stop the two agents. To do that, get to the command line by hitting Ctrl-C and running:
Using the OpenAPI/Swagger User Interface¶
Try to organize what you see on your screen to include both the Alice and Faber OpenAPI/Swagger tabs, and both (Alice and Faber) terminal sessions, all at the same time. After you execute an API call in one of the browser tabs, you will see a webhook event from the ACA-Py instance in the terminal window of the other agent. That's a controller's life. See an event, process it, send a response.
From time to time you will want to see what's happening on the ledger, so keep that handy as well. As well, if you make an error with one of the commands (e.g. bad data, improperly structured JSON), you will see the errors in the terminals.
In the instructions that follow, we’ll let you know if you need to be in the Faber, Alice or Indy browser tab. We’ll leave it to you to track which is which.
Using the OpenAPI/Swagger user interface is pretty simple. In the steps below, we’ll indicate what API endpoint you need use, such as POST /connections/create-invitation
. That means you must:
- scroll to and find that endpoint;
- click on the endpoint name to expand its section of the UI;
- click on the
Try it out
button; - fill in any data necessary to run the command;
- click
Execute
; - check the response to see if the request worked.
So, the mechanical steps are easy. It’s fourth step from the list above that can be tricky. Supplying the right data and, where JSON is involved, getting the syntax correct - braces and quotes can be a pain. When steps don’t work, start your debugging by looking at your JSON.
Enough with the preliminaries, let’s get started!
Establishing a Connection¶
We’ll start the demo by establishing a connection between the Alice and Faber agents. We’re starting there to demonstrate that you can use agents without having a ledger. We won’t be using the Indy public ledger at all for this step. Since the agents communicate using DIDComm messaging and connect by exchanging pairwise DIDs and DIDDocs based on (an early version of) the did:peer
DID method, a public ledger is not needed.
Use the Faber Agent to Create an Invitation¶
In the Faber browser tab, navigate to the POST /connections/create-invitation
endpoint. Replace the sample body with and empty production ({}
) and execute the call. If successful, you should see a connection id, an invitation, and the invitation URL. The connection ids will be different on each run.
Hint: set an Alias on the Invitation, this makes it easier to find the Connection later on
Show me a screenshot - Create Invitation Request
Show me a screenshot - Create Invitation Response
Copy the Invitation created by the Faber Agent¶
Copy the entire block of the invitation
object, from the curly brackets {}
, excluding the trailing comma.
Show me a screenshot - Create Invitation Response
Before switching over to the Alice browser tab, scroll to and execute the GET /connections
endpoint to see the list of Faber's connections. You should see a connection with a connection_id
that is identical to the invitation you just created, and that its state is invitation
.
Show me a screenshot - Faber Connection Status
Use the Alice Agent to Receive Faber's Invitation¶
Switch to the Alice browser tab and get ready to execute the POST /connections/receive-invitation
endpoint. Select all of the pre-populated text and replace it with the invitation object from the Faber tab. When you click Execute
you should get back a connection response with a connection Id, an invitation key, and the state of the connection, which should be invitation
.
Hint: set an Alias on the Invitation, this makes it easier to find the Connection later on
Show me a screenshot - Receive Invitation Request
Show me a screenshot - Receive Invitation Response
A key observation to make here. The "copy and paste" we are doing here from Faber's agent to Alice's agent is what is called an "out of band" message. Because we don't yet have a DIDComm connection between the two agents, we have to convey the invitation in plaintext (we can't encrypt it - no channel) using some other mechanism than DIDComm. With mobile agents, that's where QR codes often come in. Once we have the invitation in the receivers agent, we can get back to using DIDComm.
Tell Alice's Agent to Accept the Invitation¶
At this point Alice has simply stored the invitation in her wallet. You can see the status using the GET /connections
endpoint.
Show me a screenshot
To complete a connection with Faber, she must accept the invitation and send a corresponding connection request to Faber. Find the connection_id
in the connection response from the previous POST /connections/receive-invitation
endpoint call. You may note that the same data was sent to the controller as an event from ACA-Py and is visible in the terminal. Scroll to the POST /connections/{conn_id}/accept-invitation
endpoint and paste the connection_id
in the id
parameter field (you will have to click the Try it out
button to see the available URL parameters). The response from clicking Execute
should show that the connection has a state of request
.
Show me a screenshot - Accept Invitation Request
Show me a screenshot - Accept Invitation Response
The Faber Agent Gets the Request¶
In the Faber terminal session, an event (a web service callback from ACA-Py to the controller) has been received about the request from Alice. Copy the connection_id
from the event for the next step.
Show me the event
Note that the connection ID held by Alice is different from the one held by Faber. That makes sense, as both independently created connection objects, each with a unique, self-generated GUID.
The Faber Agent Completes the Connection¶
To complete the connection process, Faber will respond to the connection request from Alice. Scroll to the POST /connections/{conn_id}/accept-request
endpoint and paste the connection_id
you previously copied into the id
parameter field (you will have to click the Try it out
button to see the available URL parameters). The response from clicking the Execute
button should show that the connection has a state of response
, which indicates that Faber has accepted Alice's connection request.
Show me a screenshot - Accept Connection Request
Show me a screenshot - Accept Connection Request
Review the Connection Status in Alice's Agent¶
Switch over to the Alice browser tab.
Scroll to and execute GET /connections
to see a list of Alice's connections, and the information tracked about each connection. You should see the one connection Alice’s agent has, that it is with the Faber agent, and that its state is active
.
Show me a screenshot - Alice Connection Status
As with Faber's side of the connection, Alice received a notification that Faber had accepted her connection request.
Show me the event
Review the Connection Status in Faber's Agent¶
You are connected! Switch to the Faber browser tab and run the same GET /connections
endpoint to see Faber's view of the connection. Its state is also active
. Note the connection_id
, you’ll need it later in the tutorial.
Show me a screenshot - Faber Connection Status
Basic Messaging Between Agents¶
Once you have a connection between two agents, you have a channel to exchange secure, encrypted messages. In fact these underlying encrypted messages (similar to envelopes in a postal system) enable the delivery of messages that form the higher level protocols, such as issuing Credentials and providing Proofs. So, let's send a couple of messages that contain the simplest of context—text. For this we wil use the Basic Message protocol, Aries RFC 0095.
Sending a message from Alice to Faber¶
On Alice's swagger page, scroll to the POST /connections/{conn_id}/send-message
endpoint. Click on Try it Out
and enter a message in the body provided (for example {"content": "Hello Faber"}
). Enter the connection id of Alice's connection in the field provided. Then click on Execute
.
Show me a screenshot
Receiving a Basic Message (Faber)¶
How does Faber know that a message was sent? If you take a look at Faber's console window, you can see that Faber's agent has raised an Event that the message was received:
Show me a screenshot
Faber's controller application can take whatever action is necessary to process this message. It could trigger some application code, or it might just be something the Faber application needs to display to its user (for example a reminder about some action the user needs to take).
Alice's Agent Verifies that Faber has Received the Message¶
How does Alice get feedback that Faber has received the message? The same way - when Faber's agent acknowledges receipt of the message, Alice's agent raises an Event to let the Alice controller know:
Show me a screenshot
Again, Alice's agent can take whatever action is necessary, possibly just flagging the message as having been received
.
Preparing to Issue a Credential¶
The next thing we want to do in the demo is have the Faber agent issue a credential to Alice’s agent. To this point, we have not used the Indy ledger at all. Establishing the connection and messaging has been done with pairwise DIDs based on the did:peer
method. Verifiable credentials must be rooted in a public DID ledger to enable the presentation of proofs.
Before the Faber agent can issue a credential, it must register a DID on the Indy public ledger, publish a schema, and create a credential definition. In the “real world”, the Faber agent would do this before connecting with any other agents. And, since we are using the handy "./run_demo faber" (and "./run_demo alice") scripts to start up our agents, the Faber version of the script has already:
- registered a public DID and stored it on the ledger;
- created a schema and registered it on the ledger;
- created a credential definition and registered it on the ledger.
The schema and credential definition could also be created through this swagger interface.
We don't cover the details of those actions in this tutorial, but there are other materials available that go through these details.
To Do: Add a link to directions for doing this manually, and to where in the controller Python code this is done.
Confirming your Schema and Credential Definition¶
You can confirm the schema and credential definition were published by going back to the Indy ledger browser tab using Faber's public DID. You may have saved that from a previous step, but if not here is an API call you can make to get that information. Using Faber's swagger page and scroll to the GET /wallet/did/public
endpoint. Click on Try it Out
and Execute
and you will see Faber's public DID.
Show me a screenshot
On the ledger browser of the BCovrin ledger, click the Domain
page, refresh, and paste the Faber public DID into the Filter:
field:
Show me a screenshot
The ledger browser should refresh and display the four (4) transactions on the ledger related to this DID:
- the initial DID registration
- registration of the DID endpoint (Faber is an issuer so it has a public endpoint)
- the registered schema
- the registered credential definition
Show me the ledger transactions
You can also look up the Schema and Credential Definition information using Faber's swagger page. Use the GET /schemas/created
endpoint to get a list of schemas, including the one schema_id
that the Faber agent has defined. Keep this section of the Swagger page expanded as we'll need to copy the Id as part of starting the issue credential protocol coming next.
Show me a screenshot
Likewise use the GET /credential-definitions/created
endpoint to get the list of the one (in this case) credential definition id created by Faber. Keep this section of the Swagger page expanded as we'll also need to copy the Id as part of starting the issue credential protocol coming next.
Show me a screenshot
Hint: Remember how the schema and credential definitions were created for you as Faber started up? To do it yourself, use the POST
versions of these endpoints. Now you know!
Notes¶
The one time setup work for issuing a credential is complete—creating a DID, schema and credential definition. We can now issue 1 or 1 million credentials without having to do those steps again. Astute readers might note that we did not setup a revocation registry, so we cannot revoke the credentials we issue with that credential definition. You can’t have everything in an "easy" tutorial!
Issuing a Credential¶
Triggering the issuance of a credential from the Faber agent to Alice’s agent is done with another API call. In the Faber browser tab, scroll down to the POST /issue-credential-2.0/send
and get ready to (but don’t yet) execute the request. Before execution, you need to update most of the data elements in the JSON. We now cover how to update all the fields.
Faber - Preparing to Issue a Credential¶
First, get the connection Id for Faber's connection with Alice. You can copy that from the Faber terminal (the last received event includes it), or scroll up on the Faber swagger tab to the GET /connections
API endpoint, execute, copy it and paste the connection_id
value into the same field in the issue credential JSON.
Click here to see a screenshot
For the following fields, scroll on Faber's Swagger page to the listed endpoint, execute (if necessary), copy the response value and paste as the values of the following JSON items:
issuer_did
the Faber public DID (useGET /wallet/DID/public
),schema_id
the Id of the schema Faber created (useGET /schemas/created
) and,cred_def_id
the Id of the credential definition Faber created (useGET /credential-definitions/created
)
into the filter
section's indy
subsection. Remove the "dif"
subsection of the filter
section within the JSON, and specify the remaining indy filter criteria as follows:
schema_version
: set to the last segment of theschema_id
, a three part version number that was randomly generated on startup of the Faber agent. Segments of theschema_id
are separated by ":"s.schema_issuer_did
: set to the same the value as inissuer_did
,schema_name
: set to the second last segment of theschema_id
, in this casedegree schema
Finally, set the remaining values as follows:
- auto_remove
: set to true
(no quotes), see note below
- comment
: set to any string. It's intended to let Alice know something about the credential being offered.
- trace
: set to false
(no quotes). It's for troubleshooting, performance profiling, and/or diagnostics.
By setting auto_remove
to true, ACA-Py will automatically remove the credential exchange record after the protocol completes. When implementing a controller, this is the likely setting to use to reduce agent storage usage, but implies if a record of the issuance of the credential is needed, the controller must save it somewhere else. For example, Faber College might extend their Student Information System, where they track all their students, to record when credentials are issued to students, and the Ids of the issued credentials.
Faber - Issuing the Credential¶
Finally, we need put into the JSON the data values for the credential_preview
section of the JSON. Copy the following and paste it between the square brackets of the attributes
item, replacing what is there. Feel free to change the attribute value
items, but don't change the labels or names:
{
"name": "name",
"value": "Alice Smith"
},
{
"name": "timestamp",
"value": "1234567890"
},
{
"name": "date",
"value": "2018-05-28"
},
{
"name": "degree",
"value": "Maths"
},
{
"name": "birthdate_dateint",
"value": "19640101"
}
(Note that the birthdate above is used to present later on to pass an "age proof".)
OK, finally, you are ready to click Execute
. The request should work, but if it doesn’t - check your JSON! Did you get all the quotes and commas right?
Show me a screenshot - credential offer
To confirm the issuance worked, scroll up on the Faber Swagger page to the issue-credential v2.0
section and execute the GET /issue-credential-2.0/records
endpoint. You should see a lot of information about the exchange just initiated.
Alice Receives Credential¶
Let’s look at it from Alice’s side. Alice's agent source code automatically handles credential offers by immediately responding with a credential request. Scroll back in the Alice terminal to where the credential issuance started. If you've followed the full script, that is just after where we used the basic message protocol to send text messages between Alice and Faber.
Alice's agent first received a notification of a Credential Offer, to which it responded with a Credential Request. Faber received the Credential Request and responded in turn with an Issue Credential message. Scroll down through the events from ACA-Py to the controller to see the notifications of those messages. Make sure you scroll all the way to the bottom of the terminal so you can continue with the process.
Show me a screenshot - issue credential
Alice Stores Credential in her Wallet¶
We can check (via Alice's Swagger interface) the issue credential status by hitting the GET /issue-credential-2.0/records
endpoint. Note that within the results, the cred_ex_record
just received has a state
of credential-received
, but not yet done
. Let's address that.
Show me a screenshot - check credential exchange status
First, we need the cred_ex_id
from the API call response above, or from the event in the terminal; use the endpoint POST /issue-credential-2.0/records/{cred_ex_id}/store
to tell Alice's ACA-Py instance to store the credential in agent storage (aka the Indy Wallet). Note that in the JSON for that endpoint we can provide a credential Id to store in the wallet by setting a value in the credential_id
string. A real controller might use the cred_ex_id
for that, or use something else that makes sense in the agent's business scenario (but the agent generates a random credential identifier by default).
Show me a screenshot - store credential
Now, in Alice’s swagger browser tab, find the credentials
section and within that, execute the GET /credentials
endpoint. There should be a list of credentials held by Alice, with just a single entry, the credential issued from the Faber agent. Note that the element referent
is the value of the credential_id
element used in other calls. referent
is the name returned in the indy-sdk
call to get the set of credentials for the wallet and ACA-Py code does not change it in the response.
Faber Receives Acknowledgment that the Credential was Received¶
On the Faber side, we can see by scanning back in the terminal that it receive events to notify that the credential was issued and accepted.
Show me Faber's event activity
Note that once the credential processing completed, Faber's agent deleted the credential exchange record from its wallet. This can be confirmed by executing the endpoint GET /issue-credential-2.0/records
Show me a screenshot
You’ve done it, issued a credential! w00t!
Issue Credential Notes¶
Those that know something about the Indy process for issuing a credential and the DIDComm Issue Credential
protocol know that there multiple steps to issuing credentials, a back and forth between the issuer and the holder to (at least) offer, request and issue the credential. All of those messages happened, but the two agents took care of those details rather than bothering the controller (you, in this case) with managing the back and forth.
- On the Faber agent side, this is because we used the
POST /issue-credential-2.0/send
administrative message, which handles the back and forth for the issuer automatically. We could have used the other/issue-credential-2.0/
endpoints to allow the controller to handle each step of the protocol. - On Alice's agent side, this is because the handler for the
issue_credential_v2_0
event always responds to credential offers with corresponding credential requests.
Bonus Points¶
If you would like to perform all of the issuance steps manually on the Faber agent side, use a sequence of the other /issue-credential-2.0/
messages. Use the GET /issue-credential-2.0/records
to both check the credential exchange state as you progress through the protocol and to find some of the data you’ll need in executing the sequence of requests.
The following table lists endpoints that you need to call ("REST service") and callbacks that your agent will receive ("callback") that your need to respond to. See the detailed API docs.
Protocol Step | Faber (Issuer) | Alice (Holder) | Notes |
---|---|---|---|
Send Credential Offer | POST /issue-credential-2.0/send-offer |
REST service | |
Receive Offer | callback | ||
Send Credential Request | POST /issue-credential-2.0/records/{cred_ex_id}/send-request |
REST service | |
Receive Request | callback | ||
Issue Credential | POST /issue-credential-2.0/records/{cred_ex_id}/issue |
REST service | |
Receive Credential | callback | ||
Store Credential | POST /issue-credential-2.0/records/{cred_ex_id}/store |
REST service | |
Receive Acknowledgement | callback | ||
Store Credential Id | application function |
Requesting/Presenting a Proof¶
Alice now has her Faber credential. Let’s have the Faber agent send a request for a presentation (a proof) using that credential. This should be pretty easy for you at this point.
Faber sends a Proof Request¶
From the Faber browser tab, get ready to execute the POST /present-proof-2.0/send-request
endpoint. After hitting Try it Now
, erase the data in the block labelled "Edit Value Model", replacing it with the text below. Once that is done, replace in the JSON each instance of cred_def_id
(there are four instances) and connection_id
with the values found using the same techniques we've used earlier in this tutorial. Both can be found by scrolling back a little in the Faber terminal, or you can execute API endpoints we've already covered. You can also change the value of the comment
item to whatever you want.
{
"comment": "This is a comment about the reason for the proof",
"connection_id": "e469e0f3-2b4d-4b12-9ac7-293f23e8a816",
"presentation_request": {
"indy": {
"name": "Proof of Education",
"version": "1.0",
"requested_attributes": {
"0_name_uuid": {
"name": "name",
"restrictions": [
{
"cred_def_id": "SsX9siFWXJyCAmXnHY514N:3:CL:8:faber.agent.degree_schema"
}
]
},
"0_date_uuid": {
"name": "date",
"restrictions": [
{
"cred_def_id": "SsX9siFWXJyCAmXnHY514N:3:CL:8:faber.agent.degree_schema"
}
]
},
"0_degree_uuid": {
"name": "degree",
"restrictions": [
{
"cred_def_id": "SsX9siFWXJyCAmXnHY514N:3:CL:8:faber.agent.degree_schema"
}
]
},
"0_self_attested_thing_uuid": {
"name": "self_attested_thing"
}
},
"requested_predicates": {
"0_age_GE_uuid": {
"name": "birthdate_dateint",
"p_type": "<=",
"p_value": 20030101,
"restrictions": [
{
"cred_def_id": "SsX9siFWXJyCAmXnHY514N:3:CL:8:faber.agent.degree_schema"
}
]
}
}
}
}
}
(Note that the birthdate requested above is used as an "age proof", the calculation is something like now() - years(18)
, and the presented birthdate must be on or before this date. You can see the calculation in action in the faber.py
demo code.)
Notice that the proof request is using a predicate to check if Alice is older than 18 without asking for her age. Not sure what this has to do with her education level! Click Execute
and cross your fingers. If the request fails check your JSON!
Show me a screenshot - send proof request
Alice - Responding to the Proof Request¶
As before, Alice receives a webhook event from her agent telling her she has received a Proof Request. In our scenario, the ACA-Py instance automatically selects a matching credential and responds with a Proof.
Show me Alice's event activity
In a real scenario, for example if Alice had a mobile agent on her smartphone, the agent would prompt Alice whether she wanted to respond or not.
Faber - Verifying the Proof¶
Note that in the response, the state is request-sent
. That is because when the HTTP response was generated (immediately after sending the request), Alice's agent had not yet responded to the request. We’ll have to do another request to verify the presentation worked. Copy the value of the pres_ex_id
field from the event in the Faber terminal and use it in executing the GET /present-proof-2.0/records/{pres_ex_id}
endpoint. That should return a result showing the state
as done
and verified
as true
. Proof positive!
You can see some of Faber's activity below:
Show me Faber's event activity
Present Proof Notes¶
As with the issue credential process, the agents handled some of the presentation steps without bothering the controller. In this case, Alice's agent processed the presentation request automatically through its handler for the present_proof_v2_0
event, and her wallet contained exactly one credential that satisfied the presentation-request from the Faber agent. Similarly, the Faber agent's handler for the event responds automatically and so on receipt of the presentation, it verified the presentation and updated the status accordingly.
Bonus Points¶
If you would like to perform all of the proof request/response steps manually, you can call all of the individual /present-proof-2.0
messages.
The following table lists endpoints that you need to call ("REST service") and callbacks that your agent will receive ("callback") that you need to respond to. See the detailed API docs.
Protocol Step | Faber (Verifier) | Alice (Holder/Prover) | Notes |
---|---|---|---|
Send Proof Request | POST /present-proof-2.0/send-request |
REST service | |
Receive Proof Request | callback (webhook) | ||
Find Credentials | GET /present-proof-2.0/records/{pres_ex_id}/credentials |
REST service | |
Select Credentials | application or user function | ||
Send Proof | POST /present-proof-2.0/records/{pres_ex_id}/send-presentation |
REST service | |
Receive Proof | callback (webhook) | ||
Validate Proof | POST /present-proof-2.0/records/{pres_ex_id}/verify-presentation |
REST service | |
Save Proof | application data |
Conclusion¶
That’s the OpenAPI-based tutorial. Feel free to play with the API and learn how it works. More importantly, as you implement a controller, use the OpenAPI user interface to test out the calls you will be using as you go. The list of API calls is grouped by protocol and if you are familiar with the protocols (Aries RFCs) the API call names should be pretty obvious.
One limitation of you being the controller is that you don't see the events from the agent that a controller program sees. For example, you, as Alice's agent, are not notified when Faber initiates the sending of a Credential. Some of those things show up in the terminal as messages, but others you just have to know have happened based on a successful API call.