Skip to main content

Query Public Credentials for a web3name

web3names are linked to KILT DIDs, and KILT DIDs can define services to expose additional service/information. One of the possible endpoint types is the KiltPublishedCredentialCollectionV1 type. The type defines the structure to make KILT credentials public and accessible to anyone.

Because of the relationship between web3names and DIDs, it is possible, given a certain web3name, to retrieve all public credentials that the DID subject identified by that web3name has made available. Below is a code snippet showing how to do that using the KILT SDK, and how to perform the needed security checks/validation as recommended by the specification.

import fetch from 'node-fetch'

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

export async function queryPublishedCredentials(
web3Name: Kilt.Did.Web3Name
): Promise<Kilt.KiltPublishedCredentialCollectionV1> {
const api = Kilt.ConfigService.get('api')

const encodedDidForWeb3Name = await api.call.did.queryByWeb3Name(web3Name)
const {
document: { uri }
} = Kilt.Did.linkedInfoFromChain(encodedDidForWeb3Name)

console.log(`DID for "${web3Name}": ${uri}`)

const resolutionResult = await Kilt.Did.resolve(uri)
if (!resolutionResult) {
throw new Error('The DID does not exist on the KILT blockchain.')
}

const { document } = resolutionResult
// If no details are returned but resolutionResult is not null, the DID has been deleted.
// This information is present in `resolutionResult.metadata.deactivated`.
if (!document) {
throw new Error('The DID has already been deleted.')
}

// Filter the endpoints by their type.
const credentialEndpoints = document.service?.filter((service) =>
service.type.includes(Kilt.KiltPublishedCredentialCollectionV1Type)
)

console.log(
`Endpoints of type "${Kilt.KiltPublishedCredentialCollectionV1Type}" for the retrieved DID:`
)
console.log(JSON.stringify(credentialEndpoints, null, 2))

// For demonstration, only the first endpoint and its first URL are considered.
const firstCredentialCollectionEndpointUrl =
credentialEndpoints?.[0]?.serviceEndpoint[0]
if (!firstCredentialCollectionEndpointUrl) {
console.log(
`The DID has no services of type "${Kilt.KiltPublishedCredentialCollectionV1Type}".`
)
}

// Retrieve the credentials pointed at by the endpoint.
// Being an IPFS endpoint, the fetching can take an arbitrarily long time or even fail if the timeout is reached.
// In production settings, error cases including those where the result is not a correct JSON should be handled accordingly.
const response = await fetch(firstCredentialCollectionEndpointUrl as string)
const credentialCollection: Kilt.KiltPublishedCredentialCollectionV1 =
await response.json()
console.log(`Credential collection behind the endpoint:`)
console.log(JSON.stringify(credentialCollection, null, 2))

// Verify that all credentials are valid and that they all refer to the same subject DID.
await Promise.all(
credentialCollection.map(async ({ credential }) => {
const { revoked } = await Kilt.Credential.verifyCredential(credential)

// Verify that the credential is not revoked.
if (revoked) {
throw new Error(
'One of the credentials has been revoked, hence it is not valid.'
)
}

// Verify that the credential refers to the intended subject.
if (!Kilt.Did.isSameSubject(credential.claim.owner, uri)) {
throw new Error(
'One of the credentials refers to a different subject than expected.'
)
}
})
)

// If none of the above operations throw, the credentials are valid.
console.log('All retrieved credentials are valid! ✅!')

return credentialCollection
}