Reputation: 76
I'm trying to implement passkeys for my web app according to these guides, and I'm primarily working on the registration flow. After I've created the passkey using navigator.credential.create
, I try obtaining the newly generated public key using the getPublicKey
method on the returned response attribute. However, when I try inspecting the object, my browser says:
Restricted { }
According to MDN, this object should be an ArrayBuffer. In order to send it to the server I try to convert it to a base64-encoded string, however whenever I try to do something with this (in fact, whenever I try to do anything to it), the browser throws an error saying I can't access this object.
I've already worked around this by sending the attestationObject
to the server and having the server decode it, but according to everything I've read, I should be able to do this client-side. Am I missing something obvious, or is this expected behavior?
Upvotes: 0
Views: 254
Reputation: 9356
After you call navigator.credential.create()
you need to parse the response data to retrieve the newly created public key (among other things). The guides you linked are a little old now, the official Webauthn guide shows more precisely how to parse the attestation response data.
The create call should return a response like:
PublicKeyCredential {
id: 'ADSUllKQmbqdGtpu4sjseh4cg2TxSvrbcHDTBsv4NSSX9...',
rawId: ArrayBuffer(59),
response: AuthenticatorAttestationResponse {
clientDataJSON: ArrayBuffer(121),
attestationObject: ArrayBuffer(306),
},
type: 'public-key'
}
Next thing you need to do is parse the attestation object from the response:
// note: a CBOR decoder library is needed here.
const decodedAttestationObj = CBOR.decode(
credential.response.attestationObject);
console.log(decodedAttestationObject);
{
authData: Uint8Array(196),
fmt: "fido-u2f",
attStmt: {
sig: Uint8Array(70),
x5c: Array(1),
},
}
Finally, to retrieve the public key from the authData
:
const {authData} = decodedAttestationObject;
// 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);
console.log(publicKeyObject)
{
1: 2,
3: -7,
-1: 1,
-2: Uint8Array(32) ...
-3: Uint8Array(32) ...
}
The resulting publicKeyObject
should then be paired and save with the user registration on the server.
Upvotes: 0