lepture / authlib

The ultimate Python library in building OAuth, OpenID Connect clients and servers. JWS,JWE,JWK,JWA,JWT included.
https://authlib.org/
BSD 3-Clause "New" or "Revised" License
4.49k stars 448 forks source link

Empty request body for non-urlencoded content when using SIGNATURE_TYPE_QUERY OAuth signature type #543

Open balazser opened 1 year ago

balazser commented 1 year ago

Hello,

I have encountered an issue with the HTTPX Authlib Auth plugin when using the SIGNATURE_TYPE_QUERY OAuth signature type. The issue appears when the request body is non-urlencoded content, resulting in an empty request body for PUT requests.

In the auth_flow method, the body is used when returning self.prepare:

def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]:
    url, headers, body = self.prepare(
        request.method, str(request.url), request.headers, request.content)
    headers['Content-Length'] = str(len(body))
    yield build_request(url=url, headers=headers, body=body, initial_request=request)

However, in the prepare method, the body is set to empty at this line:

body = b''

Here is the relevant part of the prepare method:

def prepare(self, method, uri, headers, body):
    ...
    if CONTENT_TYPE_FORM_URLENCODED in content_type:
        headers['Content-Type'] = CONTENT_TYPE_FORM_URLENCODED
        uri, headers, body = self.sign(method, uri, headers, body)
    elif self.force_include_body:
        # To allow custom clients to work on non form encoded bodies.
        uri, headers, body = self.sign(method, uri, headers, body)
    else:
        # Omit body data in the signing of non form-encoded requests
        uri, headers, _ = self.sign(method, uri, headers, b'')
        body = b''
    return uri, headers, body

This leads to an empty request body for PUT requests with the SIGNATURE_TYPE_QUERY OAuth signature type and non-urlencoded content.

To Reproduce

A minimal example to reproduce the behavior:

import httpx
from authlib.integrations.httpx_client import OAuth1Auth

async def main():
    auth = OAuth1Auth(
        client_id="YOUR_CLIENT_ID",
        client_secret="YOUR_CLIENT_SECRET",
        access_token="YOUR_ACCESS_TOKEN",
        token_secret="YOUR_TOKEN_SECRET",
        signature_type="QUERY"
    )

    url = "https://api.example.com/resource"
    headers = {"Content-Type": "application/json"}
    data = {"key": "value"}

    async with httpx.AsyncClient() as client:
        response = await client.put(url, json=data, headers=headers, auth=auth)
        print(response.text)

await main()

Expected behavior

When making a PUT request with non-urlencoded content and using the SIGNATURE_TYPE_QUERY OAuth signature type, I expect the request body to be properly included.

Environment:

Additional context

hamiltonkibbe commented 1 month ago

Seeing the same behavior with POST requests & JSON content. removing the body = b'' line referenced above fixes the issue for me. Happy to PR if that would be helpful