2 years ago
#298755

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_urlwith no success.
Thank you for your help.
python
oauth-2.0
authlib
httpx
0 Answers
Your Answer