This post is a follow up of part 5, where I explain how to create a schema, a credential definition and issue a credential using ACA-py. In the meantime ACA-py v0.6.0 is about to be released which features new endpoints for issuing credentials according to Aries RFC0453. Let’s take a look at the new endpoints and create clearer examples than in part 5.
Creating a schema and credential definition
The schema and credential definition parts have not changed since the last blog post, so I recommend reading the post to get some more details. However, let’s add some examples for using the endpoints here.
Creating a schema is straight-forward by posting to the /schemas
endpoint:
|
|
You can retrieve the schema using the /schemas/{id}
endpoint. The id
can be either the schema_id
or the seqNo
values that the POST
to /schemas
returned:
|
|
|
|
The last call, based on the seqNo
will come in useful later.
Now let’s create a credential definition based upon the just created schema:
|
|
As you can see, the request failed because I haven’t configured a tails server base url. This tails server is required in case you want to support revocation. You can read more about revocation and the tails server in part 6.
For now, let’s ignore revocation and create a credential definition without it.
|
|
You can retrieve the credential definition by using the /credential-definitions/{id}
endpoint, where the id
is the credential_definition_id
that the POST
to /credential-definitions
returned.
|
|
The interesting thing to note here is that a credential definition contains a schemaId
in the form of a seqNo
that we saw before. This schemaId
can be used the retrieve the schema using /schemas/{id}
endpoint as shown before.
You can use other peoples schemas to create a credential definition as well. You can find schemas at indyscan.io for the Sovrin ledgers, or at test.bcovrin.vonx.io, dev.bcovrin.vonx.io and prod.bcovrin.vonx.io for the BCovrin ledgers. Make sure your ACA-py is configured for the right ledger in case you want to use a schema from one of them. Check connecting ACA-py to hosted ledgers for more details.
With the credential definition set up, it’s time to issue credentials.
The Issue Credential dance
Just as with a tango, there are two parties involved when issuing a credential. There is the issuer and the holder.
There are three flows for issuing credentials, based on which party (issuer, holder) initiates the dance and with what. When you, as a holder, start the dance, you start with sending a proposal to the issuer (step 1). The proposal contains what you would like to receive from the issuer. Based on that the issuer can send an offer to the holder. When the issuer starts the dance, it starts with sending an offer to the holder (step 2). The holder can also start by directly sending a request to the issuer, thereby skipping the proposal and offer steps.
The flow for issuing credentials is:
- Holder sends a proposal to the issuer (issuer receives proposal)
- Issuer sends an offer to the holder based on the proposal (holder receives offer)
- Holder sends a request to the issuer (issuer receives request)
- Issuer sends credential to holder (holder receives credentials)
- Holder stores credential (holder sends acknowledge to issuer)
- Issuer receives acknowledge
These steps are the same as in the first version of issuing credentials, the only difference is that the names of the states the holder or issuer are in are different.
Issuing a credential
For the following examples to work for you, make sure you have two ACA-py instances running at the same time on different ports, and with different wallets. You can check out connecting using DIDComm Exchange to see how to set up to instances and create a connection between them. In these examples I assume localhost:11000
to be an issuer, and localhost:11001
to be a holder.
When the holder starts with sending a proposal, it can use the /issue-credential-2.0/send-proposal
endpoint.
|
|
The holder can specify any (or none) of the fields in filter
to let the issuer know what he is looking for. The fields in filter.indy
are not required, but the filter.dif
and filter.indy
objects are required, so you can leave them empty.
The connection_id
is different for the issuer and the holder, so please make sure you use the connection_id
that the holder uses to identify the issuer.
The smallest proposal I could send is:
|
|
The result you get back is a Credential Exchange Record. It is a record that contains the state of the credential dance. These records are stored in ACA-py and can be retrieved using the /issue-credentials-2.0/records/{id}
endpoint where the id
is the cred_ex_id
in the result.
The issuer receives the proposal and can respond with an offer using the /issue-credential-2.0/records/{id}/send-offer
endpoint. Note here that the id
that the issuer uses is different from the cred_ex_id
that the holder got. Each ACA-py instance creates its own identifiers.
If the holder specified fields in filter.indy
, the issuer will try to find a credential definition that matches those criteria and send an offer based on it. If no credential definition can be found, the issuer will be greeted with an error:
|
|
In that case, the holder will receive a problem report which can only be retrieved as a webhook.
It could also be that the issuer does have a matching credential definition, but that the attributes do not match the requested attributes. In that case the error will be:
|
|
In case the issuer does have a suitable credential definition, the request and response will look like:
|
|
The result is again the updated Credential Exchange Record.
After the offer has been received by the holder, the holder can send a request for a credential.
|
|
Then the issuer can issue the credential.
|
|
And finally the holder can store the received credential.
|
|
The holder can retrieve the stored credential by using:
|
|
If you made it this far: congratulations! Please take a break and don’t forget to hydrate.
This flow is very similar, but instead starts with the issuer offering a credential to the holder.
|
|
After this offer, the flow continues with the holder responding with a request.
This flow is described in Aries RFC0453, but there is no endpoint for a holder to start with sending a request independent of a Credential Exchange Record in ACA-py.
Automating the issue credential flow
There is one last endpoint that we haven’t discussed, which is /issue-credential-2.0/send
. Which is the same as /issue-credential-2.0/send-offer
from the issuer viewpoint, but which sets the flag auto_offer
and auto_issue
to true. If the holder automatically accepts offers and turns them into requests, then this would completely automate the issuing of credentials.
Development and debugging
For development purposes you can automate a large part of the flow. To make debugging easier, you can provide --debug-credentials
to ACA-py which will log information in the console.
The flow of issuing credentials can be automated using:
--auto-respond-credential-proposal
--auto-respond-credential-offer
--auto-respond-credential-request
--auto-store-credential
If you have read this blog post so far, then these command line options should speak for themselves. Of course these are for development and debugging, so never enable these for production usage.
When you create a credential proposal or a credential offer, the credential exchange record will be automatically removed after the issuing of the credential has completed. The automatic removal can be disabled by providing --preserve-exchange-records
to ACA-py.
Connection-less issuing of credentials
There is one last item to discuss, which is the issuing of credentials without having a prior connection. You can imagine scanning a QR-code somewhere that will automatically add a credential to your wallet.
This functionality is not available yet, but a start of the implementation can be found in ACA-py already.
Conclusion
The issuing of credentials with the v2.0 endpoints is straight-forward and doesn’t require too many intricate details.
If this blog post helped you, or if you have any questions, please feel free to reach out to me.