Skip to content

Instantly share code, notes, and snippets.

@ardislu
Last active July 31, 2025 07:35
Show Gist options
  • Select an option

  • Save ardislu/98564ae85254854ba78b704fe1679ebc to your computer and use it in GitHub Desktop.

Select an option

Save ardislu/98564ae85254854ba78b704fe1679ebc to your computer and use it in GitHub Desktop.
Minimal boilerplate JavaScript code to use the pseudo-random function extension (prf) of the Web Authentication API.
/**
* =================================================== IMPORTANT ===================================================
*
* The passkey creation and retrieval MUST occur in the same "effective domain" or "registrable domain suffix"
* ([more info](https://www.w3.org/TR/webauthn-3/#rp-id)):
*
* > For example, given a Relying Party whose origin is `https://login.example.com:1337`, then the following
* > RP IDs are valid: `login.example.com` (default) and `example.com`, but not `m.login.example.com` and not `com`.
*
* As a result of this restriction, data encrypted with a crypto key that was derived from a passkey's PRF extension
* is tied to a specific domain, and **can only be decrypted on the same domain**. Only perform long-term encryption
* if you expect the domain to be online for a long time as well.
*
* =================================================================================================================
*/
/**
* Create a new resident passkey and get a pseudo-random value produced from the passkey PRF extension.
*
* If the passkey does not return a PRF value after creation, try to sign in immediately after passkey creation
* and get the PRF value from the sign in flow.
* @see {@link https://www.w3.org/TR/webauthn-3/#prf-extension}
* @returns {Promise<ArrayBuffer&{byteLength:32}>} A 32 byte long `ArrayBuffer` containing a pseudo-random
* value produced from the passkey PRF extension.
*/
async function createPrf() {
return navigator.credentials.create({
publicKey: {
rp: { name: '' },
user: { id: new ArrayBuffer(1), name: crypto.randomUUID(), displayName: '' }, // Windows Hello requires non-empty id; Yubikey requires non-empty name
pubKeyCredParams: [{ type: 'public-key', alg: -8 }, { type: 'public-key', alg: -7 }, { type: 'public-key', alg: -257 }],
extensions: { prf: { eval: { first: new ArrayBuffer(0) } } },
challenge: new ArrayBuffer(0),
authenticatorSelection: { residentKey: 'required' } // Resident key is required because there is no server to store c.rawId
}
}).then(c => c.getClientExtensionResults().prf?.results?.first ?? getPrf()); // Yubikey does not return PRF on creation, workaround is sign in immediately after creation
}
/**
* Request a "sign in" with a passkey created previously, if it exists. This function will return the same
* pseudo-random value produced during the passkey creation function.
* @see {@link https://www.w3.org/TR/webauthn-3/#prf-extension}
* @returns {Promise<ArrayBuffer&{byteLength:32}>} A 32 byte long `ArrayBuffer` containing a pseudo-random
* value produced from the passkey PRF extension.
*/
async function getPrf() {
return navigator.credentials.get({
publicKey: {
extensions: { prf: { eval: { first: new ArrayBuffer(0) } } },
challenge: new ArrayBuffer(0) // Challenge is not used in this context
}
}).then(c => c.getClientExtensionResults().prf.results.first);
}
// Usage example:
const prf1 = await createPrf(); // Will prompt user to create a new passkey
const prf2 = await getPrf(); // Will prompt user to "sign in" with passkey created above
// The values are exactly equal:
const a1 = [...new BigUint64Array(prf1)];
const a2 = [...new BigUint64Array(prf2)];
console.log(a1.every((n, i) => n === a2[i]));
// true
// You can use the prf value for crypto:
const keyMaterial = await crypto.subtle.importKey(
'raw',
prf1,
'HKDF',
false,
['deriveKey']
);
// Then pass to `crypto.subtle.deriveKey` and `crypto.subtle.encrypt`, etc...
// See https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto
// To signal that this file is a JS module which can use top-level await
export { };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment