Credential Flows

Urls#

Mobile flow issue#

Example integration tests for mobile flow. Form a connection with the entity

POST /v2/{ENTITY}/api/connection Example ENTITY values: kiva, fsp, ncra, demo.

Authorization header: Auth0 Bearer token (Note if the future we’ll have specific user’s associated with specific entities so you can’t say use a ncra token to access the kiva controller) No request body

Response body:

{
"connection_id": "c8a1c517-fa5c-47c1-802c-7484e58cd8b7",
"invitation": {
"@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation",
"@id": "963569fc-e7fc-4823-a88b-9adee8d07dfc",
"serviceEndpoint": "https://sandbox-gateway.protocol-prod.kiva.org/v1/router/demo-agent",
"recipientKeys": [
"A3MkqnnBccokmHAtZWKGPBjGrBWWMgnkXNezfH4gmxW3"
],
"label": "demo-agent"
}
}

Note that if you base64 encode the “invitation” field you can display it as a QR code. You can use the “conneciton_id” field to check in on the connection. Mobile app accepts connection (action on the user’s side)

Admin tool can monitor the status of the connection#

GET /v2/{ENTITY}/api/connection/{CONNECTION_ID) Authorization header Auth0 Bearer token Response body:

{
"connection_id": "c8a1c517-fa5c-47c1-802c-7484e58cd8b7",
"routing_state": "none",
"initiator": "self",
"invitation_mode": "once",
"updated_at": "2020-09-23 12:18:41.377422Z",
"state": "invitation",
"invitation_key": "A3MkqnnBccokmHAtZWKGPBjGrBWWMgnkXNezfH4gmxW3",
"accept": "manual",
"created_at": "2020-09-23 12:18:41.377422Z"
}

You can monitor the response body checking the state field. It’s ready when it switches to active or response.

Once connection is ready issue credential#

POST /v2/{ENTITY}/api/issue Authorization header

Request body:

{
“profile”: “employee.cred.def.json”,
“connectionId”: “CONNECTION_ID”
“entityData”: {
"firstName":"First",
"lastName":"Last",
"companyEmail":"[email protected]",
"currentTitle":"Engineer",
"team":"Engineering",
"hireDate":"1/1/2015",
"officeLocation":"Cloud",
"photo~attach":"PHOTO_BASE64_ENCODED",
"type":"Staff",
"endDate":"",
}
}

Response:

{ created_at: '2020-09-23 12:40:11.161193Z',
auto_offer: false,
trace: false,
auto_remove: true,
credential_exchange_id: '7ee9018b-6847-42ce-b1b9-2e455ae9b008',
updated_at: '2020-09-23 12:40:11.161193Z',
role: 'issuer',
schema_id: 'McP1XxbNgbWL1ZNgQQB9YL:2:Identity:1.0.27',
initiator: 'self',
connection_id: '7c6771c3-9610-474d-ab0a-c99df2d94990',
thread_id: 'f26c46bf-934d-4c30-b7c8-cd1b18519534',
auto_issue: true,
state: 'offer_sent',
credential_definition_id: '2MEMk5sErXBrXo82iAQdVE:3:CL:13:tag'
}

In the response the important fields are credential_exchange_id which will be used below, and the state. The mobile user needs to accept the credential, you can check the status.

Check Credential Status#

GET /v2/{ENTITY}/api/issue/{CREDENTIAL_EXCHANGE_ID}

This is a bit confusing but while you’re waiting for the mobile app to accept you will see the state, but as soon as the credential is accepted there’s not more “credential exchange” so there’s just a 400 response. This is what aries does under the hood, if it’s confusing I can see if there’s a way around it.

Response body:

{
code: 'ValidationException',
message: 'Request failed with status code 404',
httpStatus: 400
}

Mobile flow verify#

If you want to use the FSP controller it’s POST /v2/{ENTITY}/verify Authorization header Request body:

{
"connectionId":"c8a1c517-fa5c-47c1-802c-7484e58cd8b7",
"profile":"employee.proof.request.json"
}

Response:

{
"updated_at":"2020-09-23 12:44:54.138895Z",
"role":"verifier",
"auto_present": false,
"created_at":"2020-09-23 12:44:54.138895Z",
"initiator":"self",
"state":"request_sent",
"connection_id":"1e34fe1f-9434-45e0-904f-686a30671ec5",
"trace": false,
"thread_id":"3dc6f4d3-c27a-414b-9450-f235ab436596",
"presentation_exchange_id":"38d67fe8-9a51-428a-8787-6d122ce96805"
}

In the response the important fields are “presentation_exchange_id” which will be used below, and the “state”

GET v2/{ENTITY}/api/verify/{PRESENTATION_EXCHANGE_ID} Authorization header Response:

{
"presentation":{
"requested_proof":{
"revealed_attrs":{
"photo~attach":{
"sub_proof_index":0,
"raw":"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAAA=",
"encoded":"72255350251601573190648834028700343405774108340563905202816779778236665881252"
},
"firstName":{
"sub_proof_index":0,
"raw":"First",
"encoded":"72966909777722199857565909645400774050677128824761866158421944618652143161759"
},
"lastName":{
"sub_proof_index":0,
"raw":"Last",
"encoded":"106560414708951830725465891294489754012998847785380950406668079680645656730709"
},
"nationalId":{
"sub_proof_index":0,
"raw":"ABC123",
"encoded":"101655084385305756928825370050774922903811787236607150592864376216915705343114"
},
"birthDate":{
"sub_proof_index":0,
"raw":"1975-10-10 00:00:00",
"encoded":"101132328746978669154321210273143344150717106155117004284073345241846824577158"
}
},
},
},
"created_at":"2020-10-05 16:45:47.578144Z",
"state":"verified",
"verified":"true",
"thread_id":"78151bfb-84b7-4f7c-95ab-8c463c5aa5fd",
"presentation_exchange_id":"5c7b6c2c-3937-477a-bd30-0e4e2d7d2045",
"initiator":"self",
"trace":false,
"auto_present":false,
"connection_id":"4fbaf796-1abc-4678-b564-5355d924c936",
"updated_at":"2020-10-05 16:45:49.316602Z"
}

In the response the important fields are that the “state” attribute is verified and the “verified” attribute is “true”, and the values are inside Presentation.requested_proof.revealed_attrs.{attribute_name}.raw. NOTE: if the state is verified but the verified attribute is false that means the credential has been revoked

Guardianship flow:#

Example integration tests for guardianship flow.

Onboard into guardianship#

POST v2/{ENTITY}/api/guardian/onboard Authorization header Request body:

{
"profile":"employee.cred.def.json",
"guardianData":[
{
"pluginType":"SMS_OTP",
"filters":{
“govId1”:"[email protected]",
“govId2”:"[email protected]"
},
"params":{
"phoneNumber": “+16282185460”
}
}
],
"entityData":{
"firstName":"First",
"lastName":"Last",
"companyEmail":"[email protected]",
"currentTitle":"Engineer",
"team":"Engineering",
"hireDate":"1/1/2015",
"officeLocation":"Cloud",
"photo~attach":"89504e470d0a1a0a0000000d49484452000000010000000108060000001f15c4890000000d4944415478da6364f8ffbf1e000584027fc25b1e2a00000000",
"type":"Staff",
"endDate":""
}
}

Response:

{
"agentId":"AGENT_ID"
}

In the future we will start returning the credential id so the front end can keep track of how to revoke it. But for now the response isn’t important.

Verifying in guardianship to get SMS OTP#

POST v2/{ENTITY}/api/guardian/verify Authorization header

Request body:

{
"profile":"employee.proof.request.json",
"guardianData":{
"pluginType":"SMS_OTP",
"filters":{
“govId1”:"[email protected]"
},
"params":{
"phoneNumber": “+16282185460”
}
}
}

Response:

{
"status":"sent"
}

The response isn’t important, just the SMS sending

Verifying in guardianship withSMS OTP#

POST v2/{ENTITY}/api/guardian/verify Authorization header Request body:

{
"profile":"employee.proof.request.json",
"guardianData":{
"pluginType":"SMS_OTP",
"filters":{
“govId1”:"[email protected]"
},
"params":{
"otp": “185460”
}
}
}

Response:

{
"firstName":"First",
"lastName":"Last",
"companyEmail":"[email protected]",
"currentTitle":"Engineer",
"team":"Engineering",
"hireDate":"1/1/2015",
"officeLocation":"Cloud",
"photo~attach":"89504e470d0a1a0a0000000d49484452000000010000000108060000001f15c4890000000d4944415478da6364f8ffbf1e000584027fc25b1e2a00000000",
"type":"Staff",
"endDate":""
}

The response will contain all the requested fields is the proof request went through

Fetch all stored admin credential data#

GET v2/{ENTITY}/api/records Response:

[
{
"entityData": {
"firstName": "First",
"lastName": "Last",
"companyEmail": "[email protected]",
"hireDate": "1/1/2015",
"currentTitle": "Engineer",
"team": "Engineering",
"officeLocation": "Cloud",
"photo~attach": "89504e470d0a1a0a0000000d49484452000000010000000108060000001f15c4890000000d4944415478da6364f8ffbf1e000584027fc25b1e2a00000000",
"type": "Intern",
"endDate": "1605043300",
"phoneNumber": "+16282185460"
},
"connection_id": "c56f336c-6774-4542-8dc5-904fd3733e48",
"schema_id": "Th7MpTaRZVRYnPiabds81Y:2:Employee:1.0.3",
"credential_definition_id": "Th7MpTaRZVRYnPiabds81Y:3:CL:16:tag3",
"credential_exchange_id": "0d487a71-f8c6-4105-b1f1-31f4fd6281d0",
"state": "request_received",
"created_at": "2021-01-11 13:48:18.657209Z",
"thread_id": "1dff4153-3cf6-48ef-b8fe-479f31a7ac03",
"updated_at": "2021-01-11 13:48:19.341783Z",
"revocation_id": null,
"credential_id": null,
"revoc_reg_id": null
}
]

This returns an array of all issued credentials. The entity data (ie employee data) is under the “entityData” attribute. The state can have the values: proposal_sent, proposal_recieved, offer_sent, offer_received, request_sent, request_received, credential_issued, credential_received, credential_acked

Revoke a credential#

POST v2/{ENTITY}/api/revoke Authorization header Request body: OLD

{
"cred_rev_id”: ”revocation_id_from_above”,
“rev_reg_id”: “revoc_reg_id_from_above”
}

NEW

{
“cred_ex_id”: “credential_exchange_id_from_above”
}

Response:

{
}

The response on success doesn't contain anything important. If there is a failure then it will throw an exception (ie a 400 or 500 code)

Get all profiles, including proof requests#

GET v2/{ENTITY}/api/profiles Authorization header Request body:

Response:

[
{...profile data},
]