gavinwahl / django-u2f

FIDO U2F security token support for Django
BSD 2-Clause "Simplified" License
166 stars 32 forks source link

Error when adding U2F key via Firefox #24

Closed justinmayer closed 6 years ago

justinmayer commented 7 years ago

Many thanks, Gavin, for all your work on django-u2f. I was very pleasantly surprised to see the inclusion of the demo project, which by the way functioned perfectly right out of the box. Bravo!

With full realization that the ReadMe mentions Chrome/Chromium as the only supported browser, I could not help but try Firefox with the U2F add-on (source). When I tapped the U2F device to finalize adding the key, I got an error: ValueError: Missing required fields: version (full traceback follows below).

Interestingly, if I use Chromium to add the U2F key, I can subsequently use Firefox and the U2F add-on to log in successfully.

That said, it would be nice to be able to use Firefox to add U2F keys as well. Do you have any idea what might be behind the error, and perhaps what might be done to fix it?

← Tap triangle to toggle traceback display Traceback (most recent call last): File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/contrib/staticfiles/handlers.py", line 63, in __call__ return self.application(environ, start_response) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/wsgi.py", line 157, in __call__ response = self.get_response(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/base.py", line 124, in get_response response = self._middleware_chain(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 43, in inner response = response_for_exception(request, exc) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 93, in response_for_exception response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info()) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 139, in handle_uncaught_exception return debug.technical_500_response(request, *exc_info) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django_extensions/management/technical_response.py", line 6, in null_technical_500_response six.reraise(exc_type, exc_value, tb) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/six.py", line 686, in reraise raise value File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner response = get_response(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/utils/deprecation.py", line 140, in __call__ response = self.get_response(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 43, in inner response = response_for_exception(request, exc) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 93, in response_for_exception response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info()) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 139, in handle_uncaught_exception return debug.technical_500_response(request, *exc_info) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django_extensions/management/technical_response.py", line 6, in null_technical_500_response six.reraise(exc_type, exc_value, tb) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/six.py", line 686, in reraise raise value File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner response = get_response(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/utils/deprecation.py", line 140, in __call__ response = self.get_response(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 43, in inner response = response_for_exception(request, exc) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 93, in response_for_exception response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info()) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 139, in handle_uncaught_exception return debug.technical_500_response(request, *exc_info) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django_extensions/management/technical_response.py", line 6, in null_technical_500_response six.reraise(exc_type, exc_value, tb) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/six.py", line 686, in reraise raise value File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner response = get_response(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/utils/deprecation.py", line 140, in __call__ response = self.get_response(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 43, in inner response = response_for_exception(request, exc) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 93, in response_for_exception response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info()) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 139, in handle_uncaught_exception return debug.technical_500_response(request, *exc_info) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django_extensions/management/technical_response.py", line 6, in null_technical_500_response six.reraise(exc_type, exc_value, tb) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/six.py", line 686, in reraise raise value File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner response = get_response(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/utils/deprecation.py", line 140, in __call__ response = self.get_response(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 43, in inner response = response_for_exception(request, exc) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 93, in response_for_exception response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info()) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 139, in handle_uncaught_exception return debug.technical_500_response(request, *exc_info) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django_extensions/management/technical_response.py", line 6, in null_technical_500_response six.reraise(exc_type, exc_value, tb) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/six.py", line 686, in reraise raise value File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner response = get_response(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/utils/deprecation.py", line 140, in __call__ response = self.get_response(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 43, in inner response = response_for_exception(request, exc) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 93, in response_for_exception response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info()) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 139, in handle_uncaught_exception return debug.technical_500_response(request, *exc_info) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django_extensions/management/technical_response.py", line 6, in null_technical_500_response six.reraise(exc_type, exc_value, tb) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/six.py", line 686, in reraise raise value File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner response = get_response(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/utils/deprecation.py", line 140, in __call__ response = self.get_response(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 43, in inner response = response_for_exception(request, exc) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 93, in response_for_exception response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info()) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 139, in handle_uncaught_exception return debug.technical_500_response(request, *exc_info) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django_extensions/management/technical_response.py", line 6, in null_technical_500_response six.reraise(exc_type, exc_value, tb) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/six.py", line 686, in reraise raise value File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner response = get_response(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/utils/deprecation.py", line 140, in __call__ response = self.get_response(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 43, in inner response = response_for_exception(request, exc) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 93, in response_for_exception response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info()) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 139, in handle_uncaught_exception return debug.technical_500_response(request, *exc_info) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django_extensions/management/technical_response.py", line 6, in null_technical_500_response six.reraise(exc_type, exc_value, tb) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/six.py", line 686, in reraise raise value File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner response = get_response(request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/base.py", line 187, in _get_response response = self.process_exception_by_middleware(e, request) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/core/handlers/base.py", line 185, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/contrib/auth/decorators.py", line 23, in _wrapped_view return view_func(request, *args, **kwargs) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/views/generic/base.py", line 68, in view return self.dispatch(request, *args, **kwargs) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django_u2f/views.py", line 90, in dispatch return super(AddKeyView, self).dispatch(request, *args, **kwargs) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/views/generic/base.py", line 88, in dispatch return handler(request, *args, **kwargs) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django/views/generic/edit.py", line 183, in post return self.form_valid(form) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/django_u2f/views.py", line 115, in form_valid device, attestation_cert = u2f.complete_registration(request, response) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/u2flib_server/u2f.py", line 45, in complete_registration return U2fRegisterRequest.wrap(request).complete(response, valid_facets) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/u2flib_server/model.py", line 416, in complete resp = RegisterResponse.wrap(response) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/u2flib_server/model.py", line 261, in wrap return data if isinstance(data, cls) else cls(data) File "/virtualenvs/u2fdemo/lib/python3.6/site-packages/u2flib_server/model.py", line 246, in __init__ raise ValueError('Missing required fields: %s' % ', '.join(missing)) ValueError: Missing required fields: version
gavinwahl commented 7 years ago

Hi Justin,

I'm not sure if this is a bug in django-u2f or in the firefox addon. It's happening because the firefox isn't submitting the version field as part of the registration request. I don't know if compliant implementations of u2f are required to submit the version or not.

justinmayer commented 7 years ago

Thank you for the rapid response, Gavin. I'm not an expert at reading specifications, but my cursory research indicates that version may indeed be a required field: https://fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-javascript-api-v1.1-id-20160915.html#u2f-operations

I took the liberty of filing an issue in the U2F Firefox add-on repository in hopes of soliciting its author's opinion on the topic: https://github.com/gavinwahl/django-u2f/issues/24

justinmayer commented 6 years ago

As an update to this issue, I just encountered the same error with Firefox Developer Edition 57.0b3, which includes preliminary support for U2F USB keys. I'm not sure what the spec dictates, but the python-u2flib-server library seems to expect and require the protocol version.

@dainnilsson: Given that this appears to be more closely related to u2flib than to django-u2f, do you have any thoughts on this problem?

@jcjones: Many thanks for your hard work on supporting U2F in Firefox. If indeed Firefox is not including the protocol version (U2F_V2) in the request, do you think it should be?

jcjones commented 6 years ago

@justinmayer Huh! https://www.fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-javascript-api-v1.1-id-20160915.html#dictionary-u2fregisterrequest-members does show that the RegisterResponse WebIDL should have version. So this is my bug.

dictionary RegisterResponse {
    DOMString version;
    DOMString registrationData;
    DOMString clientData;
};

I've filed bug 1403279 to change this.

jcjones commented 6 years ago

This bug will be fixed in the next beta of Firefox, 57 beta 5. Thanks for pinging me!

justinmayer commented 6 years ago

This has indeed been fixed in Firefox 57. Thank you, J.C., for all your work on supporting U2F in Firefox! 🎉