Skip to content

Instantly share code, notes, and snippets.

@getify
Created October 2, 2025 12:31
Show Gist options
  • Save getify/b7395cdf0d2c5d3c0dd6685b5fae406d to your computer and use it in GitHub Desktop.
Save getify/b7395cdf0d2c5d3c0dd6685b5fae406d to your computer and use it in GitHub Desktop.
slide code for "Past Time For Passkeys" talk
let {
name: USER_NAME,
displayName: USER_DISPLAY_NAME,
id: USER_ID,
regChallenge: USER_REG_CHALLENGE
} = await (
fetch("/api/register-passkey",{
// ..
})
.then(resp => resp.json())
);
USER_ID = new Uint8Array(USER_ID);
USER_REG_CHALLENGE = new Uint8Array(USER_REG_CHALLENGE);
let authResult = await navigator.credentials.get(/* .. */);
let authClientData = JSON.parse(
(new TextDecoder("utf-8",{ fatal: true }))
.decode(
authResult.response.clientDataJSON
)
);
if (authClientData.type != "webauthn.get") {
throw new Error("Invalid auth response");
}
let credID = authResult.id;
let signature = new Uint8Array(authResult.response.signature);
let authenticatorData = new Uint8Array(
authResult.response.authenticatorData
);
let clientData = new Uint8Array(
authResult.response.clientDataJSON
);
let { success } = await (
fetch("/api/verify-passkey",{
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
credentialID: credID,
// JSON serialize typed arrays as simple arrays
signature: [ ...signature ],
authenticatorData: [ ...authenticatorData ],
clientData: [ ...clientData ]
})
})
.then(resp => resp.json())
);
async function handleRequest(requ,resp) {
// if (requ.url == "/api/register-passkey") { .. }
// else if (requ.url == "/api/save-passkey") { .. }
// else if (requ.url == "/api/auth-passkey") { .. }
else if (requ.url == "/api/verify-passkey") {
let { userID } = await lookupCurrentUser(requ);
let {
credentialID,
signature,
authenticatorData,
clientData
} = JSON.parse(await getStream(requ));
let isVerified = await verifyUserPasskey(
userID,
credentialID,
new Uint8Array(signature),
new Uint8Array(authenticatorData),
new Uint8Array(clientData)
);
resp.writeHead(200,{ "Content-Type": "application/json" });
resp.end(JSON.stringify({ success: isVerified });
}
}
let crypto = require("node:crypto").webcrypto;
async function verifyUserPasskey(
userID,
credentialID,
signature,
authenticatorData,
clientData
) {
let { algo, pubkey } = await lookupUserPasskey(userID,credentialID);
let clientDataHash = new Uint8Array(
await crypto.subtle.digest("SHA-256",clientData)
);
var verificationData = new Uint8Array(
authenticatorData.byteLength +
clientDataHash.byteLength
);
verificationData.set(authenticatorData,0);
verificationData.set(clientDataHash,authenticatorData.byteLength);
if (algo == -7) {
// // ES256 / ECDSA (P-256)? signature comes back
// ASN.1 encoded, so decode it
let der = ASN1.parseVerbose(signature);
signature = new Uint8Array([
...der.children[0].value,
...der.children[1].value,
]);
}
let cipherOpts = (
algo == -257 ?
{
name: "RSASSA-PKCS1-v1_5",
hash: { name: "SHA-256", },
} :
algo == -7 ?
{
name: "ECDSA",
namedCurve: "P-256",
hash: { name: "SHA-256", },
} :
/* .. */
);
let pubKeySubtle = await crypto.subtle.importKey(
"spki", // Simple Public Key Infrastructure rfc2692
pubkey,
cipherOpts,
false, // extractable
[ "verify", ]
);
return crypto.subtle.verify(
cipherOpts,
pubKeySubtle,
signature,
verificationData
);
}
import {
regDefaults, authDefaults,
register, auth
} from "@lo-fi/webauthn-local-client";
var regResult = await register(
regDefaults({
relyingPartyName: "My Cool Web Site",
user: {
name: USER_NAME,
displayName: USER_DISPLAY_NAME,
id: USER_ID
},
challenge:
})
);
var authResult = await auth(
authDefaults({
challenge: USER_AUTH_CHALLENGE
})
);
let crypto = require("node:crypto").webcrypto;
async function handleRequest(requ,resp) {
if (requ.url == "/api/register-passkey") {
// get current user info
let {
userID,
name,
displayName
} = await lookupCurrentUser(requ);
// generate registration challenge (random bytes)
let regChallenge = new Uint8Array(16);
crypto.getRandomValues(regChallenge);
resp.writeHead(200,{ "Content-Type": "application/json" });
resp.end(JSON.stringify({
name,
displayName,
// JSON serialize typed arrays as simple arrays
userID: [ ...userID ],
regChallenge: [ ...regChallenge ]
}));
}
// else if ..
}
let regResult = await navigator.credentials.create({
publicKey: {
attestation: "none", // unless you're security paranoid
rp: { // rp = relying party
id: document.location.hostname,
name: "My Cool Web Site"
},
user: {
name: USER_NAME,
displayName: USER_DISPLAY_NAME,
id: USER_ID
},
challenge: USER_REG_CHALLENGE,
pubKeyCredParams: [
{
type: "public-key",
alg: -257 // RSASSA-PKCS1-v1_5
},
{
type: "public-key",
alg: -7 // ES256 / ECDSA (P-256)
}
]
}
});
let regResult = await navigator.credentials.create(/* .. */);
let regClientData = JSON.parse(
(new TextDecoder("utf-8",{ fatal: true }))
.decode(
regResult.response.clientDataJSON
)
);
if (regClientData.type != "webauthn.create") {
throw new Error("Invalid registration response");
}
let credID = regResult.id;
let publicKeyAlgoCOSE = regResult.response.getPublicKeyAlgorithm();
let publicKeySPKI = new Uint8Array(regResult.response.getPublicKey());
let { success } = await (
fetch("/api/save-passkey",{
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
credentialID: credID,
algo: publicKeyAlgoCOSE,
// JSON serialize typed arrays as simple arrays
pubkey: [ ...publicKeySPKI ]
})
})
.then(resp => resp.json())
);
let getStream = require("get-stream");
async function handleRequest(requ,resp) {
// if (requ.url == "/api/register-passkey") { .. }
else if (requ.url == "/api/save-passkey") {
let { userID } = await lookupCurrentUser(requ);
let {
credentialID,
algo,
pubkey
} = JSON.parse(await getStream(requ));
await saveUserPasskey(
userID,
credentialID,
algo,
new Uint8Array(pubkey)
);
resp.writeHead(200,{ "Content-Type": "application/json" });
resp.end(JSON.stringify({ success: true });
}
// else if ..
}
let {
authChallenge: USER_AUTH_CHALLENGE
} = await (
fetch("/api/auth-passkey",{
// ..
})
.then(resp => resp.json())
);
USER_AUTH_CHALLENGE = new Uint8Array(USER_AUTH_CHALLENGE);
async function handleRequest(requ,resp) {
// if (requ.url == "/api/register-passkey") { .. }
// else if (requ.url == "/api/save-passkey") { .. }
else if (requ.url == "/api/auth-passkey") {
let { userID } = await lookupCurrentUser(requ);
// generate authentication challenge (random bytes)
let authChallenge = new Uint8Array(16);
crypto.getRandomValues(authChallenge);
await savePasskeyAuthChallenge(userID,authChallenge);
resp.writeHead(200,{ "Content-Type": "application/json" });
resp.end(JSON.stringify({
// JSON serialize typed arrays as simple arrays
authChallenge: [ ...authChallenge ]
}));
}
// else if ..
}
let authResult = await navigator.credentials.get({
publicKey: {
rpId: document.location.hostname,
challenge: USER_AUTH_CHALLENGE
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment