Reputation: 11148
I'm try to follow instructions at https://webauthn.guide to register biometric login for a user in my site.
The register Javascript function is this:
var randomStringFromServer = "123456";
async function CreateANew()
{
var bz = Uint8Array.from(randomStringFromServer);
const publicKeyCredentialCreationOptions = {
challenge: bz,
rp: {
name: "mysite.COM",
id: "mysite.com",
},
user: {
id: Uint8Array.from(
'some_user_name'),
name: 'some_name',
displayName: 'some_display_name',
},
pubKeyCredParams: [{alg: -7, type: "public-key"}],
authenticatorSelection: {
},
timeout: 60000,
attestation: "direct"
};
const credential = await navigator.credentials.create({
publicKey: publicKeyCredentialCreationOptions
});
if (!credential)
return;
// decode the clientDataJSON into a utf-8 string
const utf8Decoder = new TextDecoder('utf-8');
const decodedClientData = utf8Decoder.decode(credential.response.clientDataJSON);
// parse the string as an object
const clientDataObj = JSON.parse(decodedClientData);
const decodedAttestationObj = CBOR.decode(
credential.response.attestationObject);
const {authData} = decodedAttestationObj;
// get the length of the credential ID
const dataView = new DataView(
new ArrayBuffer(2));
const idLenBytes = authData.slice(53, 55);
idLenBytes.forEach(
(value, index) => dataView.setUint8(
index, value));
const credentialIdLength = dataView.getUint16();
// get the credential ID
const credentialId = authData.slice(
55, 55 + credentialIdLength);
// get the public key object
const publicKeyBytes = authData.slice(
55 + credentialIdLength);
// the publicKeyBytes are encoded again as CBOR
const publicKeyObject = CBOR.decode(
publicKeyBytes.buffer);
let CID = credentialId;
let PID = publicKeyBytes;
console.log(credentialId);
$.ajax({
url: "bio.php",
method: "POST",
data: {"create": 2, "type": <?= $tyx ?>, "uid" : <?= $u ?>, "challenge": clientDataObj.challenge, "origin": clientDataObj.origin,"ctype": clientDataObj.type,"authData": decodedAttestationObj.authData,"fmt" : decodedAttestationObj.fmt, "credentialID" : CID, "publicKeyBytes" : PID },
success: function (result) {
$("#result").html(result);
if (result.startsWith("OK"))
window.location = "bio.php";
}
});
This works, I get back a credentialID
and a publicKeyBytes
array in my PHP database.
<?php
$ar = serialize($_POST['credentialID']); // and then store $ar to database
When however I'm trying to authenticate the user:
// $or = PHP array with the credentialID taken from the database
// $or = unserialize(...);
async function Login()
{
var bz = Uint8Array.from(randomStringFromServer);
let id2 = "<?= implode(",",$or) ?>";
let id2a = id2.replace(/, +/g, ",").split(",").map(Number);
let id3 = Uint8Array.from(id2a);
console.log(id3);
const publicKeyCredentialRequestOptions = {
challenge: bz,
allowCredentials: [{
id: id3,
type: 'public-key',
}],
timeout: 60000,
}
const credential = await navigator.credentials.get({
publicKey: publicKeyCredentialRequestOptions
});
}
This time I only get an 'insert your USB key' in Chrome. It seems not to recognize my credentialID.
What am I doing wrong?
Upvotes: 1
Views: 1313
Reputation: 1662
Most likely the credential ID is getting corrupted somewhere along the way. Try logging to to the console just after extracting it:
console.log(btoa(String.fromCharCode.apply(null, new Uint8Array(CID)));
And again just before making the authentication call:
console.log(btoa(String.fromCharCode.apply(null, new Uint8Array(id3)));
They should be the same value.
As an aside:
user: { id: Uint8Array.from('some_user_name'),
The user.id should not include any identifiable information, so a user name is a bad value there. I recommend generating a per-user, 128-bit random value in your database to use as user IDs.
// get the public key object
const publicKeyBytes = authData.slice(55 + credentialIdLength);
Extensions may follow the public key and this would pick them up. That might be fine if your CBOR decoder ignores trailing data. (You can also get the public key by calling getPublicKey
on the attestation response, and you don't have to deal with CBOR that way.)
Upvotes: 3