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:
- Typescript
- Javascript
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
}
# loading code...
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:
- Typescript
- Javascript
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
}
# loading code...
That's it! The communication session has been securely established and you're ready to start sending and receiving messages.