Skip to content

SyncClient doesn't seem to retain authentication headers in request method #202

Description

@shtrom

The SyncClient (and the AsyncClient seems to do the same) creates its non GraphQL session by initialising the GraphQL client, and reusing its transport's session.

def _get_gql_session(self) -> SyncClientSession:
"""Return an AIOHTTP session.
The session will be automatically re-created anytime the auth's
token changes.
Returns:
aiohttp.ClientSession: An AIOHTTP session object.
"""
token = self.get_token()
if token == self._prev_token:
assert isinstance(self._gql_session, SyncClientSession)
return self._gql_session
# Drop the old session before building the new one, so a failure below
# leaves nothing cached and the next caller retries.
prev_client = self._gql_client
self._prev_token = None
self._gql_client = None
self._gql_session = None
if prev_client:
prev_client.close_sync()
headers = {
"Accept": "application/vnd.github+json",
}
if token:
headers["Authorization"] = f"Bearer {token}"
transport = RequestsHTTPTransport(url=GITHUB_GRAPHQL_ENDPOINT, headers=headers)
client = GqlClient(transport=transport, fetch_schema_from_transport=False)
session = client.connect_sync()
assert isinstance(session, SyncClientSession)
self._gql_client = client
self._gql_session = session
self._prev_token = token
return session
def _get_requests_session(self) -> Session:
session = self._get_gql_session()
assert isinstance(session.transport, RequestsHTTPTransport)
assert session.transport.session
return session.transport.session

However, the transport's session doesn't contain the Authorization (or Accept) headers, which are stored on the transport itself.

This means the TokenClient ends up using an unauthenticated session.

Repro:

import subprocess

from simple_github import TokenClient

token = subprocess.check_output(['gh',  'auth', 'token'], text=True).strip()

with TokenClient(token) as session:
  print(session._get_gql_session().transport.headers)
  print(session._get_gql_session().transport.session.headers)
  print(session._get_gql_session().transport)
  print(session._get_requests_session().headers)
  resp = session.get("/orgs/mozilla-firefox/teams/all-reviewers")
  print(resp)
$ uv run ./repro.py
{'Accept': 'application/vnd.github+json', 'Authorization': 'Bearer gho_XXX'}
{'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive'}
<gql.transport.requests.RequestsHTTPTransport object at 0x70f2426cc200>
{'User-Agent': 'python-requests/2.34.2', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Accept': '*/*', 'Connection': 'keep-alive'}
<Response [401]>

Note that the session._get_gql_session().transport.headers contains the token, but the session._get_requests_session().headers doesn't.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions