Run Credential Verify using a Callback

This tutorial will allow you to perform a Credential Verify flow and obtain important information from the Presentation including all the claims, the Subject Identifiers and whether the Credential has been fully verified or not.

This guide intends to show the raw workings of the Presentation Request during a Verify flow, some orchestration of events is required and the exact integration patterns with your infrastructure will need to be carefully designed.

Pre-Requisites

You need access to the MATTR Platform APIs. If you’re experiencing any difficulties, contact us.

In order to complete this tutorial you will need the following:

Sample App

If you prefer a hands-on approach to getting this working the Verify Credentials using Presentation Request Callbacks will step you through creating a simple Node.js Express server to orchestrate many of the steps below.

Invoke the Presentation Request

The Create a Presentation Request should be called every time a Presentation is requested.

{
"challenge": "GW8FGpP6jhFrl37yQZIM6w",
"did": "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5",
"templateId": "f95e71b0-9bdf-11ea-aec9-3b5c35fc28c8",
"callbackUrl": "http://71df02cc96e5.ngrok.io/callback",
"expiresTime": 1638836401000
}

challenge is a unique identifier per transaction, this can be used as an interaction id.

The did must be available on the tenant and have a key type that is able to be used for messaging (e.g. of a ed25519 key type).

Provide the ID of the templateId that exists on the tenant, this will define the response of the Presentation Request.

The callbackUrl is the exact path that you want the Platform to message once it has received and processed the Presentation from the Mobile Wallet holder, it will be in the form of a JSON body.

expiresTime is optional, with a default of 5 mins, once the time (in Unix Epoch time in milliseconds) is reached the Presentation corresponding to the challengeId will not be accepted by the Platform. For the purpose of testing you may wish to extend this out.

Response

{
"id": "38d9ca70-8b61-48c3-9efc-b67d97410743",
"callbackUrl": "https://c44077c6bd05.ngrok.io/callback",
"request": {
"id": "38d9ca70-8b61-48c3-9efc-b67d97410743",
"type": "https://mattr.global/schemas/verifiable-presentation/request/QueryByExample",
"from": "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5",
"created_time": 1607300517863,
"expires_time": 1638836401000,
"reply_url": "https://tenant.platform.mattr.global/v1/presentations/response",
"reply_to": [
"did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5"
],
"body": {
"id": "5770506c-9ce9-4d54-b295-68ad2bb12a4c",
"name": "certificate-presentation",
"domain": "tenant.platform.mattr.global",
"query": [
{
"type": "QueryByExample",
"credentialQuery": [
{
"reason": "Please provide your certificate.",
"example": {
"type": "Course",
"@context": [
"https://schema.org"
],
"trustedIssuer": [
{
"issuer": "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5",
"required": true
}
]
},
"required": true
}
]
}
],
"challenge": "GW8FGpP6jhFrl37yQZIM6w"
}
}
}

The id and callback are used on the Platform, the body of the request payload is required by the Mobile Wallet client.

Sign and Encode the Presentation Request body

In order for the Mobile Wallet to have assurance about the Presentation Request, it must be signed using the authentication key from the Verifier DID.

The Create a JWS with a DID endpoint is available for this purpose.

First find the authentication key from the same Verifier DID using in the Presentation Request using the Resolve a DID endpoint. This also known as a DIDUrl.

Request

https://tenant.platform.mattr.global/v1/dids/did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5

Response snippet from the didDocument

{
"didDocument": {
"@context": "https://w3.org/ns/did/v1",
"id": "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5#z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5",
"authentication": [
"did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5#z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5#z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5#z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5"
]
}}

Request

Use the didUrl and copy the body from the request field from the Presentation Request response to the body of the payload.

{
"didUrl": "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5#z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5",
"payload": {
"id": "38d9ca70-8b61-48c3-9efc-b67d97410743",
"type": "https://mattr.global/schemas/verifiable-presentation/request/QueryByExample",
"from": "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5",
"created_time": 1607300517863,
"expires_time": 1638836401000,
"reply_url": "https://tenant.platform.mattr.global/v1/presentations/response",
"reply_to": [
"did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5"
],
"body": {
"id": "5770506c-9ce9-4d54-b295-68ad2bb12a4c",
"name": "certificate-presentation",
"domain": "tenant.platform.mattr.global",
"query": [
{
"type": "QueryByExample",
"credentialQuery": [
{
"reason": "Please provide your certificate.",
"example": {
"type": "Course",
"@context": [
"https://schema.org"
],
"trustedIssuer": [
{
"issuer": "did:key:z6MkjBWPPa1njEKygyr3LR3pRKkqv714vyTkfnUdP6ToFSH5",
"required": true
}
]
},
"required": true
}
]
}
],
"challenge": "GW8FGpP6jhFrl37yQZIM6w"
}
}
}

Response

The response is a JWS

"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa3JldXFGcTZXcndvelRlR0t1VUR6OGJuaVRGUk5BZzhmM1pCODYyWWRMcDd2I3o2TWtyZXVxRnE2V3J3b3pUZUdLdVVEejhibmlURlJOQWc4ZjNaQjg2MllkTHA3diJ9.eyJpZCI6IjM4ZDljYTcwLThiNjEtNDhjMy05ZWZjLWI2N2Q5NzQxMDc0MyIsInR5cGUiOiJodHRwczovL21hdHRyLmdsb2JhbC9zY2hlbWFzL3ZlcmlmaWFibGUtcHJlc2VudGF0aW9uL3JlcXVlc3QvUXVlcnlCeUV4YW1wbGUiLCJmcm9tIjoiZGlkOmtleTp6Nk1rakJXUFBhMW5qRUt5Z3lyM0xSM3BSS2txdjcxNHZ5VGtmblVkUDZUb0ZTSDUiLCJjcmVhdGVkX3RpbWUiOjE2MDczMDA1MTc4NjMsImV4cGlyZXNfdGltZSI6MTYzODgzNjQwMTAwMCwicmVwbHlfdXJsIjoiaHR0cHM6Ly90ZW5hbnQucGxhdGZvcm0ubWF0dHIuZ2xvYmFsL3YxL3ByZXNlbnRhdGlvbnMvcmVzcG9uc2UiLCJyZXBseV90byI6WyJkaWQ6a2V5Ono2TWtqQldQUGExbmpFS3lneXIzTFIzcFJLa3F2NzE0dnlUa2ZuVWRQNlRvRlNINSJdLCJib2R5Ijp7ImlkIjoiNTc3MDUwNmMtOWNlOS00ZDU0LWIyOTUtNjhhZDJiYjEyYTRjIiwibmFtZSI6ImNlcnRpZmljYXRlLXByZXNlbnRhdGlvbiIsImRvbWFpbiI6InRlbmFudC5wbGF0Zm9ybS5tYXR0ci5nbG9iYWwiLCJxdWVyeSI6W3sidHlwZSI6IlF1ZXJ5QnlFeGFtcGxlIiwiY3JlZGVudGlhbFF1ZXJ5IjpbeyJyZWFzb24iOiJQbGVhc2UgcHJvdmlkZSB5b3VyIGNlcnRpZmljYXRlLiIsImV4YW1wbGUiOnsidHlwZSI6IkNvdXJzZSIsIkBjb250ZXh0IjpbImh0dHBzOi8vc2NoZW1hLm9yZyJdLCJ0cnVzdGVkSXNzdWVyIjpbeyJpc3N1ZXIiOiJkaWQ6a2V5Ono2TWtqQldQUGExbmpFS3lneXIzTFIzcFJLa3F2NzE0dnlUa2ZuVWRQNlRvRlNINSIsInJlcXVpcmVkIjp0cnVlfV19LCJyZXF1aXJlZCI6dHJ1ZX1dfV0sImNoYWxsZW5nZSI6IkdXOEZHcFA2amhGcmwzN3lRWklNNncifX0.9UxUPiJncUY-kxHmGTJpgJ-rty5OPcVMUGkVTDaChu9GWvE9y40qgYjXCnIfvITWHHUt587t-ygSFudh0N5_Dw"

Send to the Mobile Wallet App

The Mobile Wallet App has two main methods for reading Presentation Requests

  1. Scanning a QR code
  2. Opening a Deeplink

Both methods require the URL to be constructed in this format, where {jws} is the entire JWS from the Create a JWS with a DID endpoint.

didcomm://https://demo.platform.mattr.global/?request={jws}

Scanning a QR code

QR codes are a useful way to transfer information from desktop application to mobile apps using the phone’s camera.

Given the nature of the Presentation Request and resulting size of the JWS, pushing that much information into a QR code can be problematic for the mobile device reading the QR code.

Using a QR code API service results in something like this:

https://api.qrserver.com/v1/create-qr-code?&size=400x400&data=didcomm://https://demo.platform.mattr.global/?request=eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa3JkaUNuZHZjeVVCcGRUQ1ZkNVRyaldFYTlocGp6V01mbUgzM1Q5alRKODFFI3o2TWtyZGlDbmR2Y3lVQnBkVENWZDVUcmpXRWE5aHBqeldNZm1IMzNUOWpUSjgxRSJ9.eyJpZCI6ImExODZjMTdkLTc2NzMtNDA2My05YmJmLTk4YmFiZWU1MzBlZiIsInR5cGUiOiJodHRwczovL21hdHRyLmdsb2JhbC9zY2hlbWFzL3ZlcmlmaWFibGUtcHJlc2VudGF0aW9uL3JlcXVlc3QvUXVlcnlCeUV4YW1wbGUiLCJmcm9tIjoiZGlkOmtleTp6Nk1rcmRpQ25kdmN5VUJwZFRDVmQ1VHJqV0VhOWhwanpXTWZtSDMzVDlqVEo4MUUiLCJjcmVhdGVkX3RpbWUiOjE2MDczMDI4MDAxMzksImV4cGlyZXNfdGltZSI6MTYzODgzNjQwMTAwMCwicmVwbHlfdXJsIjoiaHR0cHM6Ly9kZW1vLnBsYXRmb3JtLm1hdHRyLmdsb2JhbC92MS9tZXNzYWdpbmciLCJyZXBseV90byI6WyJkaWQ6a2V5Ono2TWtyZGlDbmR2Y3lVQnBkVENWZDVUcmpXRWE5aHBqeldNZm1IMzNUOWpUSjgxRSJdLCJib2R5Ijp7ImlkIjoiYmFkOWJiNzctYjk2Ni00N2I4LWExMzktZTkwNzNlNTgyNGFkIiwibmFtZSI6Ik1BVFRSIEVtcGxveWVlIENyZWRlbnRpYWwgVjIiLCJkb21haW4iOiJkZW1vLnBsYXRmb3JtLm1hdHRyLmdsb2JhbCIsInF1ZXJ5IjpbeyJ0eXBlIjoiUXVlcnlCeUV4YW1wbGUiLCJjcmVkZW50aWFsUXVlcnkiOlt7InJlYXNvbiI6IlBsZWFzZSBwcm92aWRlIHlvdXIgTUFUVFIgRW1wbG95ZWUgQ3JlZGVudGlhbC4iLCJleGFtcGxlIjp7InR5cGUiOiJNQVRUUiBFbXBsb3llZSBDcmVkZW50aWFsIiwiQGNvbnRleHQiOlsiaHR0cHM6Ly9zY2hlbWEub3JnIl0sInRydXN0ZWRJc3N1ZXIiOlt7Imlzc3VlciI6ImRpZDprZXk6ejZNa21SSmdqSFZvWURvSEh4RVZFU1VacGdpZVVmZW1QaEs5SktVVmNTZlJzb1ZKIiwicmVxdWlyZWQiOnRydWV9XX0sInJlcXVpcmVkIjp0cnVlfV19XSwiY2hhbGxlbmdlIjoiR1c4RkdwUDZqaEZybDM3eVFaSU02dyJ9fQ.zdFvxJvH72jNOFDSH5rzF1ZCKiqwyCeY1sw40r7vL49zH0haxQtiKmW6Yla74dIyqtJGrrzJRF64Vk3knQdDAg

QR code

You might find a high-end phone with decent camera will read the QR code but many lower-end phones will never be able to.

Redirect URLs

For this reason the MATTR Mobile Wallet app will follow a 302 redirect if it is constructed in the way show below, this allows for the QR code code to be much smaller in size and therefore faster and easier to read by a wide range of devices.

didcomm://${redirecterUrl}/{path}

The redirectUrl will need to return a 302 with the location of your tenant URL with the query param of ?request={jws}

Check out the “verify-callback-express” app as part of the MATTR Sample Apps to see a working example of this method.

The MATTR Mobile Wallet is set up to register global.mattr.wallet as a scheme and requires accept/ in the path.

In order to provide the didcomm://.. URL via a Deeplink, we need a further step to base64 encode that URL before being appended to the Deeplink URL.`

Either use the terminal or an online tool to base64 encode the constructed URL.

base64 <<< "didcomm://https://demo.platform.mattr.global/?request=eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa3JkaUNuZHZjeVVCcGRUQ1ZkNVRyaldFYTlocGp6V01mbUgzM1Q5alRKODFFI3o2TWtyZGlDbmR2Y3lVQnBkVENWZDVUcmpXRWE5aHBqeldNZm1IMzNUOWpUSjgxRSJ9.eyJpZCI6ImExODZjMTdkLTc2NzMtNDA2My05YmJmLTk4YmFiZWU1MzBlZiIsInR5cGUiOiJodHRwczovL21hdHRyLmdsb2JhbC9zY2hlbWFzL3ZlcmlmaWFibGUtcHJlc2VudGF0aW9uL3JlcXVlc3QvUXVlcnlCeUV4YW1wbGUiLCJmcm9tIjoiZGlkOmtleTp6Nk1rcmRpQ25kdmN5VUJwZFRDVmQ1VHJqV0VhOWhwanpXTWZtSDMzVDlqVEo4MUUiLCJjcmVhdGVkX3RpbWUiOjE2MDczMDI4MDAxMzksImV4cGlyZXNfdGltZSI6MTYzODgzNjQwMTAwMCwicmVwbHlfdXJsIjoiaHR0cHM6Ly9kZW1vLnBsYXRmb3JtLm1hdHRyLmdsb2JhbC92MS9tZXNzYWdpbmciLCJyZXBseV90byI6WyJkaWQ6a2V5Ono2TWtyZGlDbmR2Y3lVQnBkVENWZDVUcmpXRWE5aHBqeldNZm1IMzNUOWpUSjgxRSJdLCJib2R5Ijp7ImlkIjoiYmFkOWJiNzctYjk2Ni00N2I4LWExMzktZTkwNzNlNTgyNGFkIiwibmFtZSI6Ik1BVFRSIEVtcGxveWVlIENyZWRlbnRpYWwgVjIiLCJkb21haW4iOiJkZW1vLnBsYXRmb3JtLm1hdHRyLmdsb2JhbCIsInF1ZXJ5IjpbeyJ0eXBlIjoiUXVlcnlCeUV4YW1wbGUiLCJjcmVkZW50aWFsUXVlcnkiOlt7InJlYXNvbiI6IlBsZWFzZSBwcm92aWRlIHlvdXIgTUFUVFIgRW1wbG95ZWUgQ3JlZGVudGlhbC4iLCJleGFtcGxlIjp7InR5cGUiOiJNQVRUUiBFbXBsb3llZSBDcmVkZW50aWFsIiwiQGNvbnRleHQiOlsiaHR0cHM6Ly9zY2hlbWEub3JnIl0sInRydXN0ZWRJc3N1ZXIiOlt7Imlzc3VlciI6ImRpZDprZXk6ejZNa21SSmdqSFZvWURvSEh4RVZFU1VacGdpZVVmZW1QaEs5SktVVmNTZlJzb1ZKIiwicmVxdWlyZWQiOnRydWV9XX0sInJlcXVpcmVkIjp0cnVlfV19XSwiY2hhbGxlbmdlIjoiR1c4RkdwUDZqaEZybDM3eVFaSU02dyJ9fQ.zdFvxJvH72jNOFDSH5rzF1ZCKiqwyCeY1sw40r7vL49zH0haxQtiKmW6Yla74dIyqtJGrrzJRF64Vk3knQdDAg" | less

Add this base64 string to the Deeplink URL

global.mattr.wallet://accept/{base64String}

Then send this URL inside a standard message that can be opened on the mobile device (e.g. in an email or Slack message etc).

Clicking on the Deeplink URL from the other messaging app will result in the MATTR Mobile Wallet opening, you’ll need to authenticate, then the ‘Verification request’ screen should open.

MATTR Mobile Wallet Verification request

The presentation is sent in this format

{
"presentation": {
"@context": [
"https://www.w3.org/2018/credentials/v1"
],
"type": [
"VerifiablePresentation"
],
"verifiableCredential": [
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://schema.org"
],
"type": [
"VerifiableCredential",
"Course"
],
"issuer": {
"id": "did:key:z6MkndAHigYrXNpape7jgaC7jHiWwxzB3chuKUGXJg2b5RSj",
"name": "tenant"
},
"issuanceDate": "2020-08-30T23:24:54.876Z",
"credentialSubject": {
"id": "did:key:z6MkfxQU7dy8eKxyHpG267FV23agZQu9zmokd8BprepfHALi",
"givenName": "Chris",
"familyName": "Shin",
"educationalCredentialAwarded": "Certificate Name"
},
"proof": {
"type": "Ed25519Signature2018",
"created": "2020-08-30T23:24:55Z",
"jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..BSHdalZrYml0slwgAXFVF5uAcg2DbPMfwatturKs8TnuxBxylQDnS3JkORORVmO73Ruh7h8KJvVvHO4pE5NsCQ",
"proofPurpose": "assertionMethod",
"verificationMethod": "did:key:z6MkndAHigYrXNpape7jgaC7jHiWwxzB3chuKUGXJg2b5RSj#z6MkndAHigYrXNpape7jgaC7jHiWwxzB3chuKUGXJg2b5RSj"
}
}
],
"id": "335032c1-0f8b-405e-9740-44928237e06c",
"holder": "did:key:z6MksMYtQSvQsfk3KZy7vxBzjcBJhx1i7TaLuvJePPPDEAjH",
"proof": {
"type": "Ed25519Signature2018",
"created": "2020-12-08T20:12:18Z",
"challenge": "e1b35ae0-9e0e-11ea-9bbf-a387b27c9e60",
"domain": "global.mattr.wallet",
"jws": "eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..MfVEy102c4oZA_G98G6HUxEmfXFLEIWbX9C36zF_cA-D8EF8jRMQWc5eJpMn3hgLm2xhPw8GzTrnwPHa37L-DA",
"proofPurpose": "authentication",
"verificationMethod": "did:key:z6MksMYtQSvQsfk3KZy7vxBzjcBJhx1i7TaLuvJePPPDEAjH#z6MksMYtQSvQsfk3KZy7vxBzjcBJhx1i7TaLuvJePPPDEAjH"
}
}
}

Receiving the Credential Presentation Callback

Once a valid Presentation is sent by the Mobile App, the Platform will perform some checks to ensure the validity of the Presentation:

  • Issuer DID of each Credential can be resolved
  • JSON-LD context is valid for subject claims
  • Proof is valid & the credential has not been tampered with
  • If present check any Credential on against its RevocationList2020 status

These checks will inform the verified boolean and where the contents is mapped to these fields and presented to the Callback URL as an application/json body.

{
"presentationType": "QueryByExample",
"challengeId": "GW8FGpP6jhFrl37yQZIM6w",
"claims": {
"id": "did:key:z6MkfxQU7dy8eKxyHpG267FV23agZQu9zmokd8BprepfHALi",
"givenName": "Chris",
"familyName": "Shin",
"educationalCredentialAwarded": "Certificate Name"
},
"verified": true,
"holder": "did:key:z6MkgmEkNM32vyFeMXcQA7AfQDznu47qHCZpy2AYH2Dtdu1d"
}

Conclusion

The steps outlined above give a detail explanation of the steps and some of the challenges involved in setting up an end to end experience. For a more hands-on step through, checkout the Verify Credentials using Presentation Request Callbacks sample app, alternatively Verify using the OIDC Bridge is another approach to obtaining verified credential data.