Skip to content

ValueError from oauthlib when validating query strings is raised as 500 instead of 400 HTTP error (in DRF extension)#1443

@AetherUnbound

Description

@AetherUnbound

Describe the bug

This issue is essentially #954, except we've encountered it when using Django Rest Framework (and the oauth2_provider extension for it).

Here's the stack trace from the exception we're seeing:

web-1 | 2024-07-26T21:01:08.714706Z [error ] request_failed [django_structlog.middlewares.request] code=500 filename=request.py func_name=process_exception ip=172.18.0.1 lineno=197 request=GET /v1/images/?q=73%%20of%20Arkansans%20think%20that%20crime%20is%20on%20the%20rise%20in%20their%20state request_id=c58a2c8f-8899-4641-989d-dc8b703e47be user_id=2 web-1 | Traceback (most recent call last): web-1 | File "/.venv/lib/python3.11/site-packages/asgiref/sync.py", line 518, in thread_handler web-1 | raise exc_info[1] web-1 | File "/.venv/lib/python3.11/site-packages/django/core/handlers/base.py", line 253, in _get_response_async web-1 | response = await wrapped_callback( web-1 | ^^^^^^^^^^^^^^^^^^^^^^^ web-1 | File "/.venv/lib/python3.11/site-packages/adrf/viewsets.py", line 120, in async_view web-1 | return await self.dispatch(request, *args, **kwargs) web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ web-1 | File "/.venv/lib/python3.11/site-packages/adrf/views.py", line 73, in async_dispatch web-1 | response = self.handle_exception(exc) web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ web-1 | File "/.venv/lib/python3.11/site-packages/rest_framework/views.py", line 469, in handle_exception web-1 | self.raise_uncaught_exception(exc) web-1 | File "/.venv/lib/python3.11/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception web-1 | raise exc web-1 | File "/.venv/lib/python3.11/site-packages/adrf/views.py", line 57, in async_dispatch web-1 | await sync_to_async(self.initial)(request, *args, **kwargs) web-1 | File "/.venv/lib/python3.11/site-packages/asgiref/sync.py", line 468, in __call__ web-1 | ret = await asyncio.shield(exec_coro) web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ web-1 | File "/.venv/lib/python3.11/site-packages/asgiref/current_thread_executor.py", line 40, in run web-1 | result = self.fn(*self.args, **self.kwargs) web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ web-1 | File "/.venv/lib/python3.11/site-packages/asgiref/sync.py", line 522, in thread_handler web-1 | return func(*args, **kwargs) web-1 | ^^^^^^^^^^^^^^^^^^^^^ web-1 | File "/.venv/lib/python3.11/site-packages/rest_framework/views.py", line 414, in initial web-1 | self.perform_authentication(request) web-1 | File "/.venv/lib/python3.11/site-packages/rest_framework/views.py", line 324, in perform_authentication web-1 | request.user web-1 | File "/.venv/lib/python3.11/site-packages/adrf/requests.py", line 19, in user web-1 | self._authenticate() web-1 | File "/.venv/lib/python3.11/site-packages/adrf/requests.py", line 49, in _authenticate web-1 | user_auth_tuple = authenticator.authenticate(self) web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ web-1 | File "/api/conf/oauth2_extensions.py", line 21, in authenticate web-1 | user_auth_tuple = super().authenticate(request) web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ web-1 | File "/.venv/lib/python3.11/site-packages/oauth2_provider/contrib/rest_framework/authentication.py", line 27, in authenticate web-1 | valid, r = oauthlib_core.verify_request(request, scopes=[]) web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ web-1 | File "/.venv/lib/python3.11/site-packages/oauth2_provider/oauth2_backends.py", line 202, in verify_request web-1 | valid, r = self.server.verify_request(uri, http_method, body, headers, scopes=scopes) web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ web-1 | File "/.venv/lib/python3.11/site-packages/oauthlib/oauth2/rfc6749/endpoints/base.py", line 112, in wrapper web-1 | return f(endpoint, uri, *args, **kwargs) web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ web-1 | File "/.venv/lib/python3.11/site-packages/oauthlib/oauth2/rfc6749/endpoints/resource.py", line 65, in verify_request web-1 | request = Request(uri, http_method, body, headers) web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ web-1 | File "/.venv/lib/python3.11/site-packages/oauthlib/common.py", line 393, in __init__ web-1 | self._params.update(dict(urldecode(self.uri_query))) web-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^ web-1 | File "/.venv/lib/python3.11/site-packages/oauthlib/common.py", line 122, in urldecode web-1 | raise ValueError('Invalid hex encoding in query string.') web-1 | ValueError: Invalid hex encoding in query string. 

To Reproduce

I'm not able to give a minimum set of steps to reproduce, but it's essentially caused by the same circumstances as #954. You can see similar instructions in WordPress/openverse#3199.

Expected behavior

Similar to the solution added in #963, this should probably also raise a SuspiciousOperation exception instead.

Version

  • I have tested with the latest published release and it's still a problem.
  • I have tested with the master branch and it's still a problem.

Additional context

I think we'd just need to apply the same logic from backends.py (edited in #963):

https://github.com/jazzband/django-oauth-toolkit/blob/102c85141ec44549e17080c676292e79e5eb46cc/oauth2_provider/backends.py#L18-L27

To the authentication.py module of the rest_framework extension:

https://github.com/jazzband/django-oauth-toolkit/blob/102c85141ec44549e17080c676292e79e5eb46cc/oauth2_provider/contrib/rest_framework/authentication.py#L21-L31

I will try to get a PR up for this!

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions