Aries playground

Introduction

The Aries playground environment is setup for developers to perform API call flows during a verified data exchange process using Hyperledger Indy as the distributed ledger registry and Hyperldger Aries agent as the client application.

For developers to try out the Aries agent, iGrant.io Aries agents are pointed to the indy ledger hosted at https://indy.igrant.io. This can be changed to any ledger of choice by updating GENESIS_URL environment variable in docker-compose.yml file.

Reference system

In order to make it easier to comprehend SSI concepts, a reference system is defined. In this reference system, we have the individual (Alice) holding her health data in her wallet app (Data4Life) issued by a Health Test Center. This health data could be used by Alice to prove her health status to a travel company, for example. The legal entity ensures that the test center follows the schema and governance.

Use case scenario

Alice is a travel vlogger. Her next destination is Dubai but, due to the Covid pandemic, she has to prove to a travel agent, that her result is negative so that she will be allowed to travel.

Alice listed out the things she has to do as illustrated in the figure below: Use case diagram

  1. Visit a Covid test center and identifies herself
    • Test center should follow a schema defined by a legal entity to issue the certificate. Refer Prepare public schema
  2. Test center and alice creates a connection to exchange information. Refer Establish connection between Issuer and Holder
  3. The Test center will test alice's samples and issues a certificate. Refer Issue verifiable credential
  4. Store the issued credentials to her wallet. Refer Negotiate verifiable credential
  5. Present proof to travel agent. Refer Verfiier proof workflow

Prepare reference system environment

In order to test with the aries playground, the first step is to clone the repository.

git clone https://github.com/decentralised-dataexchange/aries-playground.git

The test environment is collection of docker containers and uses docker-compose for managing the conatiners.

cd aries-playground
docker-compose up

Selection of ledger is set in docker-compose.yml file. While this demo uses iGrant.io Indy Sandbox it is easy to switch to a Sovrin ledger or any other indy ledger (as of today). Replace the GENESIS_URL row in docker-compose.yml file with this line in order to use Sovrin.

GENESIS_URL: https://github.com/sovrin-foundation/sovrin/blob/master/sovrin/pool_transactions_sandbox_genesis

As an example, you can use this command for setting up docker-compose with Sovrin.

docker-compose -f docker-compose-sovrin.yml up

Once the reference system is up and running you can start 3 terminals and monitor their logs by executing the docker container logs given below:

AgentSwagger API endpoint admin UI
Test Centerdocker exec -it test-center.webhook tail -f demo.log
Alice (Data4Life User)docker exec -it data4Life-user.webhook tail -f demo.log
Travel Companydocker exec -it travel-company.webhook tail -f demo.log

In your browser you can start three tabs to execute the APIs using swagger. To test any request click "Try it out" and "Execute" with any required input. Note for your convenience swagger links also provided with each request.

AgentSwagger API endpoint admin UI
Test Centertest-center.swagger.localhost
Alice (Data4Life User)data4life-user.swagger.localhost
Travel Companytravel-company.swagger.localhost

Verifiable Credential work-flow

The work flow shows 4 steps when creating a verifiable credential (or agreement) as depicted in below diagram.

  1. Prepare public schema including registering issuer DID
  2. Establish connection between Issuer and Holder (Alice)
  3. Issue credential (Automated Flow) represented as an automated flow
  4. Issue credential (Manual flow) with a manual flow

http://localhost:3000/ssi/ssi-apg#issue-credential-automated-flow

The last step (4) allows the Holder (Alice) to review the offer from the Issuer before accept it. Note step 3 is automated to skip the negotiation. In order to allow to negotiate The file startup.sh in folder aries-playground/cloud/agent needs to be renamed for startup_temp.sh.

For further details on the diagram refer to Hyperledger Aries RFC 0167 Consent Lifecycle

Prepare public schema

Before creating schema it is necessary to create a DID.

Create DID in a wallet

The steps below to create DID in a wallet is a pre-requesite for any agent before acquiring agent roles.

  1. Create a local DID for the agent using POST ​/wallet​/did​/create. This generates the DID and verification key localhost endpoint

    Response body

    {
    "result": {
    "did": "TqQDq6vrPoW7hsf4JvqPaR",
    "verkey": "FdHtGAaod2xoZ7wJfXU4yCEEuTt6hSf38EpdKkX4UHgK",
    "posture": "wallet_only"
    }
    }
  2. For organisations, after creating the local DID, you need to register it with ledger at indy.igrant.io as shown below with the DID and verification key

After registering with the Indy ledger, call POST ​/wallet​/did​/public(localhost endpoint).

Optional: To register with Sovrin Staging network use this Sovrin Indy network link and complete details as shown in diagram. Important that docker-compose.yml has been updated prior to starting docker-compose.

note

In case the ledger requires a transaction author agreement (TAA) to be accepted before writing to the ledger, make sure to execute the APIs for the same.

For e.g.

Endpoint for fetching the TAA: GET ​/ledger​/taa

The response lists the available acceptance mechanisms for this particular ledger. E.g. "product_eula", "click_agreement" etc.

Endpoint for accepting the TAA by providing one of the acceptance mechanism: POST ​/ledger/taa/accept

Assign DID as public

This is used to let the wallet know that the DID is public by using POST /wallet/did/public

Create Schema

To make it easier, we have used the Test Center Agent to register the schema. Ideally this is defined by a legal entity or a standardisation body.

You can execute the schema definiton API to register the schema in the ledger.(localhost endpoint)

Sends a schema to the ledger with the API POST: ​/schemas with the json body as given:

{
"schema_version": "1.0",
"schema_name": "Covid-19 Test Results",
"attributes": [
"testResult",
"testDate"
]
}

Response body

{
"schema_id": "3jbQ51y4RwCb4U45rkeKY1:2:Covid-19 Test Results:1.0",
"schema": {
"ver": "1.0",
"id": "3jbQ51y4RwCb4U45rkeKY1:2:Covid-19 Test Results:1.0",
"name": "Covid-19 Test Results",
"version": "1.0",
"attrNames": [
"testResult",
"testDate"
],
"seqNo": 370
}
}

Create credential definition by the Test Center

Prior to making any verifiable credential a credential definition has to be created which is means of performing proofs against the credential. POST method /credential-definitions stores a credential definition against the ledger. (localhost endpoint)

Add following body to the request.

{
"revocation_registry_size": 4,
"support_revocation": false,
"schema_id": "PWr9PACurgoMwowC5Bx8RD:2:Covid-19 Test Results:1.0",
"tag": "default"
}

Response body

{
"credential_definition_id": "3jbQ51y4RwCb4U45rkeKY1:3:CL:370:default"
}

Ready to issue verifiable credentials.

Establish connection between Issuer and Holder

Here, the Test Center and Data4Life-User agents establishes connection with each other. The following is the API call sequence:

  1. Create a new invitation (by Test Center)

    Test Center Agent: POST ​/connections​/create-invitation. Use default values.(localhost endpoint)

    This generates the connection_id and invitation.

    Response body

    {
    "connection_id": "521f106f-b10e-472a-b7ad-219a812e31e1",
    "invitation": {
    "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation",
    "@id": "c3055fe4-9bca-4585-918c-67bbdb07bc10",
    "label": "Test-Center",
    "serviceEndpoint": "http://test-center.localhost",
    "recipientKeys": [
    "7myawP9fdA1y237bi3Xdfukuv2rX8YRNzt2s2xEdpJh3"
    ]
    },
    "invitation_url": "http://test-center.localhost?c_i=eyJAdHlwZSI6ICJkaWQ6c292OkJ6Q2JzTlloTXJqSGlxWkRUVUFTSGc7c3BlYy9jb25uZWN0aW9ucy8xLjAvaW52aXRhdGlvbiIsICJAaWQiOiAiYzMwNTVmZTQtOWJjYS00NTg1LTkxOGMtNjdiYmRiMDdiYzEwIiwgImxhYmVsIjogIlRlc3QtQ2VudGVyIiwgInNlcnZpY2VFbmRwb2ludCI6ICJodHRwOi8vdGVzdC1jZW50ZXIubG9jYWxob3N0IiwgInJlY2lwaWVudEtleXMiOiBbIjdteWF3UDlmZEExeTIzN2JpM1hkZnVrdXYyclg4WVJOenQyczJ4RWRwSmgzIl19"
    }
  2. Receive connection invitation by Data4Life-User (Alice)

    Alice Agent: POST ​/connections​/receive-invitation with the invitation extracted from the json body generated in step 1. (localhost endpoint)

    Response body

    {
    "connection_id": "54b9fdd1-99b4-4ecf-a388-5cb04ebffa9f",
    "initiator": "external",
    "invitation_key": "7myawP9fdA1y237bi3Xdfukuv2rX8YRNzt2s2xEdpJh3",
    "state": "invitation",
    "created_at": "2020-12-23 15:49:24.936970Z",
    "routing_state": "none",
    "invitation_mode": "once",
    "their_label": "Test-Center",
    "updated_at": "2020-12-23 15:49:24.936970Z",
    "accept": "manual"
    }
  3. Accept a received connection invitation from Test Center by the Data4Life-user (Alice)

    Alice Agent: POST ​/connections​/{conn_id}​/accept-invitation passing the connection_id as input. (localhost endpoint)

    Response body

    {
    "connection_id": "54b9fdd1-99b4-4ecf-a388-5cb04ebffa9f",
    "initiator": "external",
    "invitation_key": "7myawP9fdA1y237bi3Xdfukuv2rX8YRNzt2s2xEdpJh3",
    "state": "request",
    "created_at": "2020-12-23 15:49:24.936970Z",
    "request_id": "2bbcb177-83b1-4f6a-819c-d3b585c2b80d",
    "routing_state": "none",
    "my_did": "TiXWip9b6KCYdmfa4MJRng",
    "invitation_mode": "once",
    "their_label": "Test-Center",
    "updated_at": "2020-12-23 15:55:26.730952Z",
    "accept": "manual"
    }
  4. Check status

    Check both Test Center Agent and Alice Agent by GET /connections and both are in Active status.

    Test Center agent status check (localhost endpoint)

    Alice agent status check (localhost endpoint)

After the secured connection is established between the two agents, the Test Center can now issue credential to Data4Life user (Alice) with her personal data. Alice then is able to see the credential in her data wallet.

Issue credential (Automated Flow)

A credential is issued by the Test Center based on a standard schema earlier defined (e.g. by the legal entity). Test Center now issues the credential to the holder Alice (Data4Life-user).

Test Center Agent: POST ​/issue-credential​/send with auto_remove set to FALSE. (localhost endpoint)

note

In the json body below, the schema_issuer_did and the credential issuer_did used are the same. In reality, they could be different as schema definitions are owned by a legal entity.

{
"schema_name": "Covid-19 Test Results",
"schema_version": "1.0",
"cred_def_id": "PWr9PACurgoMwowC5Bx8RD:3:CL:19:default",
"auto_remove": false,
"comment": "string",
"connection_id": "a8ea680f-0704-4327-99b8-02e5e3d03ea4",
"trace": false,
"schema_issuer_did": "PWr9PACurgoMwowC5Bx8RD",
"schema_id": "PWr9PACurgoMwowC5Bx8RD:2:Covid-19 Test Results:1.0",
"issuer_did": "PWr9PACurgoMwowC5Bx8RD",
"credential_proposal": {
"@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/credential-preview",
"attributes": [
{
"name": "testDate",
"mime-type": "text/plain",
"value": "22-Aug-2020"
},
{
"name": "testResult",
"mime-type": "text/plain",
"value": "negative"
}
]
}
}

In the case of manual flow, Data4Life user (Alice), the credential is automatically stored into the wallet. In the case of manual flow, this need to be done explicitly.

Data4Life user (Alice) can fetch the credentials from the wallet by GET /credentials

Issue credential (Manual Flow)

The following steps describes the manual flow.

  1. Issuer (Test Center) sends a offer with the result of the test.

    Test Center Agent: POST/issue-credential​/send-offer (localhost endpoint)

    Note: In request body auto issue and auto remove should be false

    {
    "cred_def_id": "WgWxqztrNooG92RXvxSTWv:3:CL:20:tag",
    "credential_preview": {
    "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/issue-credential/1.0/credential-preview",
    "attributes": [
    {
    "name": "testDate",
    "mime-type": "text/plain",
    "value": "22-Aug-2020"
    },
    {
    "name": "testResult",
    "mime-type": "text/plain",
    "value": "negative"
    }
    ]
    },
    "connection_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "auto_issue": false,
    "comment": "string",
    "trace": false,
    "auto_remove": false
    }

    Now the state will be offer_sent for Issuer (Test Center)

    Note: GET /issue-credential​/records to view the state. (localhost endpoint)

  2. Alice will call GET /issue-credential​/records

    The state will be offer_received and from this response copy the credential exchange id. (localhost endpoint)

  3. Alice will send request to issue the credential using credential exchange id

    Alice Agent: POST /issue-credential​/records​/{cred_ex_id}​/send-request (localhost endpoint)

    Now the state will be request_sent for Alice

  4. Issuer (Test Center) will call GET/issue-credential​/records

    The state will be request_received and from this response copy the credential exchange id. (localhost endpoint)

  5. Issuer (Test Center) issues the certificate using credential exchange id

    Test Center Agent: POST /issue-credential​/records​/{cred_ex_id}​/issue (localhost endpoint)

    Now, the credential will be issued to Alice and state will change to credential_issued

  6. Stores credential into a personal wallet

    Personal wallet here is provided by Data4Life app. In the case of automated flow, the credential is automatically stored into the wallet. In the case of manual flow, this need to be done explicitly.

    For manual flow, Alice, the Data4Life-User, now stores the received credentials

    Alice Agent: POST ​/issue-credential​/records​/{cred_ex_id}​/store (localhost endpoint)

    Data4Life user can fetch the credentials from the wallet by GET /credentials

Verifier/Proof work-flow

This is from the Holder app (Data4Life) to Verifier (Travel Company)

Before any communication happens between Alice (Data4Life-User) and the verifier, a secured connection is established between two agents. After that Travel Company issues a proof request to Alice, showing what type of proof is needed to qualify in order for Alice to travel using the Covid-19 test result. Alice will build the proof based on the credential in her Data4Life wallet. Alice then sends the proof to the travel company which will observe the result. The proof response can be done automatically or through manual input. In order to run manual modify startup.sh file as described in section Work flow.

The following diagram provides an overview of all the steps taking place under the hood. For further details refer to Hyperledger Aries RFC 0167 Consent Lifecycle

The following sections are broken down into the following areas:

  1. Establish connection between Verifier and Holder
  2. Automated Holder response
  3. Manual Holder response

Establish connection between Verifier and Holder

Establishing connection is identical to the flow in Issuer and holder as described in section Establish connection between Issuer and Holder

  1. Establish connection

    Travel Company Agent: POST /connections/create-invitation, from the response get the invitation object (from { to }) as shown earlier during the connection between Test Center and Alice. (localhost endpoint)

    Alice Agent: POST /connections/receive-invitation with the invitation object. (localhost endpoint)

  2. Accept a stored connection invitation by Alice (Data4Life user)

    Alice Agent: POST ​/connections​/{conn_id}​/accept-invitation passing the connection_id as input. (localhost endpoint)

    Check both Travel Company Agent and Alice Agent by GET /connections and both are in Active status

    After the secured connection is established between the two agents, the Travel Center request a proof from Alice as per the credenital defintion. The following steps cover that flow.

Make a proof request

In this demo, the Proof request details the Test Center is asking for is

testResult
testDate

The requested attributes follows the Credential Definition specified by the ID

Travel Center Agent: POST /present-proof/send-request is called with the following payload. (localhost endpoint)

The body in the request shall have the following details. Modify the connection_id and cred_def_id (For e.g. the Travel company can insist to reuse the same credential definition ID as issued by the Test Center).

{
"connection_id": "a8ea680f-0704-4327-99b8-02e5e3d03ea4",
"comment": "Ready to travel",
"proof_request": {
"name": "Proof of COVID19 Negative",
"version": "1.0",
"requested_attributes": {
"0_testresult_uuid": {
"name": "testResult",
"restrictions": [
{
"cred_def_id": "PWr9PACurgoMwowC5Bx8RD:3:CL:19:default"
}
]
},
"0_testdate_uuid": {
"name": "testDate",
"restrictions": [
{
"cred_def_id": "PWr9PACurgoMwowC5Bx8RD:3:CL:19:default"
}
]
}
},
"requested_predicates": {}
}
}

Using the requested_predicates, you can do some assertions, example, the testDate shall be less than 2 days from today.

In the webhook interceptor, you can view the sequence of events happening:

  • receiving the proof request
  • checking credentials
  • generating proof
  • sending proof to Travel Company

From Travel Company, we can use GET /present-proof/records to see the proof sent by Alice. The presentation_exchange_id is the identifier of the presentation proof and state will tell you the current status of the presented proof. (localhost endpoint)

Send proof and verify

Instead of an automatic response from the Holder the proof can be manually entered.

  1. Holder (Alice) has to get the presentation exchange id
    Alice Agent: GET /present-proof/records (localhost endpoint)

  2. Holder (Alice) will send the presentation

    Alice Agent: POST ​/present-proof​/records​/{pres_ex_id}​/send-presentation is called with the following payload. (localhost endpoint)

    The body in the request shall have the following details. The pres_ex_id is copied from previous step.

    note

    The cred_id below is the referent value in the holders wallet by using the endpoint: GET /credentials. Also the requested_attribute fields keys should be same as in the body of POST /present-proof/send-request.

    {
    "requested_predicates": {
    },
    "trace": false,
    "self_attested_attributes": {
    },
    "requested_attributes": {
    "0_testresult_uuid": {
    "cred_id": "b5df5eac-047f-4ad0-98cc-7e6138a2f339",
    "revealed": true
    },
    "0_testdate_uuid": {
    "cred_id": "b5df5eac-047f-4ad0-98cc-7e6138a2f339",
    "revealed": true
    }
    }
    }
  3. Finally Travel Company use POST /present-proof/{pres-ex-id}/verify-presentation to see Alice’s proof presentation. (localhost endpoint)

References

  1. Setting up indy network and agents
  2. Hyperledger Aries RFC 0167 Consent Lifecycle