Skip to content

Commit 1d817f5

Browse files
committed
Accomodate auth emulator behaviour in tests.
Where possible, tests are modified to account for the current behaviour in emulator mode (e.g., invalid or expired tokens or cookies still work). In one case, signer verification didn't account for padding being stripped in JWT tokens, and was fixed to do so.
1 parent f687f38 commit 1d817f5

File tree

2 files changed

+87
-30
lines changed

2 files changed

+87
-30
lines changed

firebase_admin/_auth_client.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def verify_id_token(self, id_token, check_revoked=False):
127127
raise_auth_utils.TenantIdMismatchError(
128128
'Invalid tenant ID: {0}'.format(token_tenant_id))
129129

130-
ifnotself.emulatedandcheck_revoked:
130+
ifcheck_revoked:
131131
self._check_jwt_revoked(verified_claims, _token_gen.RevokedIdTokenError, 'ID token')
132132
returnverified_claims
133133

tests/test_token_gen.py

+86-29
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,18 @@ def _merge_jwt_claims(defaults, overrides):
7676

7777
defverify_custom_token(custom_token, expected_claims, tenant_id=None):
7878
assertisinstance(custom_token, bytes)
79-
token=google.oauth2.id_token.verify_token(
80-
custom_token,
81-
testutils.MockRequest(200, MOCK_PUBLIC_CERTS),
82-
_token_gen.FIREBASE_AUDIENCE)
79+
expected_email=MOCK_SERVICE_ACCOUNT_EMAIL
80+
if_is_emulated():
81+
expected_email=_token_gen.AUTH_EMULATOR_EMAIL
82+
token=jwt.decode(custom_token, verify=False)
83+
else:
84+
token=google.oauth2.id_token.verify_token(
85+
custom_token,
86+
testutils.MockRequest(200, MOCK_PUBLIC_CERTS),
87+
_token_gen.FIREBASE_AUDIENCE)
8388
asserttoken['uid'] ==MOCK_UID
84-
asserttoken['iss'] ==MOCK_SERVICE_ACCOUNT_EMAIL
85-
asserttoken['sub'] ==MOCK_SERVICE_ACCOUNT_EMAIL
89+
asserttoken['iss'] ==expected_email
90+
asserttoken['sub'] ==expected_email
8691
iftenant_idisNone:
8792
assert'tenant_id'notintoken
8893
else:
@@ -141,7 +146,15 @@ def _overwrite_iam_request(app, request):
141146
client=auth._get_client(app)
142147
client._token_generator.request=request
143148

144-
@pytest.fixture(scope='module', params=[{'emulated': False}, {'emulated': True}])
149+
150+
def_is_emulated():
151+
emulator_host=os.getenv(EMULATOR_HOST_ENV_VAR, '')
152+
returnemulator_hostand'//'notinemulator_host
153+
154+
155+
# These fixtures are set to the default function scope as the emulator environment variable bleeds
156+
# over when in module scope.
157+
@pytest.fixture(params=[{'emulated': False}, {'emulated': True}])
145158
defauth_app(request):
146159
"""Returns an App initialized with a mock service account credential.
147160
@@ -157,7 +170,7 @@ def auth_app(request):
157170
firebase_admin.delete_app(app)
158171
monkeypatch.undo()
159172

160-
@pytest.fixture(scope='module', params=[{'emulated': False}, {'emulated': True}])
173+
@pytest.fixture(params=[{'emulated': False}, {'emulated': True}])
161174
defuser_mgt_app(request):
162175
monkeypatch=testutils.new_monkeypatch()
163176
ifrequest.param['emulated']:
@@ -230,6 +243,12 @@ def test_invalid_params(self, auth_app, values):
230243
auth.create_custom_token(user, claims, app=auth_app)
231244

232245
deftest_noncert_credential(self, user_mgt_app):
246+
if_is_emulated():
247+
# Should work fine with the emulator, so do a condensed version of
248+
# test_sign_with_iam below.
249+
custom_token=auth.create_custom_token(MOCK_UID, app=user_mgt_app).decode()
250+
self._verify_signer(custom_token, _token_gen.AUTH_EMULATOR_EMAIL)
251+
return
233252
withpytest.raises(ValueError):
234253
auth.create_custom_token(MOCK_UID, app=user_mgt_app)
235254

@@ -304,7 +323,7 @@ def test_sign_with_discovery_failure(self):
304323
def_verify_signer(self, token, signer):
305324
segments=token.split('.')
306325
assertlen(segments) ==3
307-
body=json.loads(base64.b64decode(segments[1]).decode())
326+
body=jwt.decode(token, verify=False)
308327
assertbody['iss'] ==signer
309328
assertbody['sub'] ==signer
310329

@@ -406,14 +425,23 @@ class TestVerifyIdToken:
406425
'BadFormatToken': 'foobar'
407426
}
408427

409-
@pytest.mark.parametrize('id_token', valid_tokens.values(), ids=list(valid_tokens))
410-
deftest_valid_token(self, user_mgt_app, id_token):
411-
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
412-
claims=auth.verify_id_token(id_token, app=user_mgt_app)
428+
tokens_not_invalid_in_emulator= [
429+
'WrongKid',
430+
'FutureToken',
431+
'ExpiredToken'
432+
]
433+
434+
def_assert_valid_token(self, id_token, app):
435+
claims=auth.verify_id_token(id_token, app=app)
413436
assertclaims['admin'] isTrue
414437
assertclaims['uid'] ==claims['sub']
415438
assertclaims['firebase']['sign_in_provider'] =='provider'
416439

440+
@pytest.mark.parametrize('id_token', valid_tokens.values(), ids=list(valid_tokens))
441+
deftest_valid_token(self, user_mgt_app, id_token):
442+
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
443+
self._assert_valid_token(id_token, app=user_mgt_app)
444+
417445
deftest_valid_token_with_tenant(self, user_mgt_app):
418446
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
419447
claims=auth.verify_id_token(TEST_ID_TOKEN_WITH_TENANT, app=user_mgt_app)
@@ -458,8 +486,12 @@ def test_invalid_arg(self, user_mgt_app, id_token):
458486
auth.verify_id_token(id_token, app=user_mgt_app)
459487
assert'Illegal ID token provided'instr(excinfo.value)
460488

461-
@pytest.mark.parametrize('id_token', invalid_tokens.values(), ids=list(invalid_tokens))
462-
deftest_invalid_token(self, user_mgt_app, id_token):
489+
@pytest.mark.parametrize('id_token_key', list(invalid_tokens))
490+
deftest_invalid_token(self, user_mgt_app, id_token_key):
491+
id_token=self.invalid_tokens[id_token_key]
492+
if_is_emulated() andid_token_keyinself.tokens_not_invalid_in_emulator:
493+
self._assert_valid_token(id_token, user_mgt_app)
494+
return
463495
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
464496
withpytest.raises(auth.InvalidIdTokenError) asexcinfo:
465497
auth.verify_id_token(id_token, app=user_mgt_app)
@@ -469,6 +501,9 @@ def test_invalid_token(self, user_mgt_app, id_token):
469501
deftest_expired_token(self, user_mgt_app):
470502
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
471503
id_token=self.invalid_tokens['ExpiredToken']
504+
if_is_emulated():
505+
self._assert_valid_token(id_token, user_mgt_app)
506+
return
472507
withpytest.raises(auth.ExpiredIdTokenError) asexcinfo:
473508
auth.verify_id_token(id_token, app=user_mgt_app)
474509
assertisinstance(excinfo.value, auth.InvalidIdTokenError)
@@ -506,6 +541,10 @@ def test_custom_token(self, auth_app):
506541

507542
deftest_certificate_request_failure(self, user_mgt_app):
508543
_overwrite_cert_request(user_mgt_app, testutils.MockRequest(404, 'not found'))
544+
if_is_emulated():
545+
# Shouldn't fetch certificates in emulator mode.
546+
self._assert_valid_token(TEST_ID_TOKEN, app=user_mgt_app)
547+
return
509548
withpytest.raises(auth.CertificateFetchError) asexcinfo:
510549
auth.verify_id_token(TEST_ID_TOKEN, app=user_mgt_app)
511550
assert'Could not fetch certificates'instr(excinfo.value)
@@ -540,20 +579,27 @@ class TestVerifySessionCookie:
540579
'IDToken': TEST_ID_TOKEN,
541580
}
542581

582+
cookies_not_invalid_in_emulator= [
583+
'WrongKid',
584+
'FutureCookie',
585+
'ExpiredCookie'
586+
]
587+
588+
def_assert_valid_cookie(self, cookie, app, check_revoked=False):
589+
claims=auth.verify_session_cookie(cookie, app=app, check_revoked=check_revoked)
590+
assertclaims['admin'] isTrue
591+
assertclaims['uid'] ==claims['sub']
592+
543593
@pytest.mark.parametrize('cookie', valid_cookies.values(), ids=list(valid_cookies))
544594
deftest_valid_cookie(self, user_mgt_app, cookie):
545595
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
546-
claims=auth.verify_session_cookie(cookie, app=user_mgt_app)
547-
assertclaims['admin'] isTrue
548-
assertclaims['uid'] ==claims['sub']
596+
self._assert_valid_cookie(cookie, user_mgt_app)
549597

550598
@pytest.mark.parametrize('cookie', valid_cookies.values(), ids=list(valid_cookies))
551599
deftest_valid_cookie_check_revoked(self, user_mgt_app, cookie):
552600
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
553601
_instrument_user_manager(user_mgt_app, 200, MOCK_GET_USER_RESPONSE)
554-
claims=auth.verify_session_cookie(cookie, app=user_mgt_app, check_revoked=True)
555-
assertclaims['admin'] isTrue
556-
assertclaims['uid'] ==claims['sub']
602+
self._assert_valid_cookie(cookie, app=user_mgt_app, check_revoked=True)
557603

558604
@pytest.mark.parametrize('cookie', valid_cookies.values(), ids=list(valid_cookies))
559605
deftest_revoked_cookie_check_revoked(self, user_mgt_app, revoked_tokens, cookie):
@@ -567,9 +613,7 @@ def test_revoked_cookie_check_revoked(self, user_mgt_app, revoked_tokens, cookie
567613
deftest_revoked_cookie_does_not_check_revoked(self, user_mgt_app, revoked_tokens, cookie):
568614
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
569615
_instrument_user_manager(user_mgt_app, 200, revoked_tokens)
570-
claims=auth.verify_session_cookie(cookie, app=user_mgt_app, check_revoked=False)
571-
assertclaims['admin'] isTrue
572-
assertclaims['uid'] ==claims['sub']
616+
self._assert_valid_cookie(cookie, app=user_mgt_app, check_revoked=False)
573617

574618
@pytest.mark.parametrize('cookie', INVALID_JWT_ARGS.values(), ids=list(INVALID_JWT_ARGS))
575619
deftest_invalid_args(self, user_mgt_app, cookie):
@@ -578,8 +622,12 @@ def test_invalid_args(self, user_mgt_app, cookie):
578622
auth.verify_session_cookie(cookie, app=user_mgt_app)
579623
assert'Illegal session cookie provided'instr(excinfo.value)
580624

581-
@pytest.mark.parametrize('cookie', invalid_cookies.values(), ids=list(invalid_cookies))
582-
deftest_invalid_cookie(self, user_mgt_app, cookie):
625+
@pytest.mark.parametrize('cookie_key', list(invalid_cookies))
626+
deftest_invalid_cookie(self, user_mgt_app, cookie_key):
627+
cookie=self.invalid_cookies[cookie_key]
628+
if_is_emulated() andcookie_keyinself.cookies_not_invalid_in_emulator:
629+
self._assert_valid_cookie(cookie, user_mgt_app)
630+
return
583631
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
584632
withpytest.raises(auth.InvalidSessionCookieError) asexcinfo:
585633
auth.verify_session_cookie(cookie, app=user_mgt_app)
@@ -589,6 +637,9 @@ def test_invalid_cookie(self, user_mgt_app, cookie):
589637
deftest_expired_cookie(self, user_mgt_app):
590638
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
591639
cookie=self.invalid_cookies['ExpiredCookie']
640+
if_is_emulated():
641+
self._assert_valid_cookie(cookie, user_mgt_app)
642+
return
592643
withpytest.raises(auth.ExpiredSessionCookieError) asexcinfo:
593644
auth.verify_session_cookie(cookie, app=user_mgt_app)
594645
assertisinstance(excinfo.value, auth.InvalidSessionCookieError)
@@ -621,6 +672,10 @@ def test_custom_token(self, auth_app):
621672

622673
deftest_certificate_request_failure(self, user_mgt_app):
623674
_overwrite_cert_request(user_mgt_app, testutils.MockRequest(404, 'not found'))
675+
if_is_emulated():
676+
# Shouldn't fetch certificates in emulator mode.
677+
auth.verify_session_cookie(TEST_SESSION_COOKIE, app=user_mgt_app)
678+
return
624679
withpytest.raises(auth.CertificateFetchError) asexcinfo:
625680
auth.verify_session_cookie(TEST_SESSION_COOKIE, app=user_mgt_app)
626681
assert'Could not fetch certificates'instr(excinfo.value)
@@ -637,9 +692,11 @@ def test_certificate_caching(self, user_mgt_app, httpserver):
637692
verifier.cookie_verifier.cert_url=httpserver.url
638693
verifier.id_token_verifier.cert_url=httpserver.url
639694
verifier.verify_session_cookie(TEST_SESSION_COOKIE)
640-
assertlen(httpserver.requests) ==1
695+
# No requests should be made in emulated mode
696+
request_count=0if_is_emulated() else1
697+
assertlen(httpserver.requests) ==request_count
641698
# Subsequent requests should not fetch certs from the server
642699
verifier.verify_session_cookie(TEST_SESSION_COOKIE)
643-
assertlen(httpserver.requests) ==1
700+
assertlen(httpserver.requests) ==request_count
644701
verifier.verify_id_token(TEST_ID_TOKEN)
645-
assertlen(httpserver.requests) ==1
702+
assertlen(httpserver.requests) ==request_count

0 commit comments

Comments
 (0)
close