klen / aioauth-client Goto Github PK
View Code? Open in Web Editor NEWOAuth client for aiohttp
License: MIT License
OAuth client for aiohttp
License: MIT License
Thanks for this useful module! Is there a plan to support Twitter's Streaming API?
Hello thanks for this - do you have a sample (or is it not possible) to have the user login via this module without resorting to the pin based authorization? The Github sample is perfect - as a user I can login directly in the browser, which is what I need for my app. I've tried reworking the Twitter sample but can't quite get it working. Anybody have a sample for this? Thanks!
Hello.
How about this popular socila network?
Hello! Thank you for the library, it became very handy and I enjoyed using it.
What do you think about little refactoring of the code I've seen while using your library:
I didn't enjoy to re-instantiate the Client
s everytime I've got an access token. That's why I fixed this. Would you like me to PR?
OPTIONAL: the API you're using for requests here is outdated, so I believe I could refactor it from
response = yield from self.request('POST', self.access_token_url, data=payload, loop=loop)
to async with aiohttp.ClientSession() as session: async with session.post(self.access_token_url, data=payload) as resp:
syntax and use async/await
syntax instead of old one, but it'll break compatibility with older Python versions. On the other hand, aiohttp
's API is changing frequently so maybe we should move on to be up-to-date with them?
When trying to use FB auth in example app Facebook shows 'App Not Setup' error
(similar to the shown here: https://i.stack.imgur.com/p2Zsn.png)
When lichess user profile is empty I get:
2019-08-20T12:43:56.631908+00:00 app[web.1]: Traceback (most recent call last):
2019-08-20T12:43:56.631909+00:00 app[web.1]: File "/app/routes.py", line 84, in login
2019-08-20T12:43:56.631911+00:00 app[web.1]: user, info = await client.user_info()
2019-08-20T12:43:56.631912+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/aioauth_client.py", line 179, in user_info
2019-08-20T12:43:56.631913+00:00 app[web.1]: user = User(**dict(self.user_parse(data)))
2019-08-20T12:43:56.631914+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.6/site-packages/aioauth_client.py", line 476, in user_parse
2019-08-20T12:43:56.631915+00:00 app[web.1]: yield 'first_name', data.get('profile').get("firstName")
2019-08-20T12:43:56.631918+00:00 app[web.1]: AttributeError: 'NoneType' object has no attribute 'get'
aioauth-client/aioauth_client.py
Lines 154 to 156 in 54f5824
I'm trying to use OAuth2Client.request
get some resource from own server, but client.request
always return empty dict. I found out the problem happens in Client._request
.
maybe you can add a parameter like raw_content
, let us control how to deal with the response?
When the server returns an error when getting the access token aioauth just raises the unhelpful web.HTTPBadRequest(reason='Failed to obtain OAuth access token.')
.
While implementing flows with Facebook and Intercom (I used the OAuth2 client as a base for that) I encountered problems that couldn't be debugged, because I couldn't see the server's response.
I'll submit a PR with a fix in a few moments.
Hi,
I am trying to run the example code from this link as it is
https://github.com/klen/aioauth-client/blob/develop/example/app.py
After authorizing the app from Github I get the below error.
aiohttp.client_exceptions.ClientConnectorCertificateError: Cannot connect to host github.com:443 ssl:True [SSLCertVerificationError: (1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)')]
Complete Traceback
/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/bin/python /Users/sureshkl/PycharmProjects/Learn_aiohttp/example/app.py
/Users/sureshkl/PycharmProjects/Learn_aiohttp/example/app.py:160: DeprecationWarning: Application.make_handler(...) is deprecated, use AppRunner API instead
f = loop.create_server(app.make_handler(), '127.0.0.1', 5000)
serving on ('127.0.0.1', 5000)
SSL handshake failed on verifying the certificate
protocol: <asyncio.sslproto.SSLProtocol object at 0x1037fd208>
transport: <_SelectorSocketTransport fd=11 read=polling write=<idle, bufsize=0>>
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/sslproto.py", line 625, in _on_handshake_complete
raise handshake_exc
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/sslproto.py", line 189, in feed_ssldata
self._sslobj.do_handshake()
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py", line 763, in do_handshake
self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)
SSL error in data received
protocol: <asyncio.sslproto.SSLProtocol object at 0x1037fd208>
transport: <_SelectorSocketTransport closing fd=11 read=idle write=<idle, bufsize=0>>
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/sslproto.py", line 526, in data_received
ssldata, appdata = self._sslpipe.feed_ssldata(data)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/sslproto.py", line 189, in feed_ssldata
self._sslobj.do_handshake()
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py", line 763, in do_handshake
self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)
Error handling request
Traceback (most recent call last):
File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/connector.py", line 936, in _wrap_create_connection
return await self._loop.create_connection(*args, **kwargs) # type: ignore # noqa
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/base_events.py", line 986, in create_connection
ssl_handshake_timeout=ssl_handshake_timeout)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/base_events.py", line 1014, in _create_connection_transport
await waiter
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/sslproto.py", line 526, in data_received
ssldata, appdata = self._sslpipe.feed_ssldata(data)
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/sslproto.py", line 189, in feed_ssldata
self._sslobj.do_handshake()
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py", line 763, in do_handshake
self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/web_protocol.py", line 418, in start
resp = await task
File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/web_app.py", line 458, in _handle
resp = await handler(request)
File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/example/app.py", line 133, in oauth
_, meta = await client.get_access_token(request.query)
File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aioauth_client.py", line 340, in get_access_token
data = await self.request('POST', self.access_token_url, data=payload, loop=loop)
File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aioauth_client.py", line 152, in _request
async with session.request(method, url, **kwargs) as response:
File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/client.py", line 1012, in __aenter__
self._resp = await self._coro
File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/client.py", line 483, in _request
timeout=real_timeout
File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/connector.py", line 523, in connect
proto = await self._create_connection(req, traces, timeout)
File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/connector.py", line 859, in _create_connection
req, traces, timeout)
File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/connector.py", line 1004, in _create_direct_connection
raise last_exc
File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/connector.py", line 986, in _create_direct_connection
req=req, client_error=client_error)
File "/Users/sureshkl/PycharmProjects/Learn_aiohttp/venv/lib/python3.7/site-packages/aiohttp/connector.py", line 939, in _wrap_create_connection
req.connection_key, exc) from exc
aiohttp.client_exceptions.ClientConnectorCertificateError: Cannot connect to host github.com:443 ssl:True [SSLCertVerificationError: (1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056)')]
Environment: macOS 10.15.4
Python 3.7.3
For now, all requests in OAuth Clients in this package are performed using aiohttp.request
function. But there are cases, when I need to use my own ClientSession instance instead of leaving it up to oauth lib - that is useful for both testing and ease of configuration.
So, I imagine it like this:
from aioauth_client import OAuth2Client
from aiohttp import ClientSession
custom_session = ClientSession(custom_setting='custom_value')
client = OAuth2Client(session=custom_session)
One possible case, why users would need it is a client session instance bound to a fake server emulating an API (like here https://github.com/aio-libs/aiohttp/blob/master/examples/fake_server.py#L144), which makes testing some service relying on external API isolated from the actual API easy and honest.
I'm currently working on Gitlab as OAuth2 provider :
https://github.com/factorysh/demo-aiohttp-oauth2-gitlab
Would you like a Pull Request for Gitlab integration?
Hi,
With the GoogleClient
, we can specify access_type='offline'
, but how does that relate to using the refresh token automatically, to avoid an explicit redirection after the 3600 seconds of TTL of the token ?
https://developers.google.com/identity/protocols/OAuth2WebServer#offline
Thanks !
Given this code:
t = TwitterClient(consumer_key=TWITTER_CONSUMER_KEY,
consumer_secret=TWITTER_CONSUMER_SECRET,
oauth_token='976766514308067328-aJlndNEpXueYdnds7dEmJrGw3WhLNa1',
oauth_token_secret='RpofCLZd72IuzM1FX98Tl7unJx5yZIoUOZJIEqnBF0Hki')
f = await t.request('GET', 'followers/id.json?user_id=6253282&cursor=-1')
I receive this error:
File "/usr/local/lib/python3.6/site-packages/aioauth_client.py", line 233, in request
'Request parameters should be in the "params" parameter, '
ValueError: Request parameters should be in the "params" parameter, not inlined in the URL
If I were to remove that raise ValueError
(in aioauth_client.py), or if I passed params={ 'user_id' : 6253282 }
to request('GET', ...)
, Twitter gets back with error code 34 (Sorry, that page does not exist)
Hi,
The aioauth-client is using aiohttp without the max version being pinned. This is potentially pretty unstable, since aioauth-client is also using deprecated apis.
The requirements.txt (used in setup.py)
aiohttp >= 3.0.0
Per
https://docs.aiohttp.org/en/stable/changes.html#deprecations-and-removals several of the timeout arguments used in aioauth-client are marked as deprecated and do raise DeprecationWarning notices. These are here https://github.com/klen/aioauth-client/blob/develop/aioauth_client.py#L147
async def _request(self, method, url, loop=None, timeout=None, **kwargs):
"""Make a request through AIOHTTP."""
session = self.session or aiohttp.ClientSession(
loop=loop, conn_timeout=timeout, read_timeout=timeout, raise_for_status=True)
try:
async with session.request(method, url, **kwargs) as response:
if 'json' in response.headers.get('CONTENT-TYPE'):
data = await response.json()
else:
data = await response.text()
data = dict(parse_qsl(data)) or data
return data
except asyncio.TimeoutError:
raise web.HTTPBadRequest(reason='HTTP Timeout')
finally:
if not self.session and not session.closed:
await session.close()
These are slated to be removed in aiohttp 4.0.0 and once that package is released, aioauth-client users will be in a broken state due to the lack of maximum version pinning.
Can the maximum version be capped and/or the deprecated features be removed and replaced with a single timeout?
Add a access_token
parameter to the request()
method that overwrites the default one.
def request(self, method, url, headers=None, access_token=None, **aio_kwargs):
"""Request OAuth2 resource."""
url = self._get_url(url)
headers = headers or {'Accept': 'application/json'}
if self.access_token:
headers['Authorization'] = "Bearer {}".format(access_token or self.access_token)
return self._request(method, url, headers=headers, **aio_kwargs)
(From DiscordClient)
Looking for suggestions on how to adjust percent encoding of oauth parameters.
I'm in the process of creating a new client and requests work as long as '/' is not present in some oauth parameters. Has anyone else experienced a situation like this?
I tried overwriting Client._request to manually encoding parameters which are passed into aiohttp.ClientSession next line. It will work as long as '/' character is not present.
kwargs : {'params': {'oauth_consumer_key': 'aaa', 'oauth_nonce': '4a0ff66a0ac3674e6fd2b3b236890fb05dbe8304', 'oauth_signature_method': 'HMAC-SHA1', 'oauth_timestamp': '1551030612', 'oauth_version': '1.0', 'oauth_callback': 'oob', 'oauth_token': 'qbG4My9Lpz5mc5M1afhflTrG0NwOXN%2FH8bDFig4OoEU%3D',
Result: Get request fails with status 401. session.request(method, url, **kwargs) partly decoded the oauth token ? I thought aiohttp.ClientSession does not attempt percent encoding?
OAuth: oauth1 - DEBUG - response url: https://127.0.0.1?oauth_consumer_key=aaa&oauth_nonce=4a0ff66a0ac3674e6fd2b3b236890fb05dbe8304&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1551030612&oauth_version=1.0&oauth_callback=oob&oauth_token=qbG4My9Lpz5mc5M1afhflTrG0NwOXN/H8bDFig4OoEU%3D&oauth_signature=PA2VSz5drOUBWLbfLXiuPE9bB20%3D
https://tools.ietf.org/html/rfc5849#section-3.6
https://tools.ietf.org/html/rfc3986
Is there any reasons do not use aiohttp>=1.0.5
? It's not convenient to install it with --no-deps
switch :)
Hi there — great library. I tried to write a DiscordClient using your examples but could not get it to work. Namely, this is where I have gotten to but it doesn’t parse the userinfo.
Trying to run the example code provided will get the token however throws an error where it gets a 401
while tryimng to access user_info
class DiscordClient(OAuth2Client):
"""Support Discord.
* Dashboard: https://discordapp.com/developers/applications/me
* Docs: https://discordapp.com/developers/docs/intro
* API reference: https://discordapp.com/developers/docs/reference
"""
access_token_url = 'https://discordapp.com/api/oauth2/token'
authorize_url = 'https://discordapp.com/api/oauth2/authorize'
base_url = 'https://discordapp.com/api/'
name = 'discord'
user_info_url = 'https://discordapp.com/api/users/@me'
def __init__(self, *args, **kwargs):
"""Set default scope."""
super(DiscordClient, self).__init__(*args, **kwargs)
self.params.setdefault('scope', 'identify')
@classmethod
def user_parse(cls, data) -> UserInfo:
"""Parse information from the provider."""
user = data.get('response', {}).get('user', {})
return UserInfo(
id=user.get('id'),
username=user.get('username'),
discriminator=user.get('discriminator'),
avatar=user.get('avatar'),
verified=user.get('verified'),
email=user.get('email')
)
Any help would be appreciated!
Hi,
I tried to use this library to connect some custom OAuth2 service, but I noticed it only support Authorization Code grant type.
My service is using Client Credentials grant type, can you support it too?
Thanks!
client_id
, client_secret
codes and Bearer
tokens are exposed in debug logs.
These values should be sanitized before logging.
Excerpt from logs (XXX values are obviously my edits):
2022-04-04 21:29:53 Request POST: https://oauth2.googleapis.com/token {'headers': {'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'}, 'params': None, 'data': {'grant_type': 'authorization_code', 'client_id': 'XXXFOOXXX.apps.googleusercontent.com', 'client_secret': 'XXXSECRETXXX', 'code': ['XXXCODEXXX'], 'redirect_uri': 'https://hostname/path'}}
2022-04-04 21:29:53 Request GET: https://www.googleapis.com/userinfo/v2/me {'headers': {'Accept': 'application/json', 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', 'Authorization': 'Bearer XXXTOKENXXX'}, 'params': None}
When using the google people batch api.
You have to construct an url like this.
https://people.googleapis.com/v1/people:batchGet?resourceNames=people/12345&resourceNames=people/67890&personFields=photos
This fails when aioauth-client tries to modify the params to add the access token:
File "~/.venv/lib/python3.6/site-packages/aioauth_client.py", line 315, in request
params[self.access_token_key] = access_token
TypeError: list indices must be integers or slices, not str
vk.com uses get_access_token response to pass the email. Shouldn't OAuth2Client.get_access_token()
return data
instead of self.access_token
?
First off, thank you for this library. But, I have a question :)
When executing the code in:
https://github.com/klen/aioauth-client/blob/develop/README.rst#usage
There seems to be an issue:
user_info = yield from response.json()
The response
object is a dict, not the response
object: https://github.com/klen/aioauth-client/blob/develop/aioauth_client.py#L147
A simple fix is by doing this, of course:
response = await github.request('GET', 'user')
print(response)
Similar issue seems to be in the example:
https://github.com/klen/aioauth-client/blob/develop/example/app.py#L98
There it reads:
response = yield from github.request('GET', 'user')
body = yield from response.read()
Which too fails, because response
is a dict.
Of course these are all easy to fix, but I was mostly wondering if it was intended, as I too expected the response object as return value. Mainly, as that allows for checking the status
field in the request, to see if the request went through okay. It would also give access to GitHub headers indicating the rate limiter current status, etc. Of course with the access_token it is easily enough to make that request directly via aiohttp myself, but it just made me wonder why this decision?
Thank you again for the library, saved me a bit of time :)
Attempting to import aioauth-client
raises:
ModuleNotFoundError: No module named 'yarl'
Looks like it needs to be added here:
Line 32 in 18a8627
File "/home/tamas/.local/lib/python3.6/site-packages/aioauth_client.py", line 173, in user_info
data = await self.request('GET', self.user_info_url, loop=loop, **kwargs)
TypeError: object tuple can't be used in 'await' expression
aioauth-client/aioauth_client.py
Lines 148 to 150 in 54f5824
it's confusing, the reason
say "HTTP status code: 403" but the attribute status_code
and status
are both 400.
response.raise_for_status
should be better.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.