Closed infinisil closed 2 years ago
Is the idea here that instead of the FromJSON AuthenticatorAttestationResponse
instance, the library user would pass a custom parser to Aeson? Something like this parse fromWebauthnJson jsonValue
?
@lykahb I've started with it in #32, though quite unfinished at the moment. But in the end it will end up exposing functions like
encodeWebauthnJsonCreationOptions :: PublicKeyCredentialCreationOptions -> ByteString
decodeWebauthnJsonPublicKey :: ByteString -> Either Error (PublicKeyCredential AuthenticatorAttestationResponse)
And the same for login/assertion. These functions are then the only part of the library that deals with the specifics of the JavaScript encoding used.
As an alternate example, the webauthn library uses a CBOR-based encoding, which could be used with different functions of the same type.
CBOR-based encoding is a rather bad choice for using on the client. It requires adding an extra library to the client bundle. Also, the payload cannot be easily inspected on the network panel. Going forward with JSON helpers is the right way.
Moving the JSON helpers into a separate module is a nice change. However, I do not understand well the the rest of the encoding/decoding JSON changes. The way I understood it at first was that the instances of FromJSON
are removed but the parser and toJSON for the top-level data types would remain, so that they can be user as helpers the default schema.
What is in the bytestring for encodeWebauthnJsonCreationOptions
and decodeWebauthnJsonPublicKey
? Is this a JSON? It is not clear why it is not a Value
. Is it to save a call to encode for the caller? In Yesod the handler code that works with Value
is more idiomatic than one that works with raw bytestrings. Would this pair of functions be compatible with webauthn-json?
In this part of the #32 PR it is not clear what "raw JavaScript object" and "Haskell JavaScript object" mean. One of them seems to be aeson Value
.
-- - bytes -> raw JavaScript object (specific to server javascript used) -- - raw JavaScript object -> Haskell JavaScript object (generic over server javascript used)
Ah yes, I should clarify that these functions I mentioned would just be the most abstract way of presenting such an interface. The same type could be used whether it was JSON or anything else. The ByteString
would represent what is being sent/received in the HTTP request/response's body.
However as you also mentioned, I noticed quickly that this won't work particularly well for web frameworks which generally expect JSON. So instead it should end up look something like this:
newtype WebauthnJsonCreationOptions = WebauthnJsonCreationOptions PublicKeyCredentialCreationOptions
instance ToJSON WebauthnJsonCreationOptions
newtype WebauthnJsonPublicKeyAttestation = WebauthnJsonPublicKeyAttestation (PublicKeyCredential AuthenticatorAttestationResponse)
instance FromJSON WebauthnJsonPublicKeyAttestation
This should become clearer as I continue working on this. The JSON decoding separation will be pretty nice, as it's been one of the most confusing bits.
Indeed a good idea to decouple this. One could send the data in any form. JSON, ProtoBuf, or even form-url-encoded with a traditional <input type=hidden>
form...
This is essentially done with https://github.com/tweag/haskell-fido2/pull/32 and https://github.com/tweag/haskell-fido2/pull/37.
See https://github.com/tweag/haskell-fido2/blob/master/fido/Crypto/Fido2/Model/JavaScript.hs, which contains datatypes that represent how the JavaScript side encodes them. These types implement FromJSON
and ToJSON
for the JSON format used by webauthn-json, but other decoders/encoders can also be written relatively easily and independently of this library.
That JavaScript representation is then decoded into a more well-typed Haskell model declared in https://github.com/tweag/haskell-fido2/blob/master/fido/Crypto/Fido2/Model.hs. The decoding/encoding happens in https://github.com/tweag/haskell-fido2/blob/master/fido/Crypto/Fido2/Model/JavaScript/Decoding.hs and https://github.com/tweag/haskell-fido2/blob/master/fido/Crypto/Fido2/Model/JavaScript/Encoding.hs respectively.
Currently we're relying on the JavaScript library webauthn-json's encoding of JavaScript objects returned by
navigator.credentials.create()
andnavigator.credentials.get()
. This happens via theFromJSON
instances here: https://github.com/tweag/haskell-fido2/blob/247ffd99b9262f98b48891523f94b19e7c1e9f22/fido/Crypto/Fido2/Protocol.hs#L339-L369Instead we should have functions like
fromWebauthnJson :: Value -> Parser (PublicKeyCredential response)
that handle this. This then acts as a clear separation of concern between the JavaScript encoding and the Haskell value.