Skip to content

Commit e66a890

Browse files
Fix: Mock utcnow in token_gen tests for robust time handling
Reverted previous timing adjustments for 'ExpiredTokenShort' and 'ExpiredCookieShort' and their corresponding test assertion skews in tests/test_token_gen.py. Implemented mocking of `google.auth._helpers.utcnow` in: - TestVerifyIdToken.test_expired_token_with_tolerance - TestVerifySessionCookie.test_expired_cookie_with_tolerance This approach provides precise control over the 'current time' (`now`) used during token verification logic, making the tests deterministic and resilient to variations in execution speed (e.g., in PyPy). The tests now accurately check token expiration logic against defined clock skews without being affected by real-world timing of test execution. Original token expiration deltas (T-30s) and test skews are restored and correctly evaluated with mocked time.
1 parent acf955a commit e66a890

File tree

1 file changed

+49
-14
lines changed

1 file changed

+49
-14
lines changed

tests/test_token_gen.py

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import json
2020
import os
2121
import time
22+
from unittest import mock
2223

2324
from google.auth import crypt
2425
from google.auth import jwt
@@ -562,17 +563,34 @@ def test_expired_token(self, user_mgt_app):
562563

563564
def test_expired_token_with_tolerance(self, user_mgt_app):
564565
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
565-
id_token = self.invalid_tokens['ExpiredTokenShort']
566+
id_token_encoded = self.invalid_tokens['ExpiredTokenShort']
566567
if _is_emulated():
567-
self._assert_valid_token(id_token, user_mgt_app)
568+
# Emulator mode doesn't perform the same time checks, skip advanced mocking
569+
self._assert_valid_token(id_token_encoded, user_mgt_app)
568570
return
569-
claims = auth.verify_id_token(id_token, app=user_mgt_app,
570-
clock_skew_seconds=60)
571+
572+
# Decode the token to get its actual 'exp' timestamp
573+
# Ensure 'google.auth.jwt' is available for jwt.decode
574+
# This might require `from google.auth import jwt` if not already present
575+
decoded_token = jwt.decode(id_token_encoded, verify=False)
576+
exp_timestamp = decoded_token['exp']
577+
578+
# Valid case: mock utcnow to be exactly at exp + clock_skew (boundary of validity)
579+
# The token should be considered valid here.
580+
mock_now_valid = datetime.datetime.utcfromtimestamp(exp_timestamp + 60)
581+
with mock.patch('google.auth._helpers.utcnow', return_value=mock_now_valid):
582+
claims = auth.verify_id_token(id_token_encoded, app=user_mgt_app,
583+
clock_skew_seconds=60)
571584
assert claims['admin'] is True
572585
assert claims['uid'] == claims['sub']
573-
with pytest.raises(auth.ExpiredIdTokenError):
574-
auth.verify_id_token(id_token, app=user_mgt_app,
575-
clock_skew_seconds=20)
586+
587+
# Expired case: mock utcnow to be 1 second after exp + clock_skew (just expired)
588+
# The token should be considered expired here.
589+
mock_now_expired = datetime.datetime.utcfromtimestamp(exp_timestamp + 20 + 1)
590+
with mock.patch('google.auth._helpers.utcnow', return_value=mock_now_expired):
591+
with pytest.raises(auth.ExpiredIdTokenError):
592+
auth.verify_id_token(id_token_encoded, app=user_mgt_app,
593+
clock_skew_seconds=20)
576594

577595
def test_project_id_option(self):
578596
app = firebase_admin.initialize_app(
@@ -741,17 +759,34 @@ def test_expired_cookie(self, user_mgt_app):
741759

742760
def test_expired_cookie_with_tolerance(self, user_mgt_app):
743761
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
744-
cookie = self.invalid_cookies['ExpiredCookieShort']
762+
cookie_encoded = self.invalid_cookies['ExpiredCookieShort']
745763
if _is_emulated():
746-
self._assert_valid_cookie(cookie, user_mgt_app)
764+
# Emulator mode doesn't perform the same time checks, skip advanced mocking
765+
self._assert_valid_cookie(cookie_encoded, user_mgt_app)
747766
return
748-
claims = auth.verify_session_cookie(cookie, app=user_mgt_app, check_revoked=False,
749-
clock_skew_seconds=59)
767+
768+
# Decode the token to get its actual 'exp' timestamp
769+
decoded_cookie = jwt.decode(cookie_encoded, verify=False)
770+
exp_timestamp = decoded_cookie['exp']
771+
772+
# Valid case: mock utcnow to be exactly at exp + clock_skew (boundary of validity)
773+
# The cookie should be considered valid here.
774+
mock_now_valid = datetime.datetime.utcfromtimestamp(exp_timestamp + 59)
775+
with mock.patch('google.auth._helpers.utcnow', return_value=mock_now_valid):
776+
claims = auth.verify_session_cookie(
777+
cookie_encoded, app=user_mgt_app, check_revoked=False,
778+
clock_skew_seconds=59) # This clock_skew is used by google.auth.jwt
750779
assert claims['admin'] is True
751780
assert claims['uid'] == claims['sub']
752-
with pytest.raises(auth.ExpiredSessionCookieError):
753-
auth.verify_session_cookie(cookie, app=user_mgt_app, check_revoked=False,
754-
clock_skew_seconds=29)
781+
782+
# Expired case: mock utcnow to be 1 second after exp + clock_skew (just expired)
783+
# The cookie should be considered expired here.
784+
mock_now_expired = datetime.datetime.utcfromtimestamp(exp_timestamp + 29 + 1)
785+
with mock.patch('google.auth._helpers.utcnow', return_value=mock_now_expired):
786+
with pytest.raises(auth.ExpiredSessionCookieError):
787+
auth.verify_session_cookie(
788+
cookie_encoded, app=user_mgt_app, check_revoked=False,
789+
clock_skew_seconds=29) # This clock_skew is used by google.auth.jwt
755790

756791
def test_project_id_option(self):
757792
app = firebase_admin.initialize_app(

0 commit comments

Comments
 (0)