Skip to main content

Setting Up the Communication Session

The first step in creating your dapp is to set up the communication session. The purpose of the session is to pass encrypted messages back and forth between your dapp and the extension.

Dapp Indicates Credential API Support

In order to indicate its support of the extension's API, the dapp creates the window.kilt object as soon as possible. To indicate the API version that the dapp supports, we also create the properties window.kilt.meta.versions.credentials. Since meta is not an extension, this property is not enumerable. For example:

<head>
<script>
window.kilt = {}
Object.defineProperty(window.kilt, 'meta', {
value: {
versions: {
credentials: '3.0'
}
},
enumerable: false
})
</script>
</head>

Dapp Introduces Itself

The dapp introduces itself to the extension with its name, encryption key URI, and a challenge. A copy of the challenge should be stored on the server side. For example:

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

// `window` object: Should be used only in the following example.
// Otherwise import directly from the KILT extension library.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let window: {
kilt: {
sporran: {
startSession: (
dAppName: string,
dAppEncryptionKeyUri: Kilt.DidResourceUri,
challenge: string
) => Promise<void>
}
}
}

export async function main() {
const api = Kilt.ConfigService.get('api')

const did = 'did:kilt:4smcAoiTiCLaNrGhrAM4wZvt5cMKEGm8f3Cu9aFrpsh5EiNV'
const dAppName = 'Your dApp Name'

const encodedFullDid = await api.call.did.query(Kilt.Did.toChain(did))
const { document } = Kilt.Did.linkedInfoFromChain(encodedFullDid)
// If there is no DID, or the DID does not have any key agreement key, return
if (!document.keyAgreement || !document.keyAgreement[0]) {
return
}
const dAppEncryptionKeyUri =
`${document.uri}${document.keyAgreement[0].id}` as Kilt.DidResourceUri

// Generate and store challenge on the server side for the next step.
const response = await fetch('/challenge')
const challenge = await response.text()

const session = await window.kilt.sporran.startSession(
dAppName,
dAppEncryptionKeyUri,
challenge
)

return session
}

At this point the extension has received the introduction of the dapp and returned a new session along with the encrypted challenge.

Dapp checks the session values

The extension has provided the session along with an encrypted challenge. The dapp decrypts the challenge and verifies that it matches the original challenge. This should happen on the server side:

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

export async function main({
session,
keyAgreementKeyPair,
originalChallenge
}: {
session: {
encryptionKeyUri: Kilt.DidResourceUri
encryptedChallenge: string
nonce: string
}
keyAgreementKeyPair: Kilt.KiltEncryptionKeypair
originalChallenge: `0x{string}`
}) {
const { encryptionKeyUri, encryptedChallenge, nonce } = session
const encryptionKey = await Kilt.Did.resolveKey(encryptionKeyUri)
if (!encryptionKey) {
throw new Error('an encryption key is required')
}

const decryptedBytes = Kilt.Utils.Crypto.decryptAsymmetric(
{ box: encryptedChallenge, nonce },
encryptionKey.publicKey,
keyAgreementKeyPair.secretKey // derived from your seed phrase
)
// If it fails to decrypt, return.
if (!decryptedBytes) {
throw new Error('Could not decode')
}

const decryptedChallenge = Kilt.Utils.Crypto.u8aToHex(decryptedBytes)

// Compare the decrypted challenge to the challenge you stored earlier.
if (decryptedChallenge !== originalChallenge) {
throw new Error('Invalid challenge')
}
return session
}

That's it! The communication session has been securely established and you're ready to start sending and receiving messages.