1 year ago

#298755

test-img

Diego-MX

How to setup a Bearer Token Authentication in AsyncOAuth2Client with authlib

I'm calling an API that has a bearer token authentication.
With regular requests package I have successfully implemented it, but then I had to upgrade the class to run concurrent requests.

I found authlib.integrations.httpx_client.AsyncOAuth2Client to have the OAuth2 piece, and then authlib.oauth2.rfc6750.BearerTokenValidator to have the Bearer token piece.
But then I'm not able to run it correctly.

In the meantime I did try aiohttp, but moved to httpx since it seemed better with the OAuth2 authentication.
Still, my first time meeting asyncio, httpx and friends, so all suggestions are welcome.

The successful part with requests comes first:

class BearerAuth(requests.auth.AuthBase): 
  def __init__(self, token): 
    self.token = token
 
  def __call__(self, a_request): 
    a_request.headers['Authorization'] = f'Bearer {self.token}'
    return a_request 


class MySession(Session): 
  def __init__(self): 
    super().__init__()
    self.set_token()

  def set_token():
    auth_enc = encode64('{username}:{password}'.format(**access_dict))
    the_headers = {'Authorization': f'Basic {auth_enc}'}
    auth_resp = self.post(AUTH_URL, headers=the_headers)
    self.token = the_resp.json()

  def call_api(self): 
    for _ in range(tries): 
      a_resp = self.get(API_URL, auth=BearerAuth(self.token['access_token']))
      if a_resp.status_code == 401: 
        self.set_token()
        continue
      elif a_resp.status_code == 200:
        return a_resp
    else: 
      return None

The unsuccessful part with AsyncOauth2Client is next:

class AsyncBearerAuth(BearerTokenValidator): 
  def __init__(self, token): 
    self.token = token

  def authenticate_token(self, token): 
    return token

  def __call__(self, a_request): 
    a_request.headeers['Authorization'] = f'Bearer {self.token}'
    return a_request


class MyAsynClient(AsyncOAuth2Client): 
  def __init__(self): 
    AsyncOAuth2Client.__init__(self, AUTH_KEY, AUTH_SECRET)
    # self.create_authorization_url(AUTH_URL)

  async def set_token(self): 
    auth_data = { 'grant_type' : 'password', 
                  'username'   : AUTH_USERNAME, 
                  'password'   : AUTH_PASSWORD } } }
    self.token = await self.fetch_token(AUTH_URL, **auth_data)

  async def call_api(self): 
    if not hasattr(self, 'token'): 
      await self.set_token()

    for _ in range(tries): 
      the_resp = await self.get(API_URL, 
            auth=AsyncBearerAuth(self.token['access_token']))
      
      if the_resp.status_code == 401: 
        await self.set_token()
        continue
      elif the_resp.status_code == 200: 
        return the_resp 

    else: 
      return None

def main(): 
   async with MyAsyncClient() as client: 
     the_tasks = []
     for _ in range(10): 
       a_task = asyncio.create_task( client.call_api() )
       the_tasks.append(a_task)
     results = await asyncio.gather(*tasks, return_exceptions=True)

   do_something(results)

The error lies in this piece:

     the_resp = await self.get(API_URL, 
            auth=AsyncBearerAuth(self.token['access_token']))

and it says:

~\anaconda3\lib\site-packages\httpx\_client.py in _send_single_request(self, request)
   1683         Sends a single request, without handling any redirections.
   1684         """
-> 1685         transport = self._transport_for_url(request.url)
   1686         timer = Timer()
   1687         await timer.async_start()

And if I remove the 'call' from AsyncBearerAuth the error that I get is:

~\anaconda3\lib\site-packages\httpx\_auth.py in auth_flow(self, request)
    113
    114     def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]:
--> 115         yield self._func(request)
    116
    117

TypeError: __call__() missing 2 required positional arguments: 'scope' and 'request'

Other questions that I didn't fully understand are:

  • Am I right in inheriting from BearerTokenValidator?
    I'm following the documentation on Bearer Token Usage but I'm not sure about the Validator object here.

  • I have also tried commenting and uncommenting the create_authorization_url with no success.

Thank you for your help.

python

oauth-2.0

authlib

httpx

0 Answers

Your Answer

Accepted video resources