Skip to content

OAuth2 server: 'Request' object has no attribute 'payload' (instead of InsecureTransportError) when receiving requests over http and NOT localhost/127.0.0.1 #795

@caarmen

Description

@caarmen

Describe the bug

When Django receives requests without https, and not on http://localhost, and when we call get_consent_grant() it's expected to fail (unless AUTHLIB_INSECURE_TRANSPORT=true).

The problem is, instead of InsecureTransportError being propagated, an error 'Request' object has no attribute 'payload' is raised.

To Reproduce

Follow the documentation to setup OAuth2 server in Django.

The relevant part is this:

def authorize(request):
    try:
        grant = server.get_consent_grant(request, end_user=request.user)
    except OAuth2Error as error:
        return server.handle_error_response(request, error)  # 👀 expect to end up here, but no

Run the server on port 8008:

python manage.py runserver 8008

Call the authorize route on http://localhost:8008, it works ✔️

curl --location 'http://localhost:8008/v1/oauth2/authorize/8ce87789-b5cd-44dc-aff3-af95497b46f6?response_type=code&client_id=myclientid&code_challenge=wgw5CZck78w8i9KtMWLRt7hwPTAeXghvXo602q8y9FY&redirect_uri=myapp%3A%2F%2Foauth2&code_challenge_method=S256'

Now, add an entry in /etc/hosts to test:

127.0.0.1 mytestserver

Call the authorize route using http://mytestserver:8000. It fails. This is expected, but the error message isn't expected ❌

curl --location 'http://mytestserver:8008/v1/oauth2/authorize/8ce87789-b5cd-44dc-aff3-af95497b46f6?response_type=code&client_id=myclientid&code_challenge=wgw5CZck78w8i9KtMWLRt7hwPTAeXghvXo602q8y9FZ&redirect_uri=myapp%3A%2F%2Foauth2&code_challenge_method=S256'

The error insecure_transport: OAuth 2 MUST utilize https. is expected, but the error that is actually raised in the end is 'Request' object has no attribute 'payload'

Error Stacks

{"code":"UNDEFINED_ERROR","message":"Undefined Error.","debug":"'Request' object has no attribute 'payload': Traceback (most recent call last):
  File \"/home/carmen/.pyenv/versions/auth/lib/python3.10/site-packages/authlib/oauth2/rfc6749/authorization_server.py\", line 255, in get_consent_grant
    request = self.create_oauth2_request(request)
  File \"/home/carmen/.pyenv/versions/auth/lib/python3.10/site-packages/authlib/integrations/django_oauth2/authorization_server.py\", line 58, in create_oauth2_request
    return DjangoOAuth2Request(request)
  File \"/home/carmen/.pyenv/versions/auth/lib/python3.10/site-packages/authlib/integrations/django_oauth2/requests.py\", line 36, in __init__
    super().__init__(request.method, request.build_absolute_uri(), request.headers)
  File \"/home/carmen/.pyenv/versions/auth/lib/python3.10/site-packages/authlib/oauth2/rfc6749/requests.py\", line 69, in __init__
    InsecureTransportError.check(uri)
  File \"/home/carmen/.pyenv/versions/auth/lib/python3.10/site-packages/authlib/oauth2/rfc6749/errors.py\", line 65, in check
    raise cls()
authlib.oauth2.rfc6749.errors.InsecureTransportError: insecure_transport: OAuth 2 MUST utilize https.

Expected behavior

InsecureTransportError should be raised, not a python error about accessing an attribute which doesn't exist.

Environment:

  • OS: Ubuntu 24.04
  • Python Version: 3.10
  • Authlib Version: 1.6.1

Additional context

this is the path through the code:

The Authlib code that catches this security exception does this:

        except OAuth2Error as error:
            # REQUIRED if a "state" parameter was present in the client
            # authorization request.  The exact value received from the
            # client.
            error.state = request.payload.state
            raise

One problem, is that it attempts to access request.payload. However, payload hasn't been set yet. It would be set only after passing this initial security check, in the OAuth2Request initializer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions