GithubHelp home page GithubHelp logo

duo-labs / py_webauthn Goto Github PK

View Code? Open in Web Editor NEW
844.0 37.0 166.0 529 KB

Pythonic WebAuthn 🐍

Home Page: https://duo-labs.github.io/py_webauthn

License: BSD 3-Clause "New" or "Revised" License

Python 100.00%
webauthn python fido2 pip pypi duo security python3

py_webauthn's Introduction

py_webauthn

PyPI GitHub license Pythonic WebAuthn

A Python3 implementation of the server-side of the WebAuthn API focused on making it easy to leverage the power of WebAuthn.

This library supports all FIDO2-compliant authenticators, including security keys, Touch ID, Face ID, Windows Hello, Android biometrics...and pretty much everything else.

Installation

This module is available on PyPI:

pip install webauthn

Requirements

  • Python 3.8 and up

Usage

The library exposes just a few core methods on the root webauthn module:

  • generate_registration_options()
  • verify_registration_response()
  • generate_authentication_options()
  • verify_authentication_response()

Two additional helper methods are also exposed:

  • options_to_json()
  • base64url_to_bytes()

Additional data structures are available on webauthn.helpers.structs. These dataclasses are useful for constructing inputs to the methods above, and for providing type hinting to help ensure consistency in the shape of data being passed around.

Generally, the library makes the following assumptions about how a Relying Party implementing this library will interface with a webpage that will handle calling the WebAuthn API:

The examples mentioned below include uses of the options_to_json() helper (see above) to show how easily bytes values in registration and authentication options can be encoded to base64url for transmission to the front end.

The examples also include demonstrations of how to pass JSON-ified responses, using base64url encoding for ArrayBuffer values, into parse_registration_credential_json and parse_authentication_credential_json to be automatically parsed by the methods in this library. An RP can pair this with corresponding custom front end logic, or one of several frontend-specific libraries (like @simplewebauthn/browser, for example) to handle encoding and decoding such values to and from JSON.

Other arguments into this library's methods that are defined as bytes are intended to be values stored entirely on the server. Such values can more easily exist as bytes without needing potentially extraneous encoding and decoding into other formats. Any encoding or decoding of such values in the name of storing them between steps in a WebAuthn ceremony is left up to the RP to achieve in an implementation-specific manner.

Registration

See examples/registration.py for practical examples of using generate_registration_options() and verify_registration_response().

You can also run these examples with the following:

# See "Development" below for venv setup instructions
venv $> python -m examples.registration

Authentication

See examples/authentication.py for practical examples of using generate_authentication_options() and verify_authentication_response().

You can also run these examples with the following:

# See "Development" below for venv setup instructions
venv $> python -m examples.authentication

Development

Installation

Set up a virtual environment, and then install the project's requirements:

$> python3 -m venv venv
$> source venv/bin/activate
venv $> pip install -r requirements.txt

Testing

Python's unittest module can be used to execute everything in the tests/ directory:

venv $> python -m unittest

Auto-watching unittests can be achieved with a tool like nodemon.

All tests:

venv $> nodemon --exec "python -m unittest" --ext py

An individual test file:

venv $> nodemon --exec "python -m unittest tests/test_aaguid_to_string.py" --ext py

Linting and Formatting

Linting is handled via mypy:

venv $> python -m mypy webauthn
Success: no issues found in 52 source files

The entire library is formatted using black:

venv $> python -m black webauthn --line-length=99
All done! ✨ 🍰 ✨
52 files left unchanged.

py_webauthn's People

Contributors

agesagem avatar aj-dt avatar bschoenmaeckers avatar chandra158 avatar dependabot[bot] avatar dsanders11 avatar evilscientress avatar futureimperfect avatar gradyy avatar hawi74 avatar haydenth avatar jericks-duo avatar jlaine avatar jordan-wright avatar jpaniagualaconich avatar ljodal avatar masterkale avatar mdedonno1337 avatar pthimon avatar slokhorst avatar subyraman avatar warlo avatar woodruffw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

py_webauthn's Issues

Invalid Signature

Trying to get this library working with a Yubico Security Key (not the 2 version which supports FIDO2, that seems fairly recent). I keep running into Invalid signature received. when testing registration, even in the Flask demo. I'm digging into it now, but haven't had any luck so far. From what I can tell it shouldn't be a problem with usage as the security key is returning an attestation, but the library thinks the signature is wrong.

Minor data leak

The behavior on login is different depending on if the user exists (you are prompted to activate the device) versus does not exist (you are told so). This would allow an attacker to test candidate users to find if they have a registered device, and perhaps then target that user. (I know "Bob Smith" works for such-and-so, and "bsmith" is a valid name, time to steal his keyring with his fido2 device at lunch). It's a stretch, but it's probably best if the authentication pretends the username is valid no matter what, and gives the same response to both scenarios, so attackers can't use account validity as a filter.

SafetyNet attestation too strict for "now < timestamp_ms"

A recent error was thrown when trying to register an Android device:

Payload timestamp 1636585252672 was later than 1636585252000

The culprit was SafetyNet verification failing because the response appeared to come from 672ms in the future.

I need to bake in some kind of buffer on the now < payload.timestamp_ms check here to allow for a bit of time drift and network latency between the RP and Google's SafetyNet API.

server validation of credential failed: registration failed. error: registration rejected. error: unable to verify origin..

server validation of credential failed: registration failed. error: registration rejected. error: unable to verify origin..

I continuously get this error when attempting to register. I hosted the app, wriiten in Python(Flask), on heroku and the configurations are as follow:

RP_ID = 'nacesdecide.herokuapp.com'
RP_NAME = 'nacesdecides nacesdecide'
ORIGIN = 'https://nacesdecide.herokuapp.com/'

I don't know what to do to fix it currently. Besides, how do I specifically state that the registeration and assertion should use the fingerprint sensor of the device alone?

flask demo : Exception raised by webauthn_registration_response.verify() is not understandable

I'm new to webAuthn and i'm currently trying to run the flask demo app. I have python 3 (3.9.1) installed. but webauthn_credential = webauthn_registration_response.verify() is raising the following exception in console.

Server validation of credential failed: Registration failed. Error: Registration rejected. Error: Expecting value: line 1 column 1 (char 0).

I'm not understanding the actual problem that what value is missing. I'm also adding webauthn_registration_response
response
reg_response

use of attrs breaks compatibility with fastAPI

Continued from discussion at #111 (comment).

In v1.2.0 this library switched from pydantic to attrs+cattrs.

I can understand why opinionated validation should not be part of a library like this, but switching from pydantic to attrs+cattrs doesn't change that, it just switches from one library to another.

A big down side of this is that it breaks "out of the box" compatibility with FastAPI. FastAPI is the third most popular and fastest growing web framework for python. Pydantic is not just used in FastAPI, there are libraries to support it's use in flask, django and other frameworks. By contracts, while attrs is popular, it's not generally used for external data validation, cattr is not as popular or widely used as either attr or pydantic fwiw.

I propose that we either:

  • switch back to pydantic - I'll help if there are some issues with pydantic or how it's used
  • or, switch to dataclasses which are a standard library, no-validation alternative to attr, cattr and pydantic <-- this would be my recommendation

An added advantage of dataclasses is that pydantic has support for validation of dataclasses so py_webauthn would go back to workout out of the box with fastAPI and pydantic. If cattrs is in activate development, it too should support validation of dataclasses, but I can't find anything from a quick search.


Admission: I'm the original developer and maintainer of pydantic

Add a "require resident key" option

There is currently no option to require a resident key (or I don't know how to set it). Is a library change needed, or do I need something on my end?

Production?

Hi, guys.
Is it safe to use this for production?

Flask Demo: Registration is not executed

Hi,
I cloned the current version and ran the flask demo (running in venv, with python3, all requirements installed). Opening the page in the browser i tried to registrate/authenticate, but nothing happened or got displayed (also no error messages were displayed). The .db stays empty.

Terminal output for the flask demo:

$ python3 app.py
* Serving Flask app "app" (lazy loading)

 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on https://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 153-743-656
127.0.0.1 - - [04/Oct/2019 15:48:59] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [04/Oct/2019 15:48:59] "GET /static/js/lib/base64.js HTTP/1.1" 200 -
127.0.0.1 - - [04/Oct/2019 15:48:59] "GET /static/css/base.css HTTP/1.1" 200 -
127.0.0.1 - - [04/Oct/2019 15:48:59] "GET /static/js/webauthn.js HTTP/1.1" 200 -
127.0.0.1 - - [04/Oct/2019 15:48:59] "GET /favicon.ico HTTP/1.1" 404 -
127.0.0.1 - - [04/Oct/2019 15:49:06] "POST /webauthn_begin_activate HTTP/1.1" 200 -

Tested with Firefox 69.0.1 (64-bit) and Chromium Version 77.0.3865.90 (Official Build) snap (64-bit)

Any suggestions to get it run (or debug) would be great!

I got a bit further by reading the web console:
Error creating credential: DOMException: "The operation is insecure."
didClickRegister https://0.0.0.0:5000/static/js/webauthn.js:64

Attribute error

Hello,

I downloaded the zip into a folder on my mac, then I followed your suggestions to run the demo. The html form does appear on the localhost:5000, but when I enter my name and my Display Name and hit the reg button, the terminal throws out this:

Traceback (most recent call last):
File "/Users/irre/dev/webauthn/.venv/lib/python3.7/site-packages/flask/app.py", line 2309, in call
return self.wsgi_app(environ, start_response)
File "/Users/irre/dev/webauthn/.venv/lib/python3.7/site-packages/flask/app.py", line 2295, in wsgi_app
response = self.handle_exception(e)
File "/Users/irre/dev/webauthn/.venv/lib/python3.7/site-packages/flask/app.py", line 1741, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/irre/dev/webauthn/.venv/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
raise value
File "/Users/irre/dev/webauthn/.venv/lib/python3.7/site-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
File "/Users/irre/dev/webauthn/.venv/lib/python3.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/irre/dev/webauthn/.venv/lib/python3.7/site-packages/flask/app.py", line 1718, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/irre/dev/webauthn/.venv/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
raise value
File "/Users/irre/dev/webauthn/.venv/lib/python3.7/site-packages/flask/app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/irre/dev/webauthn/.venv/lib/python3.7/site-packages/flask/app.py", line 1799, in dispatch_request
return self.view_functionsrule.endpoint
File "/Users/irre/dev/webauthn/app.py", line 90, in webauthn_begin_activate
make_credential_options = webauthn.WebAuthnMakeCredentialOptions(
AttributeError: module 'webauthn' has no attribute 'WebAuthnMakeCredentialOptions'

Nothing else happens. What should I do? thx

Enhance VerifiedAuthentication struct

Currently, on successful verify_authentication_response, a VerifiedAuthentication struct is returned - this currently has the credential_id and sign_count.
It would be very useful for RPs if they also got the AuthenticatorDataFlags so that decisions about whether the authenticator satisfied single or multi-factor authentication could be made.

I am more than happy to issue a PR if you think this is a good idea.

New release

Please release a new version of the library, I find myself needing user verification functionality but it is not in the last release.

AssertionResponse verification assumes signature is hex formatted

In the WebAuthnAssertionResponse .verify() function, the assertion_response.signature attribute is assumed to be in hex format, despite all other attributes being b64 encoded.

sig = binascii.unhexlify(self.assertion_response.get('signature'))

I guess this is a result of the sample implementation encoding the signature as hex rather than b64:

signature: hexEncode(sig),

This is bit bizzare, since all other attributes are b64 encoded (in the webauthn variant).

Incorrect AssertionResponse userHandle verification

In the WebAuthnAssertionResponse .verify() function, the assertion_response.userHandle attribute is incorrectly compared against the webauthn_user.username instead of webauthn_user.user_id:

if not user_handle == self.webauthn_user.username:

Per https://developer.mozilla.org/en-US/docs/Web/API/AuthenticatorAssertionResponse .userHandle is:

The userHandle read-only property of the AuthenticatorAssertionResponse interface is an ArrayBuffer object which is an opaque identifier for the given user. Such an identifier can be used by the relying party's server to link the user account with its corresponding credentials and other data.

The same value may be found on the id property of the options.user object (used for the creation of the PublicKeyCredential instance).

Also confirmed by https://www.w3.org/TR/webauthn/#user-handle:

User Handle
The user handle is specified by a Relying Party, as the value of user.id, and used to map a specific public key credential to a specific user account with the Relying Party. Authenticators in turn map RP IDs and user handle pairs to public key credential sources.

A user handle is an opaque byte sequence with a maximum size of 64 bytes. User handles are not meant to be displayed to users. The user handle SHOULD NOT contain personally identifying information about the user, such as a username or e-mail address; see Β§14.9 User Handle Contents for details.

Which makes userHandle the opaque user_id, not the username.

This is also evidenced by the userHandle response I receive from navigator.credentials.get() exactly matching the opaque userID I passed into navigator.credentials.create().

Use attestation 'none' as default ?

The flask_demo results in the client asking the user for permission to view information about their keys.
I gusss this is because internally; py_webauthn uses direct as the default attestation:

display_name, icon_url, timeout=60000, attestation='direct',

The spec seems to indicate that none is the default: https://www.w3.org/TR/webauthn/#dom-attestationconveyancepreference-none
What is the reasoning behind choosing direct as the default in this library?

The demo itself does not seem to enforce/require attestation when direct is used. The registration is successful even when the client blocks the request to gather key data. Is this the correct behaviour ? If we want to use direct as default, maybe the demo should be updated to require this and then do something with this data?

COSE_ALG_RS256 Deprecated

I was just going through the WebAuthnMakeCredentialOptions.registration_dict and saw COSE_ALG_RS256 (-257) being used as the second priority for pubKeyCredParams. According to "IANA COSE Algorithms" specification this algorithm has been deprecated. Am I missing something over here or is this actually being used?

User registration fails in latest Firefox

Registration fails in demo for user in Firefox 67.0.4 with:

Error creating credential: DOMException: "The operation is insecure." webauthn.js:64:24
    didClickRegister https://127.0.0.1:5000/static/js/webauthn.js:64

Chrome and Firefox

Hi I am using this demo by using Chrome Version 88.0.4324.104 (Official Build) (64-bit) which doesn't work whereas using FIrefox 84.0.2 (64-bit) is working.
Can you propose any solution to it?

Thanks

Migrating from U2F

I run an RP with many keys registered using the U2F API, and now am trying to update to webauthn as the U2F api is being removed from chrome. Do you have a a guide on how to do this?

My existing registered keys have a public key in a format like A7vTrxM6bfrFNJxSZSfKSL1LY3gcFz5TIM-VX8NZ9Et5zKdKy4sENZsdQPAPGnF2OGt9GNNYYnfj8CzyH4NKJ8, a key handle in a format like xMm8q_Yv30fRCDpEGmipy9WlgVr0rJHiPB9_zCg3V4_ev4wrtcEqYj4voAe_Mjjt4n78_eVlGqDdMv365N0Q4g, and an app id in a format like https://localhost:8000.

  1. How do these values map to the options for generate_authentication_options?
  2. Is it possible to store newly-registered webauthn keys as those type of values, so I don't have to change my database schema?

This is for the open-source project https://github.com/gavinwahl/django-u2f

Thank you

Trusted Attestation Roots Not in pip Package

It looks like the trusted_attestation_roots directory is not present in the package on pip. Is this intentional? I was previously using webauthn installed directly from GitHub, so the directory was present.

CSRF token is missing

Hello,
when verifying the registration I get this error that the CSRF token is missing. The console is printing:

webauthn.js:83 Server validation of credential failed: SyntaxError: Unexpected token < in JSON at position 0

and the response body for "verify_credential_info" is showing:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>400 Bad Request</title>
<h1 >Bad Request</h1 >
<p>The CSRF token is missing.</p>

How and where can I add the csrf_token so that it is recognized as such?
I would be super grateful for any help!

Loggin in with authenticator

Could you show a demo about the login process with preferably an App as authenticator? Or some hints on how to set this up with the code provided? I'm lost here sadly.

Is pydantic the right choice (mongoengine issue)

This library is middleware and will be used in who knows how many projects with lots of layers above and below it. Pydantic isn't very pythonic in what it allows. I am testing flask-security as a RP using mongoengine as a backend. It implements its BinaryField as a clever subclass of 'bytes' - but pydantic doesn't understand it.

Here is a small repro:

from pydantic import BaseModel

class Test1(BaseModel):
    id: bytes

class Binary(bytes):
    def __new__(cls, data):
        self = bytes.__new__(cls, memoryview(data).tobytes())
        return self

A = Binary(b"hello")
T = Test1(id=A)

The Binary class is from mongoengine using the bson package. If you run this you get:

Traceback (most recent call last):
  File "/Users/jwag/fs/flask-security/flask_security/pydantic_test.py", line 17, in <module>
    T = Test1(id=A)
  File "pydantic/main.py", line 406, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for Test1
id
  Expected bytes, got Binary (type=type_error)

Pydantic doesn't allow subclasses (very un-pythonic IMHO) - and the only things I solution I found on the web was to write custom pydantic validators that check for isinstance()...

Other 'create typed data structures easily' methods such as builtin dataclasses and attr don't have this issue (they don't validate by default).

Ultimately I suppose I am asking - should a middleware package require validation? or let the developer decide - by using mypy etc..

webauthn_assertion_response.verify incorrect padding on public key

Hello! Great library, I'm using it for some projects I'm working on. I'm having some problems getting the webauthn_assertion_response.verify method to work ONLY on the cases where I am completing the assertion of a key. The error is below.

pipe_web          | Traceback (most recent call last):
pipe_web          |   File "/usr/local/lib/python3.6/dist-packages/webauthn/webauthn.py", line 899, in verify
pipe_web          |     _webauthn_b64_decode(credential_public_key))
pipe_web          |   File "/usr/local/lib/python3.6/dist-packages/webauthn/webauthn.py", line 1173, in _webauthn_b64_decode
pipe_web          |     return base64.urlsafe_b64decode(encoded)
pipe_web          |   File "/usr/lib/python3.6/base64.py", line 133, in urlsafe_b64decode
pipe_web          |     return b64decode(s)
pipe_web          |   File "/usr/lib/python3.6/base64.py", line 87, in b64decode
pipe_web          |     return binascii.a2b_base64(s)
pipe_web          | binascii.Error: Incorrect padding
pipe_web          | 
pipe_web          | During handling of the above exception, another exception occurred:
pipe_web          | 
pipe_web          | Traceback (most recent call last):
pipe_web          |   File "/usr/local/lib/python3.6/dist-packages/flask/app.py", line 2463, in __call__
pipe_web          |     return self.wsgi_app(environ, start_response)
pipe_web          |   File "/usr/local/lib/python3.6/dist-packages/flask/app.py", line 2449, in wsgi_app
pipe_web          |     response = self.handle_exception(e)
pipe_web          |   File "/usr/local/lib/python3.6/dist-packages/flask/app.py", line 1866, in handle_exception
pipe_web          |     reraise(exc_type, exc_value, tb)
pipe_web          |   File "/usr/local/lib/python3.6/dist-packages/flask/_compat.py", line 39, in reraise
pipe_web          |     raise value
pipe_web          |   File "/usr/local/lib/python3.6/dist-packages/flask/app.py", line 2446, in wsgi_app
pipe_web          |     response = self.full_dispatch_request()
pipe_web          |   File "/usr/local/lib/python3.6/dist-packages/flask/app.py", line 1951, in full_dispatch_request
pipe_web          |     rv = self.handle_user_exception(e)
pipe_web          |   File "/usr/local/lib/python3.6/dist-packages/flask/app.py", line 1820, in handle_user_exception
pipe_web          |     reraise(exc_type, exc_value, tb)
pipe_web          |   File "/usr/local/lib/python3.6/dist-packages/flask/_compat.py", line 39, in reraise
pipe_web          |     raise value
pipe_web          |   File "/usr/local/lib/python3.6/dist-packages/flask/app.py", line 1949, in full_dispatch_request
pipe_web          |     rv = self.dispatch_request()
pipe_web          |   File "/usr/local/lib/python3.6/dist-packages/flask/app.py", line 1935, in dispatch_request
pipe_web          |     return self.view_functions[rule.endpoint](**req.view_args)
pipe_web          |   File "/usr/local/lib/python3.6/dist-packages/flask_login/utils.py", line 261, in decorated_view
pipe_web          |     return func(*args, **kwargs)
pipe_web          |   File "/app/web/views/u2f.py", line 114, in u2f_assert_complete
pipe_web          |     sign_count = webauthn_assertion_response.verify()
pipe_web          |   File "/usr/local/lib/python3.6/dist-packages/webauthn/webauthn.py", line 1093, in verify
pipe_web          |     'Authentication rejected. Error: {}.'.format(e))
pipe_web          | webauthn.webauthn.AuthenticationRejectedException: Authentication rejected. Error: Incorrect padding.

And here is the calling code. My pub_key is being stored in the method used in the demo flask app (which doesnt look like it is being stored as a base64 object into my db): https://github.com/duo-labs/py_webauthn/blob/master/flask_demo/app.py

  webauthn_user = webauthn.WebAuthnUser(
    current_user.u2f_key,
    current_user.name,
    current_user.name,
    U2F_ICON,
    current_user.credential_id,
    current_user.pub_key,
    current_user.sign_count, 
    current_user.rp_id)

  webauthn_assertion_response = webauthn.WebAuthnAssertionResponse(
    webauthn_user,
    assertion,
    challenge,
    U2F_RELAYING_ORIGIN,
    uv_required=False)

  sign_count = webauthn_assertion_response.verify()

Thanks! Any options you can suggest would be great.

_verify_token_binding_id does not actually verify the tokenBinding.id property

https://github.com/duo-labs/py_webauthn/blob/master/webauthn/webauthn.py#L1285

It only checks the tokenBinding.status and never uses the token_binding_id variable or verify the id as the function name and documentations suggests.

Perhaps the implementer should implement the verification? If so, the function is inappropriate and should be renamed to describe the functionality it provides (and be documented to describe this too). In it's current form it misleads the implementer into believing they are using the function to actually do verification and the intent of the function implies in it's documentation this is also the function - however it does not..

So what is the proper use by implementers? What is the function actually meant to do? Is this a defect? Is it named and documented incorrectly and it is working as intended (but very differently from the name and description)?

Client Extensions Optional?

When writing the code for #16 I ran into an issue with the client extensions being expected to be sent to the server, so I made that bit optional. It's not clear to me from the spec if the server should always expect to find client extension outputs (even if they're just an empty map) or if they may be missing all together.

Also, right now the loc extension is hardcoded in the make credential code path, would be nice for that to be configurable.

Getting error: Unexpected RP ID hash while all rp_id's are equal

Hi, first of all thank you for the massive update! I was just in progress of implementing the old one so it was quite the surprise!

I've gotten up to authentication, but I'm running into the following error:
webauthn.helpers.exceptions.InvalidAuthenticationResponse: Unexpected RP ID hash

I'm testing locally, so anywhere (both registration and authentication) I had to fill in the rp_id/expected_rp_id I've filled in 'localhost'.

The values that verify_authentication_response is checking are as follows:

auth_data.rp_id_hash
b'SZYN5YgOjGh0NBcPZHZgW4_krrmihjLH'
expected_rp_id_hash
b'I\x96\r\xe5\x88\x0e\x8cht4\x17\x0fdv`[\x8f\xe4\xae\xb9\xa2\x862\xc7\x99\\\xf3\xba\x83\x1d\x97c'

The expected_rp_id_hash decoded using bytes_to_base64url gives me
SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2M
which seems to be the rp_id_hash from auth data with some extra data at the end.
What's strange is it is working in registration and as far as I can tell from your code there's no difference in the hashing method, so is there something I can do on my end?

AT_NONE attestation type not handled consistently

When using "none" as the attestation format, the verify function will accept this format all the way through to step 16, but then is missing the format from the elif statements, so it will always raise RegistrationRejectedException('Unknown attestation type.'), see:

if attestation_type == AT_SELF_ATTESTATION:
if not self.self_attestation_permitted:
raise RegistrationRejectedException(
'Self attestation is not permitted.')
elif attestation_type == AT_ATTESTATION_CA:
raise NotImplementedError(
'Attestation CA attestation type is not currently supported.'
)
elif attestation_type == AT_ECDAA:
raise NotImplementedError(
'ECDAA attestation type is not currently supported.')
elif attestation_type == AT_BASIC:
if self.trusted_attestation_cert_required:
if not _is_trusted_attestation_cert(
trust_path, trust_anchors):
raise RegistrationRejectedException(
'Untrusted attestation certificate.')
else:
raise RegistrationRejectedException(
'Unknown attestation type.')

Is project actively developed?

Hi!
Latest issues are left without comments from project developers. Some important things are not implemented, like additional attestation formats etc. PR's seems also not reviewed. Is there some development in this project?

User presence should always be verified

Currently, the USER_PRESENT flag is only required to be 0x01 when user verification is not requested. The latest WebAuthn specification requires that USER_PRESENT is always set.

JSON object must be str, not 'bytes' (SOLVED)

I'm testing the Flask demo of this project, and ran into this issue upon registering and verifying keys: (from the browser console)

Error when validating assertion on server: Assertion failed. Error: Authentication rejected. Error: the JSON object must be str, not 'bytes'.

After some careful sleuthing, I have pinpointed the problem to webauthn.py, lines 556 and 874. json.loads is passed decoded_cd, which is bytes type and not str. This can be easily fixed by a conversion between the types on both lines:

c = json.loads(decoded_cd.decode('utf-8'))

Otherwise, this project is fantastic; I've tried almost every other Python Webauthn demo without success.

Type information not included in package

Hello!

I'm using this package in a project where type annotations and type checking is used extensively. It's desirable that the sub-dependencies use type annotations as well.

py_webauthn does have type annotations but mypy doesn't seem to pick them up. It seems that the type annotations isn't packaged properly.

Would it be possible to package the type information as per PEP 561?

Docker image fails to built

I tried to built the docker image but it failed on step 7:

Step 7/8 : RUN /app/flask_demo/create_db.py
 ---> Running in 5cee43332efa
/usr/bin/env: β€˜python\r’: No such file or directory
ERROR: Service 'app' failed to build: The command '/bin/sh -c /app/flask_demo/create_db.py' returned a non-zero code: 127

Adding a python at the beginning solved it:

RUN python /app/flask_demo/create_db.py

Can someone confirm this? Happend with Windows 10.

Question/Idea - Separation of User And Credential Models

Hi,

Firstly, let me thank you for this fantastic project; it has been extremely helpful.

Question: In the flask demo app, would it make sense to turn the User model into User and Credential models instead? in order to support multiple credential registrations against one user object?

Thanks,
Pouya

Try to run the tests got error "Unable to verify origin" for test script

Hi,

When I try to test the library with the given webauthn_test.py. I got the following error all the cases

raise RegistrationRejectedException('Unable to verify origin.')
webauthn.webauthn.RegistrationRejectedException: Unable to verify origin.

In the webauthn_test.py file, I change the RPID and origin to
RP_ID = 'localhost'
ORIGIN = 'https://localhost:5000' Respectively.

But still unable to complete the tests. ??

Expose data structures publically

First - thanks so much for updating this library. I have successfully integrated it into Flask-Security...

The main APIs: e.g. generate_registration_options all are nicely typed using data structures defined via pydantic in helpers/structs.py. Currently those are not listed in init.py and thus are not 'declared' public.

This is requesting that the structs and exceptions be listed in init.py.

Signaling my intention to close all existing issues and PR's

PR #95 includes an entirely new library that has zero code in common with what's on master. Once I merge that PR in it is my intention to close all existing issues and pull requests as they will no longer be applicable. I hope the prospects of a brand new library will full WebAuthn support will be enough of an olive branch to those of you who logged those issues and/or worked hard to prepare PR's only for them to go stale.

Thank you for your continued interest in this library πŸ™‡

Registration fails with python extension

Traceback (most recent call last):
File "/home/martijn/.local/lib/python3.5/site-packages/flask/app.py", line 2309, in call
return self.wsgi_app(environ, start_response)
File "/home/martijn/.local/lib/python3.5/site-packages/flask/app.py", line 2295, in wsgi_app
response = self.handle_exception(e)
File "/home/martijn/.local/lib/python3.5/site-packages/flask/app.py", line 1741, in handle_exception
reraise(exc_type, exc_value, tb)
File "/home/martijn/.local/lib/python3.5/site-packages/flask/_compat.py", line 35, in reraise
raise value
File "/home/martijn/.local/lib/python3.5/site-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
File "/home/martijn/.local/lib/python3.5/site-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/home/martijn/.local/lib/python3.5/site-packages/flask/app.py", line 1718, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/home/martijn/.local/lib/python3.5/site-packages/flask/_compat.py", line 35, in reraise
raise value
File "/home/martijn/.local/lib/python3.5/site-packages/flask/app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "/home/martijn/.local/lib/python3.5/site-packages/flask/app.py", line 1799, in dispatch_request
return self.view_functionsrule.endpoint
File "/home/martijn/git/py_webauthn/flask_demo/app.py", line 130, in verify_credential_info
challenge = session['challenge']
File "/home/martijn/.local/lib/python3.5/site-packages/werkzeug/local.py", line 377, in
getitem = lambda x, i: x._get_current_object()[i]
File "/home/martijn/.local/lib/python3.5/site-packages/flask/sessions.py", line 83, in getitem
return super(SecureCookieSession, self).getitem(key)
KeyError: 'challenge'

Python 3

Could you clarify in setup.py whether this project supports python 3 currently or intends to support python 3 in the future?

Support for Ed25519 signature verification

Hello Duo Labs,

py_webauthn currently does not support Ed25519 signature verification while some authenticators (like yubikey) generate these signatures. This is something we intend to use so would like to know if you have any plans to support this?

In case you don't plan to support would you be open to accepting a PR with this update?

bytes encoding doesn't work with browsers

Thanks so much for the library, just what I needed.

Also amazing to see pydantic used in libraries like this πŸ˜„.


I'm having trouble using the output of webauthn.generate_registration_options in the browser, in particular I need ArrayBuffers not strings for challenge and user.id.

The problem is that you're currently encoding strings using urlsafe_b64encode in options_to_json() which is not compatible with atob().

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:

const asArrayBuffer = v => Uint8Array.from(atob(v), c => c.charCodeAt(0))

Alternatively, if you have an elegant way to convert the output of options_to_json to make it suitable for use with navigator.credentials.create() in JS, it might be good to add it to the docs.

Signature counters should be optional.

The WebAuthN spec seems to allow for authenticators which do not support the signature counter (see step 17 in 7.2 Verifying an Authentication Assertion), i.e. the signature counter should only be checked if it is non-zero. However, the following code:

if not sign_count:

assumes it is non-zero. I think this line should be preceded by code along the lines of:

           if sign_count == 0 and self.webauthn_user.sign_count == 0:
               return sign_count

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.