Skip to content
Snippets Groups Projects
Commit 4b74b2c3 authored by Stefano Rivera's avatar Stefano Rivera
Browse files

New upstream version 1.4.1

parent e13c5664
No related branches found
No related tags found
No related merge requests found
Pipeline #806759 failed
Showing
with 175 additions and 160 deletions
......@@ -6,15 +6,14 @@
<a href="https://github.com/sponsors/lepture"><img src="https://badgen.net/badge/support/authlib/ff69b4?icon=patreon" /></a>
<a href="https://github.com/lepture/authlib/actions"><img src="https://github.com/lepture/authlib/workflows/tests/badge.svg" alt="Build Status"></a>
<a href="https://codecov.io/gh/lepture/authlib?branch=master"><img src="https://badgen.net/codecov/c/github/lepture/authlib" alt="Coverage Status"></a>
<a href="https://codecov.io/gh/lepture/authlib" > <img src="https://codecov.io/gh/lepture/authlib/graph/badge.svg?token=BQKzPoql9j"/></a>
<a href="https://pypi.org/project/Authlib/"><img src="https://badgen.net/pypi/v/authlib" alt="PyPI Version"></a>
<a href="https://codeclimate.com/github/lepture/authlib/maintainability"><img src="https://badgen.net/codeclimate/maintainability/lepture/authlib?icon=codeclimate" alt="Maintainability" /></a>
<a href="https://twitter.com/intent/follow?screen_name=authlib"><img src="https://img.shields.io/twitter/follow/authlib.svg?maxAge=3600&style=social&logo=twitter&label=Follow" alt="Follow Twitter"></a>
The ultimate Python library in building OAuth and OpenID Connect servers.
JWS, JWK, JWA, JWT are included.
Authlib is compatible with Python3.6+.
Authlib is compatible with Python3.9+.
**[Migrating from `authlib.jose` to `joserfc`](https://jose.authlib.org/en/dev/migrations/authlib/)**
......@@ -22,12 +21,11 @@ Authlib is compatible with Python3.6+.
<table>
<tr>
<td><img align="middle" width="48" src="https://avatars.githubusercontent.com/u/105941848?s=200&v=4"></td>
<td>Kraken is the world's leading customer & culture platform for energy, water & broadband. Licensing enquiries at <a href="https://kraken.tech/">Kraken.tech</a>.
</td>
<td><img align="middle" width="48" src="https://avatars.githubusercontent.com/u/70178963?v=4"></td>
<td>If you want to quickly add secure token-based authentication to Python projects, feel free to check Auth0's Python SDK and free plan at <a href="https://auth0.com/overview?utm_source=GHsponsor&utm_medium=GHsponsor&utm_campaign=authlib&utm_content=auth">auth0.com/overview</a>.</td>
</tr>
<tr>
<td><img align="middle" width="48" src="https://typlog.com/assets/icon-black.svg"></td>
<td><img align="middle" width="48" src="https://typlog.com/assets/icon-white.svg"></td>
<td>A blogging and podcast hosting platform with minimal design but powerful features. Host your blog and Podcast with <a href="https://typlog.com/">Typlog.com</a>.
</td>
</tr>
......
name = 'Authlib'
version = '1.4.0'
version = '1.4.1'
author = 'Hsiaoming Yang <me@lepture.com>'
homepage = 'https://authlib.org/'
default_user_agent = f'{name}/{version} (+{homepage})'
......
......@@ -17,7 +17,7 @@ from ..base_client import OAuthError
__all__ = [
'OAuthError',
'OAuth1Auth', 'AsyncOAuth1Client',
'OAuth1Auth', 'AsyncOAuth1Client', 'OAuth1Client',
'SIGNATURE_HMAC_SHA1', 'SIGNATURE_RSA_SHA1', 'SIGNATURE_PLAINTEXT',
'SIGNATURE_TYPE_HEADER', 'SIGNATURE_TYPE_QUERY', 'SIGNATURE_TYPE_BODY',
'OAuth2Auth', 'OAuth2ClientAuth', 'OAuth2Client', 'AsyncOAuth2Client',
......
......@@ -22,11 +22,6 @@ class AsyncAssertionClient(_AssertionClient, httpx.AsyncClient):
claims=None, token_placement='header', scope=None, **kwargs):
client_kwargs = extract_client_kwargs(kwargs)
# app keyword was dropped!
app_value = client_kwargs.pop('app', None)
if app_value is not None:
client_kwargs['transport'] = httpx.ASGITransport(app=app_value)
httpx.AsyncClient.__init__(self, **client_kwargs)
_AssertionClient.__init__(
......
......@@ -34,11 +34,6 @@ class AsyncOAuth1Client(_OAuth1Client, httpx.AsyncClient):
force_include_body=False, **kwargs):
_client_kwargs = extract_client_kwargs(kwargs)
# app keyword was dropped!
app_value = _client_kwargs.pop('app', None)
if app_value is not None:
_client_kwargs['transport'] = httpx.ASGITransport(app=app_value)
httpx.AsyncClient.__init__(self, **_client_kwargs)
_OAuth1Client.__init__(
......
......@@ -62,11 +62,6 @@ class AsyncOAuth2Client(_OAuth2Client, httpx.AsyncClient):
# extract httpx.Client kwargs
client_kwargs = self._extract_session_request_params(kwargs)
# app keyword was dropped!
app_value = client_kwargs.pop('app', None)
if app_value is not None:
client_kwargs['transport'] = httpx.ASGITransport(app=app_value)
httpx.AsyncClient.__init__(self, **client_kwargs)
# We use a Lock to synchronize coroutines to prevent
......
......@@ -2,8 +2,8 @@ from httpx import Request
HTTPX_CLIENT_KWARGS = [
'headers', 'cookies', 'verify', 'cert', 'http1', 'http2',
'proxies', 'timeout', 'follow_redirects', 'limits', 'max_redirects',
'event_hooks', 'base_url', 'transport', 'app', 'trust_env',
'proxy', 'mounts', 'timeout', 'follow_redirects', 'limits', 'max_redirects',
'event_hooks', 'base_url', 'transport', 'trust_env', 'default_encoding',
]
......
......@@ -170,3 +170,7 @@ class OAuth1Client:
@staticmethod
def handle_error(error_type, error_description):
raise ValueError(f'{error_type}: {error_description}')
def __del__(self):
if self.session:
del self.session
......@@ -103,3 +103,7 @@ class TokenAuth:
uri, headers, body = hook(uri, headers, body)
return uri, headers, body
def __del__(self):
del self.client
del self.hooks
......@@ -438,6 +438,9 @@ class OAuth2Client:
url, data=dict(url_decode(body)),
headers=headers, auth=auth, **kwargs)
def __del__(self):
del self.session
def _guess_grant_type(kwargs):
if 'code' in kwargs:
......
......@@ -90,3 +90,7 @@ class AssertionClient:
'POST', self.token_endpoint, data=data, withhold_token=True)
return self.parse_response_token(resp)
def __del__(self):
if self.session:
del self.session
......@@ -6,6 +6,14 @@ Changelog
Here you can see the full list of changes between each Authlib release.
Version 1.4.1
-------------
**Released on Jan 28, 2025**
- Improve garbage collection on OAuth clients. :issue:`698`
- Fix client parameters for httpx. :issue:`694`
Version 1.4.0
-------------
......
import time
import pytest
from httpx import WSGITransport
from authlib.integrations.httpx_client import AssertionClient
from ..wsgi_helper import MockDispatch
......@@ -26,7 +27,7 @@ def test_refresh_token():
audience='foo',
alg='HS256',
key='secret',
app=MockDispatch(default_token, assert_func=verifier)
transport=WSGITransport(MockDispatch(default_token, assert_func=verifier)),
) as client:
client.get('https://i.b')
......@@ -43,7 +44,7 @@ def test_refresh_token():
key='secret',
scope='email',
claims={'test_mode': 'true'},
app=MockDispatch(default_token, assert_func=verifier)
transport=WSGITransport(MockDispatch(default_token, assert_func=verifier)),
) as client:
client.get('https://i.b')
client.get('https://i.b')
......@@ -56,7 +57,7 @@ def test_without_alg():
subject='foo',
audience='foo',
key='secret',
app=MockDispatch(default_token)
transport=WSGITransport(MockDispatch(default_token)),
) as client:
with pytest.raises(ValueError):
client.get('https://i.b')
import time
import pytest
from httpx import ASGITransport
from authlib.integrations.httpx_client import AsyncAssertionClient
from ..asgi_helper import AsyncMockDispatch
......@@ -28,7 +29,7 @@ async def test_refresh_token():
audience='foo',
alg='HS256',
key='secret',
app=AsyncMockDispatch(default_token, assert_func=verifier)
transport=ASGITransport(AsyncMockDispatch(default_token, assert_func=verifier)),
) as client:
await client.get('https://i.b')
......@@ -45,7 +46,7 @@ async def test_refresh_token():
key='secret',
scope='email',
claims={'test_mode': 'true'},
app=AsyncMockDispatch(default_token, assert_func=verifier)
transport=ASGITransport(AsyncMockDispatch(default_token, assert_func=verifier)),
) as client:
await client.get('https://i.b')
await client.get('https://i.b')
......@@ -59,7 +60,7 @@ async def test_without_alg():
subject='foo',
audience='foo',
key='secret',
app=AsyncMockDispatch()
transport=ASGITransport(AsyncMockDispatch()),
) as client:
with pytest.raises(ValueError):
await client.get('https://i.b')
import pytest
from httpx import ASGITransport
from authlib.integrations.httpx_client import (
OAuthError,
AsyncOAuth1Client,
......@@ -19,8 +20,8 @@ async def test_fetch_request_token_via_header():
assert 'oauth_consumer_key="id"' in auth_header
assert 'oauth_signature=' in auth_header
app = AsyncMockDispatch(request_token, assert_func=assert_func)
async with AsyncOAuth1Client('id', 'secret', app=app) as client:
transport = ASGITransport(AsyncMockDispatch(request_token, assert_func=assert_func))
async with AsyncOAuth1Client('id', 'secret', transport=transport) as client:
response = await client.fetch_request_token(oauth_url)
assert response == request_token
......@@ -38,11 +39,11 @@ async def test_fetch_request_token_via_body():
assert b'oauth_consumer_key=id' in content
assert b'&oauth_signature=' in content
mock_response = AsyncMockDispatch(request_token, assert_func=assert_func)
transport = ASGITransport(AsyncMockDispatch(request_token, assert_func=assert_func))
async with AsyncOAuth1Client(
'id', 'secret', signature_type=SIGNATURE_TYPE_BODY,
app=mock_response,
transport=transport,
) as client:
response = await client.fetch_request_token(oauth_url)
......@@ -61,11 +62,11 @@ async def test_fetch_request_token_via_query():
assert 'oauth_consumer_key=id' in url
assert '&oauth_signature=' in url
mock_response = AsyncMockDispatch(request_token, assert_func=assert_func)
transport = ASGITransport(AsyncMockDispatch(request_token, assert_func=assert_func))
async with AsyncOAuth1Client(
'id', 'secret', signature_type=SIGNATURE_TYPE_QUERY,
app=mock_response,
transport=transport,
) as client:
response = await client.fetch_request_token(oauth_url)
......@@ -83,10 +84,10 @@ async def test_fetch_access_token():
assert 'oauth_consumer_key="id"' in auth_header
assert 'oauth_signature=' in auth_header
mock_response = AsyncMockDispatch(request_token, assert_func=assert_func)
transport = ASGITransport(AsyncMockDispatch(request_token, assert_func=assert_func))
async with AsyncOAuth1Client(
'id', 'secret', token='foo', token_secret='bar',
app=mock_response,
transport=transport,
) as client:
with pytest.raises(OAuthError):
await client.fetch_access_token(oauth_url)
......@@ -98,10 +99,10 @@ async def test_fetch_access_token():
@pytest.mark.asyncio
async def test_get_via_header():
mock_response = AsyncMockDispatch(b'hello')
transport = ASGITransport(AsyncMockDispatch(b'hello'))
async with AsyncOAuth1Client(
'id', 'secret', token='foo', token_secret='bar',
app=mock_response,
transport=transport,
) as client:
response = await client.get('https://example.com/')
......@@ -121,11 +122,11 @@ async def test_get_via_body():
assert b'oauth_consumer_key=id' in content
assert b'oauth_signature=' in content
mock_response = AsyncMockDispatch(b'hello', assert_func=assert_func)
transport = ASGITransport(AsyncMockDispatch(b'hello', assert_func=assert_func))
async with AsyncOAuth1Client(
'id', 'secret', token='foo', token_secret='bar',
signature_type=SIGNATURE_TYPE_BODY,
app=mock_response,
transport=transport,
) as client:
response = await client.post('https://example.com/')
......@@ -138,11 +139,11 @@ async def test_get_via_body():
@pytest.mark.asyncio
async def test_get_via_query():
mock_response = AsyncMockDispatch(b'hello')
transport = ASGITransport(AsyncMockDispatch(b'hello'))
async with AsyncOAuth1Client(
'id', 'secret', token='foo', token_secret='bar',
signature_type=SIGNATURE_TYPE_QUERY,
app=mock_response,
transport=transport,
) as client:
response = await client.get('https://example.com/')
......
......@@ -52,12 +52,12 @@ async def assert_token_in_uri(request):
]
)
async def test_add_token_get_request(assert_func, token_placement):
mock_response = AsyncMockDispatch({'a': 'a'}, assert_func=assert_func)
transport = ASGITransport(AsyncMockDispatch({'a': 'a'}, assert_func=assert_func))
async with AsyncOAuth2Client(
'foo',
token=default_token,
token_placement=token_placement,
app=mock_response
transport=transport
) as client:
resp = await client.get('https://i.b')
......@@ -75,12 +75,12 @@ async def test_add_token_get_request(assert_func, token_placement):
]
)
async def test_add_token_to_streaming_request(assert_func, token_placement):
mock_response = AsyncMockDispatch({'a': 'a'}, assert_func=assert_func)
transport = ASGITransport(AsyncMockDispatch({'a': 'a'}, assert_func=assert_func))
async with AsyncOAuth2Client(
'foo',
token=default_token,
token_placement=token_placement,
app=mock_response
transport=transport
) as client:
async with client.stream("GET", 'https://i.b') as stream:
await stream.aread()
......@@ -94,9 +94,9 @@ async def test_add_token_to_streaming_request(assert_func, token_placement):
'foo',
token=default_token,
token_placement="header",
app=AsyncMockDispatch({'a': 'a'}, assert_func=assert_token_in_header)
transport=ASGITransport(AsyncMockDispatch({'a': 'a'}, assert_func=assert_token_in_header)),
),
AsyncClient(transport=ASGITransport(app=AsyncMockDispatch({'a': 'a'})))
AsyncClient(transport=ASGITransport(AsyncMockDispatch({'a': 'a'})))
])
async def test_httpx_client_stream_match(client):
async with client as client_entered:
......@@ -151,21 +151,21 @@ async def test_fetch_token_post():
assert 'client_id=' in content
assert 'grant_type=authorization_code' in content
mock_response = AsyncMockDispatch(default_token, assert_func=assert_func)
async with AsyncOAuth2Client('foo', app=mock_response) as client:
transport = ASGITransport(AsyncMockDispatch(default_token, assert_func=assert_func))
async with AsyncOAuth2Client('foo', transport=transport) as client:
token = await client.fetch_token(url, authorization_response='https://i.b/?code=v')
assert token == default_token
async with AsyncOAuth2Client(
'foo',
token_endpoint_auth_method='none',
app=mock_response
transport=transport
) as client:
token = await client.fetch_token(url, code='v')
assert token == default_token
mock_response = AsyncMockDispatch({'error': 'invalid_request'})
async with AsyncOAuth2Client('foo', app=mock_response) as client:
transport = ASGITransport(AsyncMockDispatch({'error': 'invalid_request'}))
async with AsyncOAuth2Client('foo', transport=transport) as client:
with pytest.raises(OAuthError):
await client.fetch_token(url)
......@@ -180,8 +180,8 @@ async def test_fetch_token_get():
assert 'client_id=' in url
assert 'grant_type=authorization_code' in url
mock_response = AsyncMockDispatch(default_token, assert_func=assert_func)
async with AsyncOAuth2Client('foo', app=mock_response) as client:
transport = ASGITransport(AsyncMockDispatch(default_token, assert_func=assert_func))
async with AsyncOAuth2Client('foo', transport=transport) as client:
authorization_response = 'https://i.b/?code=v'
token = await client.fetch_token(
url, authorization_response=authorization_response, method='GET')
......@@ -190,7 +190,7 @@ async def test_fetch_token_get():
async with AsyncOAuth2Client(
'foo',
token_endpoint_auth_method='none',
app=mock_response
transport=transport
) as client:
token = await client.fetch_token(url, code='v', method='GET')
assert token == default_token
......@@ -211,11 +211,11 @@ async def test_token_auth_method_client_secret_post():
assert 'client_secret=bar' in content
assert 'grant_type=authorization_code' in content
mock_response = AsyncMockDispatch(default_token, assert_func=assert_func)
transport = ASGITransport(AsyncMockDispatch(default_token, assert_func=assert_func))
async with AsyncOAuth2Client(
'foo', 'bar',
token_endpoint_auth_method='client_secret_post',
app=mock_response
transport=transport
) as client:
token = await client.fetch_token(url, code='v')
......@@ -231,8 +231,8 @@ async def test_access_token_response_hook():
return resp
access_token_response_hook = mock.Mock(side_effect=_access_token_response_hook)
app = AsyncMockDispatch(default_token)
async with AsyncOAuth2Client('foo', token=default_token, app=app) as sess:
transport = ASGITransport(AsyncMockDispatch(default_token))
async with AsyncOAuth2Client('foo', token=default_token, transport=transport) as sess:
sess.register_compliance_hook(
'access_token_response',
access_token_response_hook
......@@ -252,8 +252,8 @@ async def test_password_grant_type():
assert 'scope=profile' in content
assert 'grant_type=password' in content
app = AsyncMockDispatch(default_token, assert_func=assert_func)
async with AsyncOAuth2Client('foo', scope='profile', app=app) as sess:
transport = ASGITransport(AsyncMockDispatch(default_token, assert_func=assert_func))
async with AsyncOAuth2Client('foo', scope='profile', transport=transport) as sess:
token = await sess.fetch_token(url, username='v', password='v')
assert token == default_token
......@@ -272,8 +272,8 @@ async def test_client_credentials_type():
assert 'scope=profile' in content
assert 'grant_type=client_credentials' in content
app = AsyncMockDispatch(default_token, assert_func=assert_func)
async with AsyncOAuth2Client('foo', scope='profile', app=app) as sess:
transport = ASGITransport(AsyncMockDispatch(default_token, assert_func=assert_func))
async with AsyncOAuth2Client('foo', scope='profile', transport=transport) as sess:
token = await sess.fetch_token(url)
assert token == default_token
......@@ -290,9 +290,9 @@ async def test_cleans_previous_token_before_fetching_new_one():
new_token['expires_at'] = now + 3600
url = 'https://example.com/token'
app = AsyncMockDispatch(new_token)
transport = ASGITransport(AsyncMockDispatch(new_token))
with mock.patch('time.time', lambda: now):
async with AsyncOAuth2Client('foo', token=default_token, app=app) as sess:
async with AsyncOAuth2Client('foo', token=default_token, transport=transport) as sess:
assert await sess.fetch_token(url) == new_token
......@@ -316,10 +316,10 @@ async def test_auto_refresh_token():
token_type='bearer', expires_at=100
)
app = AsyncMockDispatch(default_token)
transport = ASGITransport(AsyncMockDispatch(default_token))
async with AsyncOAuth2Client(
'foo', token=old_token, token_endpoint='https://i.b/token',
update_token=update_token, app=app
update_token=update_token, transport=transport
) as sess:
await sess.get('https://i.b/user')
assert update_token.called is True
......@@ -331,7 +331,7 @@ async def test_auto_refresh_token():
)
async with AsyncOAuth2Client(
'foo', token=old_token, token_endpoint='https://i.b/token',
update_token=update_token, app=app
update_token=update_token, transport=transport
) as sess:
with pytest.raises(OAuthError):
await sess.get('https://i.b/user')
......@@ -352,13 +352,13 @@ async def test_auto_refresh_token2():
expires_at=100
)
app = AsyncMockDispatch(default_token)
transport = ASGITransport(AsyncMockDispatch(default_token))
async with AsyncOAuth2Client(
'foo', token=old_token,
token_endpoint='https://i.b/token',
grant_type='client_credentials',
app=app,
transport=transport,
) as client:
await client.get('https://i.b/user')
assert update_token.called is False
......@@ -366,7 +366,7 @@ async def test_auto_refresh_token2():
async with AsyncOAuth2Client(
'foo', token=old_token, token_endpoint='https://i.b/token',
update_token=update_token, grant_type='client_credentials',
app=app,
transport=transport,
) as client:
await client.get('https://i.b/user')
assert update_token.called is True
......@@ -386,12 +386,12 @@ async def test_auto_refresh_token3():
expires_at=100
)
app = AsyncMockDispatch(default_token)
transport = ASGITransport(AsyncMockDispatch(default_token))
async with AsyncOAuth2Client(
'foo', token=old_token, token_endpoint='https://i.b/token',
update_token=update_token, grant_type='client_credentials',
app=app,
transport=transport,
) as client:
await client.post('https://i.b/user', json={'foo': 'bar'})
assert update_token.called is True
......@@ -412,12 +412,12 @@ async def test_auto_refresh_token4():
expires_at=100
)
app = AsyncMockDispatch(default_token)
transport = ASGITransport(AsyncMockDispatch(default_token))
async with AsyncOAuth2Client(
'foo', token=old_token, token_endpoint='https://i.b/token',
update_token=update_token, grant_type='client_credentials',
app=app,
transport=transport,
) as client:
coroutines = [client.get('https://i.b/user') for x in range(10)]
await asyncio.gather(*coroutines)
......@@ -426,9 +426,9 @@ async def test_auto_refresh_token4():
@pytest.mark.asyncio
async def test_revoke_token():
answer = {'status': 'ok'}
app = AsyncMockDispatch(answer)
transport = ASGITransport(AsyncMockDispatch(answer))
async with AsyncOAuth2Client('a', app=app) as sess:
async with AsyncOAuth2Client('a', transport=transport) as sess:
resp = await sess.revoke_token('https://i.b/token', 'hi')
assert resp.json() == answer
......@@ -441,6 +441,7 @@ async def test_revoke_token():
@pytest.mark.asyncio
async def test_request_without_token():
async with AsyncOAuth2Client('a', app=AsyncMockDispatch()) as client:
transport = ASGITransport(AsyncMockDispatch())
async with AsyncOAuth2Client('a', transport=transport) as client:
with pytest.raises(OAuthError):
await client.get('https://i.b/token')
import pytest
from httpx import WSGITransport
from authlib.integrations.httpx_client import (
OAuthError,
OAuth1Client,
......@@ -18,8 +19,8 @@ def test_fetch_request_token_via_header():
assert 'oauth_consumer_key="id"' in auth_header
assert 'oauth_signature=' in auth_header
app = MockDispatch(request_token, assert_func=assert_func)
with OAuth1Client('id', 'secret', app=app) as client:
transport = WSGITransport(MockDispatch(request_token, assert_func=assert_func))
with OAuth1Client('id', 'secret', transport=transport) as client:
response = client.fetch_request_token(oauth_url)
assert response == request_token
......@@ -36,11 +37,11 @@ def test_fetch_request_token_via_body():
assert content.get('oauth_consumer_key') == 'id'
assert 'oauth_signature' in content
mock_response = MockDispatch(request_token, assert_func=assert_func)
transport = WSGITransport(MockDispatch(request_token, assert_func=assert_func))
with OAuth1Client(
'id', 'secret', signature_type=SIGNATURE_TYPE_BODY,
app=mock_response,
transport=transport,
) as client:
response = client.fetch_request_token(oauth_url)
......@@ -58,11 +59,11 @@ def test_fetch_request_token_via_query():
assert 'oauth_consumer_key=id' in url
assert '&oauth_signature=' in url
mock_response = MockDispatch(request_token, assert_func=assert_func)
transport = WSGITransport(MockDispatch(request_token, assert_func=assert_func))
with OAuth1Client(
'id', 'secret', signature_type=SIGNATURE_TYPE_QUERY,
app=mock_response,
transport=transport,
) as client:
response = client.fetch_request_token(oauth_url)
......@@ -79,10 +80,10 @@ def test_fetch_access_token():
assert 'oauth_consumer_key="id"' in auth_header
assert 'oauth_signature=' in auth_header
mock_response = MockDispatch(request_token, assert_func=assert_func)
transport = WSGITransport(MockDispatch(request_token, assert_func=assert_func))
with OAuth1Client(
'id', 'secret', token='foo', token_secret='bar',
app=mock_response,
transport=transport,
) as client:
with pytest.raises(OAuthError):
client.fetch_access_token(oauth_url)
......@@ -93,10 +94,10 @@ def test_fetch_access_token():
def test_get_via_header():
mock_response = MockDispatch(b'hello')
transport = WSGITransport(MockDispatch(b'hello'))
with OAuth1Client(
'id', 'secret', token='foo', token_secret='bar',
app=mock_response,
transport=transport,
) as client:
response = client.get('https://example.com/')
......@@ -115,11 +116,11 @@ def test_get_via_body():
assert content.get('oauth_consumer_key') == 'id'
assert 'oauth_signature' in content
mock_response = MockDispatch(b'hello', assert_func=assert_func)
transport = WSGITransport(MockDispatch(b'hello', assert_func=assert_func))
with OAuth1Client(
'id', 'secret', token='foo', token_secret='bar',
signature_type=SIGNATURE_TYPE_BODY,
app=mock_response,
transport=transport,
) as client:
response = client.post('https://example.com/')
......@@ -131,11 +132,11 @@ def test_get_via_body():
def test_get_via_query():
mock_response = MockDispatch(b'hello')
transport = WSGITransport(MockDispatch(b'hello'))
with OAuth1Client(
'id', 'secret', token='foo', token_secret='bar',
signature_type=SIGNATURE_TYPE_QUERY,
app=mock_response,
transport=transport,
) as client:
response = client.get('https://example.com/')
......
......@@ -2,6 +2,7 @@ import time
import pytest
from unittest import mock
from copy import deepcopy
from httpx import WSGITransport
from authlib.common.security import generate_token
from authlib.common.urls import url_encode
from authlib.integrations.httpx_client import (
......@@ -45,12 +46,12 @@ def assert_token_in_uri(request):
]
)
def test_add_token_get_request(assert_func, token_placement):
mock_response = MockDispatch({'a': 'a'}, assert_func=assert_func)
transport = WSGITransport(MockDispatch({'a': 'a'}, assert_func=assert_func))
with OAuth2Client(
'foo',
token=default_token,
token_placement=token_placement,
app=mock_response
transport=transport
) as client:
resp = client.get('https://i.b')
......@@ -67,12 +68,12 @@ def test_add_token_get_request(assert_func, token_placement):
]
)
def test_add_token_to_streaming_request(assert_func, token_placement):
mock_response = MockDispatch({'a': 'a'}, assert_func=assert_func)
transport = WSGITransport(MockDispatch({'a': 'a'}, assert_func=assert_func))
with OAuth2Client(
'foo',
token=default_token,
token_placement=token_placement,
app=mock_response
transport=transport
) as client:
with client.stream("GET", 'https://i.b') as stream:
stream.read()
......@@ -125,21 +126,21 @@ def test_fetch_token_post():
assert content.get('client_id') == 'foo'
assert content.get('grant_type') == 'authorization_code'
mock_response = MockDispatch(default_token, assert_func=assert_func)
with OAuth2Client('foo', app=mock_response) as client:
transport = WSGITransport(MockDispatch(default_token, assert_func=assert_func))
with OAuth2Client('foo', transport=transport) as client:
token = client.fetch_token(url, authorization_response='https://i.b/?code=v')
assert token == default_token
with OAuth2Client(
'foo',
token_endpoint_auth_method='none',
app=mock_response
transport=transport
) as client:
token = client.fetch_token(url, code='v')
assert token == default_token
mock_response = MockDispatch({'error': 'invalid_request'})
with OAuth2Client('foo', app=mock_response) as client:
transport = WSGITransport(MockDispatch({'error': 'invalid_request'}))
with OAuth2Client('foo', transport=transport) as client:
with pytest.raises(OAuthError):
client.fetch_token(url)
......@@ -153,8 +154,8 @@ def test_fetch_token_get():
assert 'client_id=' in url
assert 'grant_type=authorization_code' in url
mock_response = MockDispatch(default_token, assert_func=assert_func)
with OAuth2Client('foo', app=mock_response) as client:
transport = WSGITransport(MockDispatch(default_token, assert_func=assert_func))
with OAuth2Client('foo', transport=transport) as client:
authorization_response = 'https://i.b/?code=v'
token = client.fetch_token(
url, authorization_response=authorization_response, method='GET')
......@@ -163,7 +164,7 @@ def test_fetch_token_get():
with OAuth2Client(
'foo',
token_endpoint_auth_method='none',
app=mock_response
transport=transport
) as client:
token = client.fetch_token(url, code='v', method='GET')
assert token == default_token
......@@ -182,11 +183,11 @@ def test_token_auth_method_client_secret_post():
assert content.get('client_secret') == 'bar'
assert content.get('grant_type') == 'authorization_code'
mock_response = MockDispatch(default_token, assert_func=assert_func)
transport = WSGITransport(MockDispatch(default_token, assert_func=assert_func))
with OAuth2Client(
'foo', 'bar',
token_endpoint_auth_method='client_secret_post',
app=mock_response
transport=transport
) as client:
token = client.fetch_token(url, code='v')
......@@ -201,8 +202,8 @@ def test_access_token_response_hook():
return resp
access_token_response_hook = mock.Mock(side_effect=_access_token_response_hook)
app = MockDispatch(default_token)
with OAuth2Client('foo', token=default_token, app=app) as sess:
transport = WSGITransport(MockDispatch(default_token))
with OAuth2Client('foo', token=default_token, transport=transport) as sess:
sess.register_compliance_hook(
'access_token_response',
access_token_response_hook
......@@ -220,8 +221,8 @@ def test_password_grant_type():
assert content.get('scope') == 'profile'
assert content.get('grant_type') == 'password'
app = MockDispatch(default_token, assert_func=assert_func)
with OAuth2Client('foo', scope='profile', app=app) as sess:
transport = WSGITransport(MockDispatch(default_token, assert_func=assert_func))
with OAuth2Client('foo', scope='profile', transport=transport) as sess:
token = sess.fetch_token(url, username='v', password='v')
assert token == default_token
......@@ -238,8 +239,8 @@ def test_client_credentials_type():
assert content.get('scope') == 'profile'
assert content.get('grant_type') == 'client_credentials'
app = MockDispatch(default_token, assert_func=assert_func)
with OAuth2Client('foo', scope='profile', app=app) as sess:
transport = WSGITransport(MockDispatch(default_token, assert_func=assert_func))
with OAuth2Client('foo', scope='profile', transport=transport) as sess:
token = sess.fetch_token(url)
assert token == default_token
......@@ -255,9 +256,9 @@ def test_cleans_previous_token_before_fetching_new_one():
new_token['expires_at'] = now + 3600
url = 'https://example.com/token'
app = MockDispatch(new_token)
transport = WSGITransport(MockDispatch(new_token))
with mock.patch('time.time', lambda: now):
with OAuth2Client('foo', token=default_token, app=app) as sess:
with OAuth2Client('foo', token=default_token, transport=transport) as sess:
assert sess.fetch_token(url) == new_token
......@@ -280,10 +281,10 @@ def test_auto_refresh_token():
token_type='bearer', expires_at=100
)
app = MockDispatch(default_token)
transport = WSGITransport(MockDispatch(default_token))
with OAuth2Client(
'foo', token=old_token, token_endpoint='https://i.b/token',
update_token=update_token, app=app
update_token=update_token, transport=transport
) as sess:
sess.get('https://i.b/user')
assert update_token.called is True
......@@ -295,7 +296,7 @@ def test_auto_refresh_token():
)
with OAuth2Client(
'foo', token=old_token, token_endpoint='https://i.b/token',
update_token=update_token, app=app
update_token=update_token, transport=transport
) as sess:
with pytest.raises(OAuthError):
sess.get('https://i.b/user')
......@@ -315,13 +316,13 @@ def test_auto_refresh_token2():
expires_at=100
)
app = MockDispatch(default_token)
transport = WSGITransport(MockDispatch(default_token))
with OAuth2Client(
'foo', token=old_token,
token_endpoint='https://i.b/token',
grant_type='client_credentials',
app=app,
transport=transport,
) as client:
client.get('https://i.b/user')
assert update_token.called is False
......@@ -329,7 +330,7 @@ def test_auto_refresh_token2():
with OAuth2Client(
'foo', token=old_token, token_endpoint='https://i.b/token',
update_token=update_token, grant_type='client_credentials',
app=app,
transport=transport,
) as client:
client.get('https://i.b/user')
assert update_token.called is True
......@@ -348,12 +349,12 @@ def test_auto_refresh_token3():
expires_at=100
)
app = MockDispatch(default_token)
transport = WSGITransport(MockDispatch(default_token))
with OAuth2Client(
'foo', token=old_token, token_endpoint='https://i.b/token',
update_token=update_token, grant_type='client_credentials',
app=app,
transport=transport,
) as client:
client.post('https://i.b/user', json={'foo': 'bar'})
assert update_token.called is True
......@@ -361,9 +362,9 @@ def test_auto_refresh_token3():
def test_revoke_token():
answer = {'status': 'ok'}
app = MockDispatch(answer)
transport = WSGITransport(MockDispatch(answer))
with OAuth2Client('a', app=app) as sess:
with OAuth2Client('a', transport=transport) as sess:
resp = sess.revoke_token('https://i.b/token', 'hi')
assert resp.json() == answer
......@@ -375,6 +376,7 @@ def test_revoke_token():
def test_request_without_token():
with OAuth2Client('a', app=MockDispatch()) as client:
transport = WSGITransport(MockDispatch())
with OAuth2Client('a', transport=transport) as client:
with pytest.raises(OAuthError):
client.get('https://i.b/token')
import pytest
from httpx import ASGITransport
from starlette.config import Config
from starlette.requests import Request
from authlib.common.urls import urlparse, url_decode
......@@ -40,10 +41,10 @@ def test_register_with_overwrite():
@pytest.mark.asyncio
async def test_oauth1_authorize():
oauth = OAuth()
app = AsyncPathMapDispatch({
transport = ASGITransport(AsyncPathMapDispatch({
'/request-token': {'body': 'oauth_token=foo&oauth_verifier=baz'},
'/token': {'body': 'oauth_token=a&oauth_token_secret=b'},
})
}))
client = oauth.register(
'dev',
client_id='dev',
......@@ -53,7 +54,7 @@ async def test_oauth1_authorize():
access_token_url='https://i.b/token',
authorize_url='https://i.b/authorize',
client_kwargs={
'app': app,
'transport': transport,
}
)
......@@ -72,9 +73,9 @@ async def test_oauth1_authorize():
@pytest.mark.asyncio
async def test_oauth2_authorize():
oauth = OAuth()
app = AsyncPathMapDispatch({
transport = ASGITransport(AsyncPathMapDispatch({
'/token': {'body': get_bearer_token()}
})
}))
client = oauth.register(
'dev',
client_id='dev',
......@@ -83,7 +84,7 @@ async def test_oauth2_authorize():
access_token_url='https://i.b/token',
authorize_url='https://i.b/authorize',
client_kwargs={
'app': app,
'transport': transport,
}
)
......@@ -112,9 +113,9 @@ async def test_oauth2_authorize():
@pytest.mark.asyncio
async def test_oauth2_authorize_access_denied():
oauth = OAuth()
app = AsyncPathMapDispatch({
transport = ASGITransport(AsyncPathMapDispatch({
'/token': {'body': get_bearer_token()}
})
}))
client = oauth.register(
'dev',
client_id='dev',
......@@ -123,7 +124,7 @@ async def test_oauth2_authorize_access_denied():
access_token_url='https://i.b/token',
authorize_url='https://i.b/authorize',
client_kwargs={
'app': app,
'transport': transport,
}
)
......@@ -139,9 +140,9 @@ async def test_oauth2_authorize_access_denied():
@pytest.mark.asyncio
async def test_oauth2_authorize_code_challenge():
app = AsyncPathMapDispatch({
transport = ASGITransport(AsyncPathMapDispatch({
'/token': {'body': get_bearer_token()}
})
}))
oauth = OAuth()
client = oauth.register(
'dev',
......@@ -151,7 +152,7 @@ async def test_oauth2_authorize_code_challenge():
authorize_url='https://i.b/authorize',
client_kwargs={
'code_challenge_method': 'S256',
'app': app,
'transport': transport,
},
)
......@@ -189,9 +190,9 @@ async def test_with_fetch_token_in_register():
async def fetch_token(request):
return {'access_token': 'dev', 'token_type': 'bearer'}
app = AsyncPathMapDispatch({
transport = ASGITransport(AsyncPathMapDispatch({
'/user': {'body': {'sub': '123'}}
})
}))
oauth = OAuth()
client = oauth.register(
'dev',
......@@ -202,7 +203,7 @@ async def test_with_fetch_token_in_register():
authorize_url='https://i.b/authorize',
fetch_token=fetch_token,
client_kwargs={
'app': app,
'transport': transport,
}
)
......@@ -217,9 +218,9 @@ async def test_with_fetch_token_in_oauth():
async def fetch_token(name, request):
return {'access_token': 'dev', 'token_type': 'bearer'}
app = AsyncPathMapDispatch({
transport = ASGITransport(AsyncPathMapDispatch({
'/user': {'body': {'sub': '123'}}
})
}))
oauth = OAuth(fetch_token=fetch_token)
client = oauth.register(
'dev',
......@@ -229,7 +230,7 @@ async def test_with_fetch_token_in_oauth():
access_token_url='https://i.b/token',
authorize_url='https://i.b/authorize',
client_kwargs={
'app': app,
'transport': transport,
}
)
......@@ -242,9 +243,9 @@ async def test_with_fetch_token_in_oauth():
@pytest.mark.asyncio
async def test_request_withhold_token():
oauth = OAuth()
app = AsyncPathMapDispatch({
transport = ASGITransport(AsyncPathMapDispatch({
'/user': {'body': {'sub': '123'}}
})
}))
client = oauth.register(
"dev",
client_id="dev",
......@@ -253,7 +254,7 @@ async def test_request_withhold_token():
access_token_url="https://i.b/token",
authorize_url="https://i.b/authorize",
client_kwargs={
'app': app,
'transport': transport,
}
)
req_scope = {'type': 'http', 'session': {}}
......@@ -281,11 +282,11 @@ async def test_oauth2_authorize_no_url():
@pytest.mark.asyncio
async def test_oauth2_authorize_with_metadata():
oauth = OAuth()
app = AsyncPathMapDispatch({
transport = ASGITransport(AsyncPathMapDispatch({
'/.well-known/openid-configuration': {'body': {
'authorization_endpoint': 'https://i.b/authorize'
}}
})
}))
client = oauth.register(
'dev',
client_id='dev',
......@@ -294,7 +295,7 @@ async def test_oauth2_authorize_with_metadata():
access_token_url='https://i.b/token',
server_metadata_url='https://i.b/.well-known/openid-configuration',
client_kwargs={
'app': app,
'transport': transport,
}
)
req_scope = {'type': 'http', 'session': {}}
......
import pytest
from httpx import ASGITransport
from starlette.requests import Request
from authlib.integrations.starlette_client import OAuth
from authlib.jose import JsonWebKey
......@@ -16,9 +17,9 @@ async def run_fetch_userinfo(payload):
async def fetch_token(request):
return get_bearer_token()
app = AsyncPathMapDispatch({
transport = ASGITransport(AsyncPathMapDispatch({
'/userinfo': {'body': payload}
})
}))
client = oauth.register(
'dev',
......@@ -27,7 +28,7 @@ async def run_fetch_userinfo(payload):
fetch_token=fetch_token,
userinfo_endpoint='https://i.b/userinfo',
client_kwargs={
'app': app,
'transport': transport,
}
)
......@@ -110,9 +111,9 @@ async def test_force_fetch_jwks_uri():
)
token['id_token'] = id_token
app = AsyncPathMapDispatch({
transport = ASGITransport(AsyncPathMapDispatch({
'/jwks': {'body': read_key_file('jwks_public.json')}
})
}))
oauth = OAuth()
client = oauth.register(
......@@ -123,7 +124,7 @@ async def test_force_fetch_jwks_uri():
jwks_uri='https://i.b/jwks',
issuer='https://i.b',
client_kwargs={
'app': app,
'transport': transport,
}
)
user = await client.parse_id_token(token, nonce='n')
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment