go-webauthn/webauthn

Inconsistent base64 encoding/decoding via JSON

ricecake opened this issue · 6 comments

Version

0.3.1

Description

The protocol.CredentialCreation object returned by webauthn.BeginRegistration uses the WebauthnID method from the webauthn.User interface to get the Id of the user.

If this object is serialized via the encoding/json module, the field gets serialized as base64 via base64.StdEncoding.

Later, after registration, if the credential is sent back as part of the login process, it will be decoded as the UserHandle portion of a AuthenticatorAssertionResponse, which has the type defined as URLEncodedBase64, which defines a custom json marshaller which builds on base64.RawURLEncoding.

Since these two base64 encoding aren't compatible, parsing the assertion fails and login can't proceed, specifically generating the error "Parse error for Assertion".

Reproduction

This issue can be reproduced by registering a new credential, and returning the result of BeginRegistration directly encoded as json. Finish the registration without changing or parsing the Id on the client side.

Attempt to use the credential without changing the UserHandle field after deserialzing via the standard json encoder.

Expectations

The expected behavior is that the WebauthnID as returned from the interface method would be able to make a roundtrip in this fashion without needed modification on the server or client side.

Documentation

Updating the locations that reference []byte to use URLEncodedBase64 should do it, but there might be backwards compatibility concerns?

WebAuthnID() []byte

ID []byte `json:"id"`

UserHandle URLEncodedBase64 `json:"userHandle,omitempty"`

return nil, ErrBadRequest.WithDetails("Parse error for Assertion")

Can you try with the following edit to confirm I understand the problem accurately:

go mod edit -replace=github.com/go-webauthn/webauthn=github.com/go-webauthn/webauthn@v0.6.1-0.20230126043752-68b0aefad880

That did seem to resolve the issue.

Looking through the change in #95 , I think that for it to properly solve this issue in all cases, it would also need to replace '+' and '/' with '-' and '_' respectively.
That way it'll fully convert the standard base64 encoding used by encoding/json into the unpadded url safe encoding being used here.

The intention in #95 is to avoid compatibility issues specifically with padding, as it's reasonable to accommodate that I think. I'm willing to take a harder stance on Standard Base64 encoding as it's not permitted at all within the webauthn spec unless I'm mistaken?

Also I think your fix is still appropriate since we should only handle encoding with the web based (without padding) encoding per the spec.

Can you check #97 also solves your issue? I suspect it will. In which case I'll merge that before the others and release it as a patch.

Just to circle back and confirm: That did resolve the issue. Thanks a bunch!