Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for extensions #104

Open
jwag956 opened this issue Nov 8, 2021 · 5 comments
Open

Support for extensions #104

jwag956 opened this issue Nov 8, 2021 · 5 comments

Comments

@jwag956
Copy link

jwag956 commented Nov 8, 2021

My reading of https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#sctn-authenticator-credential-properties-extension
implies that if I as a RP want to know for example, if the authenticator created a resident key - the RegistrationCreateOptions should contains appropriate values in:
AuthenticationExtensionsClientInputs extensions;

That doesn't seem to be supported....

@MasterKale
Copy link
Collaborator

There's nothing about py_webauthn that prevents you from leveraging extensions. For now, though, you need to manually add the extensions to the options before you send them to the front end. Something like this:

# Generate options as usual
simple_registration_options = generate_registration_options(
    rp_id="example.com",
    rp_name="Example Co",
    user_id="12345",
    user_name="bob",
)
# Prepare to transmit to front end as JSON
options_json = options_to_json(simple_registration_options)
# Add in the extensions you want to leverage
options_json["extensions"] = {"credProps": True}

On the front end you have to remember to call getClientExtensionResults() on the value that comes out of navigator.credentials.create()/navigator.credentials.get(), which in the case of credProps should give you back an object like this indicating whether a resident key was created:

{
  credProps: {
    rk: true
  }
}

At which point you can include that in the response you send back to your RP however you want. It's not quite as easy as handing things off to py_webauthn, that might come later, but hopefully you won't find this too onerous to implement yourself for now 🙇

Simplifying the use of extensions has been something I've wanted to do in my other WebAuthn library, and now in py_webauthn, but haven't spent a whole lot of time on trying to architect because of how spotty support is for extensions across browsers.

@jwag956
Copy link
Author

jwag956 commented Nov 8, 2021

Thanks for the detailed instructions - not too onerous at all! I find that while jumping back and forth through the spec I lose track of what is JSON and what is encoded.

Feel free to close this or leave it as a 'feature' request.

@jwag956
Copy link
Author

jwag956 commented Nov 11, 2021

As a followup - it isn't quite as simple/nice:

options_to_json returns a string - you have to do something like:

        co_json = json.loads(webauthn.options_to_json(credential_options))
        co_json["extensions"] = {"credProps": True}
        xxx = json.dumps(co_json)

And on the response you can't just use parse_raw - you have to decode the entire response:

        response_full = json.loads(response)
        self.extensions = response_full.get("extensions", None)

Again not a super big deal - but unfortunate that can't just use the help parse/encode utilities.

Note that one cant do:

credential_options = webauthn.generate_registration_options(...)
setattr(credential_options, "extensions", dict(xxx)) 

since pydantic doesn't like that (which I suppose is the point of using pydantic).

@jeriox
Copy link

jeriox commented Apr 3, 2022

We also stumbled upon the extension problem while working with large blobs. We patched the library with appropriate structs and additional parameters for generate_registration_options. You can find these changes at #128, happy to discuss whether this approach is fine for you

@MasterKale
Copy link
Collaborator

Copy-pasting some brainstorming on extension support I wrote down in #128:

...I've been meaning to sit down and think about extension support with a bit more abstract API that wouldn't require you to know all of the values for a given extension.

For example, why not have an optional require_large_blob_support: bool argument to generate_registration_options(), since the only option in AuthenticationExtensionsLargeBlobInputs that's valid for registration is support? Technically require_large_blob_support=False would do the same thing as setting support: "preferred" because the spec itself states:

Otherwise (i.e. support is absent or has the value preferred):

So there's no need to even include the value if you're not going to set it to "required".

And for authentication you can specify either read or write, but not both. Therefore I could see adding a write_large_blob: Optional[bytes] = None and read_large_blog: bool = False to generate_authentication_options(), and error out when both are set, so that the developer using the method doesn't need to be aware of the intricacies of the spec - we can remove most of the footguns for them.

These are the angles from which I was going to approach adding support for extensions like largeBlob. Since you're interested in the feature, though, I'm curious to hear what you think about this approach.

This is specifically about largeBlob extension support, but this is the kind of strategy I want to use when approaching adding extension support in this library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants