def __init__(self, client_id=None, client_secret=None, token_endpoint_auth_method=None, revocation_endpoint_auth_method=None, scope=None, redirect_uri=None, token=None, token_placement='header', update_token=None, **kwargs): # extract httpx.Client kwargs client_kwargs = self._extract_session_request_params(kwargs) Client.__init__(self, **client_kwargs) _OAuth2Client.__init__( self, session=self, client_id=client_id, client_secret=client_secret, token_endpoint_auth_method=token_endpoint_auth_method, revocation_endpoint_auth_method=revocation_endpoint_auth_method, scope=scope, redirect_uri=redirect_uri, token=token, token_placement=token_placement, update_token=update_token, **kwargs)
def __init__(self, client_id=None, client_secret=None, token_endpoint_auth_method=None, revocation_endpoint_auth_method=None, scope=None, redirect_uri=None, token=None, token_placement='header', update_token=None, **kwargs): # extract httpx.Client kwargs client_kwargs = self._extract_session_request_params(kwargs) AsyncClient.__init__(self, **client_kwargs) # We use a "reverse" Event to synchronize coroutines to prevent # multiple concurrent attempts to refresh the same token self._token_refresh_event = asyncio.Event() self._token_refresh_event.set() _OAuth2Client.__init__( self, session=None, client_id=client_id, client_secret=client_secret, token_endpoint_auth_method=token_endpoint_auth_method, revocation_endpoint_auth_method=revocation_endpoint_auth_method, scope=scope, redirect_uri=redirect_uri, token=token, token_placement=token_placement, update_token=update_token, **kwargs )
def __init__(self, client_id=None, client_secret=None, token_endpoint_auth_method=None, refresh_token_url=None, refresh_token_params=None, scope=None, redirect_uri=None, token=None, token_placement='header', state=None, token_updater=None, **kwargs): Session.__init__(self) OAuth2Client.__init__(self, session=self, client_id=client_id, client_secret=client_secret, client_auth_method=token_endpoint_auth_method, refresh_token_url=refresh_token_url, refresh_token_params=refresh_token_params, scope=scope, redirect_uri=redirect_uri, token=token, token_placement=token_placement, state=state, token_updater=token_updater, **kwargs) self.token_endpoint_auth_method = token_endpoint_auth_method
def __init__(self, client_id=None, client_secret=None, token_endpoint_auth_method=None, revocation_endpoint_auth_method=None, scope=None, redirect_uri=None, token=None, token_placement='header', update_token=None, **kwargs): Session.__init__(self) OAuth2Client.__init__( self, session=self, client_id=client_id, client_secret=client_secret, token_endpoint_auth_method=token_endpoint_auth_method, revocation_endpoint_auth_method=revocation_endpoint_auth_method, scope=scope, redirect_uri=redirect_uri, token=token, token_placement=token_placement, update_token=update_token, **kwargs)
def test_pkce_disabled(dummy_application, test_client, dummy_user, pkce_enabled): dummy_application.allow_pkce_flow = pkce_enabled oauth_client = OAuth2Client(MockSession(test_client), dummy_application.client_id, None, code_challenge_method='S256', token_endpoint=url_for('oauth.oauth_token', _external=True), redirect_uri=dummy_application.default_redirect_uri) with test_client.session_transaction() as sess: sess.set_session_user(dummy_user) code_verifier = generate_token(64) auth_endpoint = url_for('oauth.oauth_authorize', _external=True) auth_url = oauth_client.create_authorization_url(auth_endpoint, scope='read:legacy_api', code_verifier=code_verifier)[0] authorized_resp = test_client.post(auth_url, data={'confirm': '1'}) assert authorized_resp.status_code == 302 target_url = authorized_resp.headers['Location'] if pkce_enabled: token = oauth_client.fetch_token(authorization_response=target_url, code_verifier=code_verifier) assert token.keys() == {'access_token', 'token_type', 'scope'} else: with pytest.raises(ValueError) as exc_info: oauth_client.fetch_token(authorization_response=target_url, code_verifier=code_verifier) assert 'invalid_client' in str(exc_info.value)
def test_oauth_flows(create_application, test_client, dummy_user, app, trusted, token_endpoint_auth_method, pkce): oauth_app = create_application(name='test', is_trusted=trusted) oauth_client = OAuth2Client( MockSession(test_client), oauth_app.client_id, oauth_app.client_secret if not pkce else None, code_challenge_method=('S256' if pkce else None), scope='read:user', response_type='code', token_endpoint=url_for('oauth.oauth_token', _external=True), token_endpoint_auth_method=token_endpoint_auth_method, redirect_uri=oauth_app.default_redirect_uri) code_verifier = generate_token(64) if pkce else None auth_url, state = oauth_client.create_authorization_url( url_for('oauth.oauth_authorize', _external=True), code_verifier=code_verifier) with test_client.session_transaction() as sess: sess.set_session_user(dummy_user) # get consent page resp = test_client.get(auth_url) if trusted: authorized_resp = resp else: assert resp.status_code == 200 assert b'is requesting the following permissions' in resp.data assert b'User information (read only)' in resp.data # give consent authorized_resp = test_client.post(auth_url, data={'confirm': '1'}) assert authorized_resp.status_code == 302 target_url = authorized_resp.headers['Location'] target_url_parts = url_parse(target_url) assert f'state={state}' in target_url_parts.query assert target_url == f'{oauth_app.default_redirect_uri}?{target_url_parts.query}' # get a token and make sure it looks fine token = oauth_client.fetch_token(authorization_response=target_url, code_verifier=code_verifier) assert token == { 'access_token': token['access_token'], 'token_type': 'Bearer', 'scope': 'read:user' } with app.test_client() as test_client_no_session: # make sure we can use our token uri, headers, data = oauth_client.token_auth.prepare( '/api/user/', {}, '') resp = test_client_no_session.get(uri, data=data, headers=headers) assert resp.status_code == 200 assert resp.json['id'] == dummy_user.id # authorizing again won't require new consent, regardless of the app being trusted assert test_client.get(auth_url).status_code == 302
def __init__(self, client_id=None, client_secret=None, authorization_endpoint=None, token_endpoint=None, token_endpoint_auth_method=None, revocation_endpoint=None, revocation_endpoint_auth_method=None, scope=None, redirect_uri=None, token=None, token_placement='header', token_updater=None, **kwargs): refresh_token_url = kwargs.pop('refresh_token_url', None) if refresh_token_url is not None and token_endpoint is None: token_endpoint = refresh_token_url Session.__init__(self) OAuth2Client.__init__( self, session=self, client_id=client_id, client_secret=client_secret, authorization_endpoint=authorization_endpoint, token_endpoint=token_endpoint, token_endpoint_auth_method=token_endpoint_auth_method, revocation_endpoint=revocation_endpoint, revocation_endpoint_auth_method=revocation_endpoint_auth_method, scope=scope, redirect_uri=redirect_uri, token=token, token_placement=token_placement, token_updater=token_updater, **kwargs)
def __init__(self, client_id=None, client_secret=None, token_endpoint=None, token_endpoint_auth_method=None, scope=None, redirect_uri=None, token=None, token_placement='header', token_updater=None, **kwargs): session = ClientSession(request_class=OAuth2Request) _OAuth2Client.__init__(self, session=session, client_id=client_id, client_secret=client_secret, client_auth_method=token_endpoint_auth_method, refresh_token_url=token_endpoint, scope=scope, redirect_uri=redirect_uri, token=token, token_placement=token_placement, token_updater=token_updater, **kwargs)
def test_no_implicit_flow(dummy_application, test_client, dummy_user): oauth_client = OAuth2Client(None, dummy_application.client_id, None, scope='read:user', response_type='token', token_endpoint=url_for('oauth.oauth_token', _external=True), redirect_uri=dummy_application.default_redirect_uri) auth_url = oauth_client.create_authorization_url(url_for('oauth.oauth_authorize', _external=True))[0] with test_client.session_transaction() as sess: sess.set_session_user(dummy_user) resp = test_client.get(auth_url) assert b'unsupported_response_type' in resp.data
def test_checkin_app_implicit_flow(test_client, dummy_user): checkin_app = OAuthApplication.query.filter_by( system_app_type=SystemAppType.checkin).one() oauth_client = OAuth2Client(None, checkin_app.client_id, None, scope='read:user', response_type='token', token_endpoint=url_for('oauth.oauth_token', _external=True), redirect_uri=checkin_app.default_redirect_uri) auth_url = oauth_client.create_authorization_url( url_for('oauth.oauth_authorize', _external=True))[0] with test_client.session_transaction() as sess: sess.set_session_user(dummy_user) resp = test_client.get(auth_url) assert resp.status_code == 302 assert '#' in resp.headers['Location'] assert 'access_token=' in resp.headers['Location']
def test_oauth_scopes(create_application, test_client, dummy_user, app): oauth_app = create_application(name='test', is_trusted=False, allowed_scopes=['read:user', 'read:legacy_api']) oauth_client = OAuth2Client(MockSession(test_client), oauth_app.client_id, oauth_app.client_secret, token_endpoint=url_for('oauth.oauth_token', _external=True), redirect_uri=oauth_app.default_redirect_uri) with test_client.session_transaction() as sess: sess.set_session_user(dummy_user) auth_endpoint = url_for('oauth.oauth_authorize', _external=True) auth_url = oauth_client.create_authorization_url(auth_endpoint, scope='read:legacy_api')[0] assert not oauth_app.user_links.count() # get consent page resp = test_client.get(auth_url) assert resp.status_code == 200 assert b'is requesting the following permissions' in resp.data assert b'Classic API (read only)' in resp.data assert b'User information (read only)' not in resp.data # give consent authorized_resp = test_client.post(auth_url, data={'confirm': '1'}) assert authorized_resp.status_code == 302 target_url = authorized_resp.headers['Location'] assert not oauth_app.user_links.count() # giving consent does not create the user/app link yet # get a token and make sure it looks fine token1 = oauth_client.fetch_token(authorization_response=target_url) assert token1 == {'access_token': token1['access_token'], 'token_type': 'Bearer', 'scope': 'read:legacy_api'} assert len(token1['access_token']) == 47 # longer would be fine but we don't expect this to change assert token1['access_token'].startswith(TOKEN_PREFIX_OAUTH) app_link = oauth_app.user_links.one() assert app_link.user == dummy_user assert app_link.scopes == ['read:legacy_api'] # we cannot use a token with an invalid scope with app.test_client() as test_client_no_session: uri, headers, data = oauth_client.token_auth.prepare('/api/user/', {}, '') resp = test_client_no_session.get(uri, data=data, headers=headers) assert resp.status_code == 403 # request a different scope auth_url = oauth_client.create_authorization_url(auth_endpoint, scope='read:user')[0] authorized_resp = test_client.post(auth_url, data={'confirm': '1'}) target_url = authorized_resp.headers['Location'] token2 = oauth_client.fetch_token(authorization_response=target_url) assert token2 == {'access_token': token2['access_token'], 'token_type': 'Bearer', 'scope': 'read:user'} assert token2['access_token'] != token1['access_token'] assert app_link.scopes == ['read:legacy_api', 'read:user'] # this token is already able to access the endpoint with app.test_client() as test_client_no_session: uri, headers, data = oauth_client.token_auth.prepare('/api/user/', {}, '') resp = test_client_no_session.get(uri, data=data, headers=headers) assert resp.status_code == 200 assert resp.json['id'] == dummy_user.id # no scope specified, so we should get a token with all authorized scopes auth_url = oauth_client.create_authorization_url(auth_endpoint)[0] authorized_resp = test_client.post(auth_url, data={'confirm': '1'}) target_url = authorized_resp.headers['Location'] token3 = oauth_client.fetch_token(authorization_response=target_url) assert set(token3.pop('scope').split()) == {'read:legacy_api', 'read:user'} assert token3 == {'access_token': token3['access_token'], 'token_type': 'Bearer'} assert token3['access_token'] != token2['access_token'] assert app_link.scopes == ['read:legacy_api', 'read:user'] # and of course that token also has access to the endpoint with app.test_client() as test_client_no_session: uri, headers, data = oauth_client.token_auth.prepare('/api/user/', {}, '') resp = test_client_no_session.get(uri, data=data, headers=headers) assert resp.status_code == 200 assert resp.json['id'] == dummy_user.id # reuse an existing scope auth_url = oauth_client.create_authorization_url(auth_endpoint, scope='read:user')[0] authorized_resp = test_client.post(auth_url, data={'confirm': '1'}) target_url = authorized_resp.headers['Location'] token4 = oauth_client.fetch_token(authorization_response=target_url) assert token4 != token2 # can't reuse the old token since it's only stored as a hash