Skip to content

Commit 5e88d92

Browse files
authored
fix(auth): Setting httpTimeout on certificate fetch requests (#538)
* fix(auth): Setting httpTimeout on certificate fetch requests * fix: Removed unused import
1 parent 04c4069 commit 5e88d92

File tree

2 files changed

+78
-2
lines changed

2 files changed

+78
-2
lines changed

firebase_admin/_token_gen.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
from firebase_admin import exceptions
3131
from firebase_admin import _auth_utils
32+
from firebase_admin import _http_client
3233

3334

3435
# ID token constants
@@ -231,12 +232,37 @@ def create_session_cookie(self, id_token, expires_in):
231232
return body.get('sessionCookie')
232233

233234

235+
class CertificateFetchRequest(transport.Request):
236+
"""A google-auth transport that supports HTTP cache-control.
237+
238+
Also injects a timeout to each outgoing HTTP request.
239+
"""
240+
241+
def __init__(self, timeout_seconds=None):
242+
self._session = cachecontrol.CacheControl(requests.Session())
243+
self._delegate = transport.requests.Request(self.session)
244+
self._timeout_seconds = timeout_seconds
245+
246+
@property
247+
def session(self):
248+
return self._session
249+
250+
@property
251+
def timeout_seconds(self):
252+
return self._timeout_seconds
253+
254+
def __call__(self, url, method='GET', body=None, headers=None, timeout=None, **kwargs):
255+
timeout = timeout or self.timeout_seconds
256+
return self._delegate(
257+
url, method=method, body=body, headers=headers, timeout=timeout, **kwargs)
258+
259+
234260
class TokenVerifier:
235261
"""Verifies ID tokens and session cookies."""
236262

237263
def __init__(self, app):
238-
session = cachecontrol.CacheControl(requests.Session())
239-
self.request = transport.requests.Request(session=session)
264+
timeout = app.options.get('httpTimeout', _http_client.DEFAULT_TIMEOUT_SECONDS)
265+
self.request = CertificateFetchRequest(timeout)
240266
self.id_token_verifier = _JWTVerifier(
241267
project_id=app.project_id, short_name='ID token',
242268
operation='verify_id_token()',

tests/test_token_gen.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from firebase_admin import auth
3232
from firebase_admin import credentials
3333
from firebase_admin import exceptions
34+
from firebase_admin import _http_client
3435
from firebase_admin import _token_gen
3536
from tests import testutils
3637

@@ -702,3 +703,52 @@ def test_certificate_caching(self, user_mgt_app, httpserver):
702703
assert len(httpserver.requests) == request_count
703704
verifier.verify_id_token(TEST_ID_TOKEN)
704705
assert len(httpserver.requests) == request_count
706+
707+
708+
class TestCertificateFetchTimeout:
709+
710+
timeout_configs = [
711+
({'httpTimeout': 4}, 4),
712+
({'httpTimeout': None}, None),
713+
({}, _http_client.DEFAULT_TIMEOUT_SECONDS),
714+
]
715+
716+
@pytest.mark.parametrize('options, timeout', timeout_configs)
717+
def test_init_request(self, options, timeout):
718+
app = firebase_admin.initialize_app(MOCK_CREDENTIAL, options=options)
719+
720+
client = auth._get_client(app)
721+
request = client._token_verifier.request
722+
723+
assert isinstance(request, _token_gen.CertificateFetchRequest)
724+
assert request.timeout_seconds == timeout
725+
726+
@pytest.mark.parametrize('options, timeout', timeout_configs)
727+
def test_verify_id_token_timeout(self, options, timeout):
728+
app = firebase_admin.initialize_app(MOCK_CREDENTIAL, options=options)
729+
recorder = self._instrument_session(app)
730+
731+
auth.verify_id_token(TEST_ID_TOKEN)
732+
733+
assert len(recorder) == 1
734+
assert recorder[0]._extra_kwargs['timeout'] == timeout
735+
736+
@pytest.mark.parametrize('options, timeout', timeout_configs)
737+
def test_verify_session_cookie_timeout(self, options, timeout):
738+
app = firebase_admin.initialize_app(MOCK_CREDENTIAL, options=options)
739+
recorder = self._instrument_session(app)
740+
741+
auth.verify_session_cookie(TEST_SESSION_COOKIE)
742+
743+
assert len(recorder) == 1
744+
assert recorder[0]._extra_kwargs['timeout'] == timeout
745+
746+
def _instrument_session(self, app):
747+
client = auth._get_client(app)
748+
request = client._token_verifier.request
749+
recorder = []
750+
request.session.mount('https://', testutils.MockAdapter(MOCK_PUBLIC_CERTS, 200, recorder))
751+
return recorder
752+
753+
def teardown(self):
754+
testutils.cleanup_apps()

0 commit comments

Comments
 (0)