Skip to main content

๐Ÿค Verification

In this section, you'll play the role of a Verifier:

  • You'll take an Presentation object given to you by a Claimer;
  • You'll verify that its data is correct;
  • You'll verify that the attestation is valid, i.e., its hash exists on-chain and the attestation has not been revoked.
  • You'll verify that the Credential is owned by the Claimer sending it
Presentation

A Credential is used to create the Presentation object by the claimer. Unlike the credential, a presentation can hide some attributes that are not required by the verifiers and can contain a claimer-signed challenge. A presentation also contains a proof that the claimer owns the credential.

Create Presentationโ€‹

It's not enough to just send our credential as a Claimer as we also need to prove ownership of it. This is done by creating a presentation and signing the Verifier's challenge.

claimer/createPresentation.ts
import * as Kilt from '@kiltprotocol/sdk-js'

export async function createPresentation(
credential: Kilt.ICredential,
signCallback: Kilt.SignCallback,
challenge?: string
): Promise<Kilt.ICredentialPresentation> {
// Create the presentation from credential, DID and challenge.
return Kilt.Credential.createPresentation({
credential,
signCallback,
challenge
})
}

Verifyโ€‹

Let's write our verification script. Here we'll expose getChallenge which returns a random and unique challenge for the Claimer to sign; this is used to prove ownership. We'll also expose verifyPresentation which will do the actual verification. Copy the code below, this completes the Verifier code!

verify.ts
import type { ApiPromise } from '@polkadot/api'

import { config as envConfig } from 'dotenv'

import * as Kilt from '@kiltprotocol/sdk-js'

import { createPresentation } from './claimer/createPresentation'
import { generateKeypairs } from './claimer/generateKeypairs'
import { generateLightDid } from './claimer/generateLightDid'

function getChallenge(): string {
return Kilt.Utils.UUID.generate()
}

// Verifies validity, ownership & attestation.
async function verifyPresentation(
api: ApiPromise,
presentation: Kilt.ICredentialPresentation,
challenge: string,
trustedAttesterUris: Kilt.DidUri[]
): Promise<boolean> {
try {
await Kilt.Credential.verifyPresentation(presentation, { challenge })

const attestationInfo = Kilt.Attestation.fromChain(
await api.query.attestation.attestations(presentation.rootHash),
presentation.rootHash
)
if (attestationInfo.revoked) {
return false
}
// Returns true if no trusted attester URI is provided or, if it is, if it matches the one that issued the presented credential.
return trustedAttesterUris.includes(attestationInfo.owner)
} catch {
return false
}
}

export async function verificationFlow(
credential: Kilt.ICredential,
signCallback: Kilt.SignCallback,
trustedAttesterUris: Kilt.DidUri[] = []
) {
const api = Kilt.ConfigService.get('api')

// Verifier sends a unique challenge to the claimer ๐Ÿ•Š
const challenge = getChallenge()

// Create a presentation and send it to the verifier ๐Ÿ•Š
const presentation = await createPresentation(
credential,
signCallback,
challenge
)

// The verifier checks the presentation.
const isValid = await verifyPresentation(
api,
presentation,
challenge,
trustedAttesterUris
)

if (isValid) {
console.log('Verification successful! You are allowed to enter the club ๐ŸŽ‰')
} else {
console.log('Verification failed! ๐Ÿšซ')
}
}

// Don't execute if this is imported by another file.
if (require.main === module) {
;(async () => {
envConfig()

try {
await Kilt.connect(process.env.WSS_ADDRESS as string)
const claimerDidMnemonic = process.env.CLAIMER_DID_MNEMONIC as string
const { authentication } = generateKeypairs(claimerDidMnemonic)
const claimerDid = generateLightDid(claimerDidMnemonic)
const attesterDid = process.env.ATTESTER_DID_URI as Kilt.DidUri
// Load credential and claimer DID
const credential = JSON.parse(process.env.CLAIMER_CREDENTIAL as string)
await verificationFlow(
credential,
async ({ data }) => ({
signature: authentication.sign(data),
keyType: authentication.type,
keyUri: `${claimerDid.uri}${claimerDid.authentication[0].id}`
}),
[attesterDid]
)
} catch (e) {
console.log('Error in the verification flow')
throw e
}
})()
}

Runโ€‹

Run the verification flow on command line:

yarn ts-node verify.ts

That's it! All done :-)