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