Closed samuelcolvin closed 2 years ago
I use this that I got from the older version of this package (they used to have a complete flask example):
`const transformCredentialCreateOptions = (credentialCreateOptionsFromServer) => { let {challenge, user} = credentialCreateOptionsFromServer; user.id = Uint8Array.from( atob(credentialCreateOptionsFromServer.user.id .replace(/_/g, "/") .replace(/-/g, "+") ), c => c.charCodeAt(0));
challenge = Uint8Array.from(
atob(credentialCreateOptionsFromServer.challenge
.replace(/\_/g, "/")
.replace(/\-/g, "+")
),
c => c.charCodeAt(0));
const transformedCredentialCreateOptions = Object.assign(
{}, credentialCreateOptionsFromServer,
{challenge, user});
return transformedCredentialCreateOptions;
}`
The spec specifically mentions base64url as a dependency, and the encoding is used throughout. For sake of adherence to the spec I chose to use base64url as well in options_to_json()
. You're right that base64url is not browser-JS friendly, and this dependency is one of my dev-focused gripes with the spec.
To tackle this I'd personally recommend using one of a couple of JS libraries that expect base64url and are capable of transforming it prior to triggering WebAuthn API calls:
I'm of course biased for my own library (the second one), and I know for a fact that it works fine with the output from options_to_json()
. Here's a gist showing how I combined py_webauthn with @simplewebauthn/browser on the front end to invoke WebAuthn API without needing to juggle an encoding type:
https://gist.github.com/MasterKale/d709e580a063887c33de9b73d509dec1
I'm going to close this issue for now assuming no news is good news. Please feel free to reopen if needed.
Humm, I'm not sure you're right about the spec requiring the use of urlsafe_b64encode
The term Base64url Encoding refers to the base64 encoding using the URL- and filename-safe character set defined in Section 5 of [RFC4648], with all trailing '=' characters omitted (as permitted by Section 3.2) and without the inclusion of any line breaks, whitespace, or other additional characters.
This implies that you should be safe to use base64.b64encode(...).strip(b'=')
.
Indeed from the above your use of base64.urlsafe_b64encode(...)
is explicitly NOT compliant with the above part of the spec since urlsafe_b64encode()
includes trailing =
.
I think this issue should be reopened.
Indeed from the above your use of
base64.urlsafe_b64encode(...)
is explicitly NOT compliant with the above part of the spec sinceurlsafe_b64encode()
includes trailing=
.
That fun fact is why I included a bytes_to_base64url() helper (and it's corresponding base64url_to_bytes
which encapsulates/benefits from other quirks about urlsafe_b64decode):
def bytes_to_base64url(val: bytes) -> str:
"""
Base64URL-encode the provided bytes
"""
return urlsafe_b64encode(val).decode("utf-8").replace("=", "")
Thanks, sorry I missed that.
By the way, you can make this code slightly more performance and expressive with
return urlsafe_b64encode(val).rstrip(b'=').decode()
Since =
can only appear at the end of the string.
Thanks so much for the library, just what I needed.
Also amazing to see pydantic used in libraries like this :smile:.
I'm having trouble using the output of
webauthn.generate_registration_options
in the browser, in particular I needArrayBuffer
s not strings forchallenge
anduser.id
.The problem is that you're currently encoding strings using
urlsafe_b64encode
inoptions_to_json()
which is not compatible withatob()
.Would you consider switching to plain
base64.b64encode
, or at least making it configurable?With that I can using the following to convert strings to
ArrayBuffer
:Alternatively, if you have an elegant way to convert the output of
options_to_json
to make it suitable for use withnavigator.credentials.create()
in JS, it might be good to add it to the docs.