1Password / passkey-rs

A framework for defining Webauthn Authenticators that support passkeys
Apache License 2.0
129 stars 17 forks source link

Error deserializing `PublicKeyCredential` from JsValue #48

Open Ben-Lichtman opened 2 months ago

Ben-Lichtman commented 2 months ago

Trying to implement webauthn on the browser, which includes deserializing the browser's response to the message into a passkey_types::webauthn::CreatedPublicKeyCredential / passkey_types::webauthn::PublicKeyCredential, however it seems that the raw_id field of PublicKeyCredential is allowed to be {} when returned from the browser, which causes the deserialization to return an error.

let promise = window()
  .navigator()
  .credentials()
  .create_with_options(&creation_options)
  .map_err(Error::JsError)?;

let cred_jsvalue = JsFuture::from(promise).await.map_err(Error::JsError)?;

let cred = web_sys::PublicKeyCredential::from(cred_jsvalue);

let cred = <JsValue as JsValueSerdeExt>::into_serde::<
passkey_types::webauthn::CreatedPublicKeyCredential,
>(&cred)
.unwrap();
panicked at app/src/webauthn.rs:82:6:
called `Result::unwrap()` on an `Err` value: Error("invalid type: map, expected A vector of bytes or a base46(url) encoded string", line: 1, column: 41)
Progdrasil commented 2 months ago

Hi @Ben-Lichtman, the issue here is the use of ArrayBuffer by the webauthn types which is readonly memory. When trying to serialize to JSON it will automatically "drop" the value and replace it with an empty object. To get around this you need to either re-map the array buffer to an array or a base64url string. OR remap it directly from the web_sys/js_sys types. Perhaps wasm_bindgen_serde could help with this but I haven't tested it.

I would like to one day build wasm dedicated library for the passkey crates, but thats a fairly heavy lift that I currently don't really have time for. We alway welcome contributions.

Ben-Lichtman commented 1 month ago

@Progdrasil I'm a little confused - isn't the deserialization though JsValueSerdeExt (via gloo) designed to do exactly that (remap from the web_sys type into your PublicKeyCredential type)?

It would make sense to me that the answer would be to change the Deserialize implementation on Bytes to handle visit_map?

Please let me know if I'm misunderstanding.

Progdrasil commented 1 month ago

JsValueSerdeExt does not. It uses the JSON.stringify method internally which transforms it to an empty map. See this JSFiddle as an example. Implementing visit_map will not help since the value will be lost by stringifying the array buffer and that map will be empty.