<!doctype html>
<html>
<body>
<iframe
id="humanos-frame"
style="width: 100%; height: 600px; border: none"
allow="camera"
></iframe>
<pre id="result">Waiting for KYC result...</pre>
<script>
const SESSION_ID = "YOUR_SESSION_ID";
const HUMANOS_ORIGIN = "https://link.humanos.app";
const HKDF_INFO = new TextEncoder().encode("humanos-postmessage-v1");
let privateKey;
async function init() {
// 1. Generate key pair
const keyPair = await crypto.subtle.generateKey(
{ name: "ECDH", namedCurve: "P-256" },
false,
["deriveKey", "deriveBits"],
);
privateKey = keyPair.privateKey;
// 2. Export public key
const spki = await crypto.subtle.exportKey("spki", keyPair.publicKey);
const pubKeyB64 = btoa(String.fromCharCode(...new Uint8Array(spki)));
// 3. Load iframe
const src =
`${HUMANOS_ORIGIN}/link/${SESSION_ID}` +
`?pubKey=${encodeURIComponent(pubKeyB64)}` +
`&postMessageOrigin=${encodeURIComponent(window.location.origin)}`;
document.getElementById("humanos-frame").src = src;
}
// 4. Listen for encrypted message
window.addEventListener("message", async (event) => {
if (event.origin !== HUMANOS_ORIGIN) return;
if (!event.data?.encrypted) return;
const { ephemeralPublicKey, iv, ciphertext } = event.data;
// Import ephemeral public key
const epkBytes = Uint8Array.from(atob(ephemeralPublicKey), (c) =>
c.charCodeAt(0),
);
const epk = await crypto.subtle.importKey(
"spki",
epkBytes,
{ name: "ECDH", namedCurve: "P-256" },
false,
[],
);
// Derive shared secret
const sharedBits = await crypto.subtle.deriveBits(
{ name: "ECDH", public: epk },
privateKey,
256,
);
// HKDF → AES key
const hkdfKey = await crypto.subtle.importKey(
"raw",
sharedBits,
"HKDF",
false,
["deriveKey"],
);
const aesKey = await crypto.subtle.deriveKey(
{
name: "HKDF",
hash: "SHA-256",
salt: new Uint8Array(0),
info: HKDF_INFO,
},
hkdfKey,
{ name: "AES-GCM", length: 256 },
false,
["decrypt"],
);
// Decrypt
const ctBytes = Uint8Array.from(atob(ciphertext), (c) =>
c.charCodeAt(0),
);
const ivBytes = Uint8Array.from(atob(iv), (c) => c.charCodeAt(0));
const plaintext = await crypto.subtle.decrypt(
{ name: "AES-GCM", iv: ivBytes, tagLength: 128 },
aesKey,
ctBytes,
);
const payload = JSON.parse(new TextDecoder().decode(plaintext));
document.getElementById("result").textContent = JSON.stringify(
payload,
null,
2,
);
});
init();
</script>
</body>
</html>