Skip to content

Commit 1e65c84

Browse files
committed
Accommodate 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). Fixtures were changed to function scope to avoid problems caused by overlap when some fixtures being in emulator mode and some in normal mode concurrently.
1 parent b70cd9f commit 1e65c84

File tree

3 files changed

+91
-32
lines changed

3 files changed

+91
-32
lines changed

firebase_admin/_auth_client.py

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

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

tests/test_token_gen.py

+88-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,24 @@ 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_accepted_in_emulator= [
429+
'NoKid',
430+
'WrongKid',
431+
'FutureToken',
432+
'ExpiredToken'
433+
]
434+
435+
def_assert_valid_token(self, id_token, app):
436+
claims=auth.verify_id_token(id_token, app=app)
413437
assertclaims['admin'] isTrue
414438
assertclaims['uid'] ==claims['sub']
415439
assertclaims['firebase']['sign_in_provider'] =='provider'
416440

441+
@pytest.mark.parametrize('id_token', valid_tokens.values(), ids=list(valid_tokens))
442+
deftest_valid_token(self, user_mgt_app, id_token):
443+
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
444+
self._assert_valid_token(id_token, app=user_mgt_app)
445+
417446
deftest_valid_token_with_tenant(self, user_mgt_app):
418447
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
419448
claims=auth.verify_id_token(TEST_ID_TOKEN_WITH_TENANT, app=user_mgt_app)
@@ -458,8 +487,12 @@ def test_invalid_arg(self, user_mgt_app, id_token):
458487
auth.verify_id_token(id_token, app=user_mgt_app)
459488
assert'Illegal ID token provided'instr(excinfo.value)
460489

461-
@pytest.mark.parametrize('id_token', invalid_tokens.values(), ids=list(invalid_tokens))
462-
deftest_invalid_token(self, user_mgt_app, id_token):
490+
@pytest.mark.parametrize('id_token_key', list(invalid_tokens))
491+
deftest_invalid_token(self, user_mgt_app, id_token_key):
492+
id_token=self.invalid_tokens[id_token_key]
493+
if_is_emulated() andid_token_keyinself.tokens_accepted_in_emulator:
494+
self._assert_valid_token(id_token, user_mgt_app)
495+
return
463496
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
464497
withpytest.raises(auth.InvalidIdTokenError) asexcinfo:
465498
auth.verify_id_token(id_token, app=user_mgt_app)
@@ -469,6 +502,9 @@ def test_invalid_token(self, user_mgt_app, id_token):
469502
deftest_expired_token(self, user_mgt_app):
470503
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
471504
id_token=self.invalid_tokens['ExpiredToken']
505+
if_is_emulated():
506+
self._assert_valid_token(id_token, user_mgt_app)
507+
return
472508
withpytest.raises(auth.ExpiredIdTokenError) asexcinfo:
473509
auth.verify_id_token(id_token, app=user_mgt_app)
474510
assertisinstance(excinfo.value, auth.InvalidIdTokenError)
@@ -506,6 +542,10 @@ def test_custom_token(self, auth_app):
506542

507543
deftest_certificate_request_failure(self, user_mgt_app):
508544
_overwrite_cert_request(user_mgt_app, testutils.MockRequest(404, 'not found'))
545+
if_is_emulated():
546+
# Shouldn't fetch certificates in emulator mode.
547+
self._assert_valid_token(TEST_ID_TOKEN, app=user_mgt_app)
548+
return
509549
withpytest.raises(auth.CertificateFetchError) asexcinfo:
510550
auth.verify_id_token(TEST_ID_TOKEN, app=user_mgt_app)
511551
assert'Could not fetch certificates'instr(excinfo.value)
@@ -540,20 +580,28 @@ class TestVerifySessionCookie:
540580
'IDToken': TEST_ID_TOKEN,
541581
}
542582

583+
cookies_accepted_in_emulator= [
584+
'NoKid',
585+
'WrongKid',
586+
'FutureCookie',
587+
'ExpiredCookie'
588+
]
589+
590+
def_assert_valid_cookie(self, cookie, app, check_revoked=False):
591+
claims=auth.verify_session_cookie(cookie, app=app, check_revoked=check_revoked)
592+
assertclaims['admin'] isTrue
593+
assertclaims['uid'] ==claims['sub']
594+
543595
@pytest.mark.parametrize('cookie', valid_cookies.values(), ids=list(valid_cookies))
544596
deftest_valid_cookie(self, user_mgt_app, cookie):
545597
_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']
598+
self._assert_valid_cookie(cookie, user_mgt_app)
549599

550600
@pytest.mark.parametrize('cookie', valid_cookies.values(), ids=list(valid_cookies))
551601
deftest_valid_cookie_check_revoked(self, user_mgt_app, cookie):
552602
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
553603
_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']
604+
self._assert_valid_cookie(cookie, app=user_mgt_app, check_revoked=True)
557605

558606
@pytest.mark.parametrize('cookie', valid_cookies.values(), ids=list(valid_cookies))
559607
deftest_revoked_cookie_check_revoked(self, user_mgt_app, revoked_tokens, cookie):
@@ -567,9 +615,7 @@ def test_revoked_cookie_check_revoked(self, user_mgt_app, revoked_tokens, cookie
567615
deftest_revoked_cookie_does_not_check_revoked(self, user_mgt_app, revoked_tokens, cookie):
568616
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
569617
_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']
618+
self._assert_valid_cookie(cookie, app=user_mgt_app, check_revoked=False)
573619

574620
@pytest.mark.parametrize('cookie', INVALID_JWT_ARGS.values(), ids=list(INVALID_JWT_ARGS))
575621
deftest_invalid_args(self, user_mgt_app, cookie):
@@ -578,8 +624,12 @@ def test_invalid_args(self, user_mgt_app, cookie):
578624
auth.verify_session_cookie(cookie, app=user_mgt_app)
579625
assert'Illegal session cookie provided'instr(excinfo.value)
580626

581-
@pytest.mark.parametrize('cookie', invalid_cookies.values(), ids=list(invalid_cookies))
582-
deftest_invalid_cookie(self, user_mgt_app, cookie):
627+
@pytest.mark.parametrize('cookie_key', list(invalid_cookies))
628+
deftest_invalid_cookie(self, user_mgt_app, cookie_key):
629+
cookie=self.invalid_cookies[cookie_key]
630+
if_is_emulated() andcookie_keyinself.cookies_accepted_in_emulator:
631+
self._assert_valid_cookie(cookie, user_mgt_app)
632+
return
583633
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
584634
withpytest.raises(auth.InvalidSessionCookieError) asexcinfo:
585635
auth.verify_session_cookie(cookie, app=user_mgt_app)
@@ -589,6 +639,9 @@ def test_invalid_cookie(self, user_mgt_app, cookie):
589639
deftest_expired_cookie(self, user_mgt_app):
590640
_overwrite_cert_request(user_mgt_app, MOCK_REQUEST)
591641
cookie=self.invalid_cookies['ExpiredCookie']
642+
if_is_emulated():
643+
self._assert_valid_cookie(cookie, user_mgt_app)
644+
return
592645
withpytest.raises(auth.ExpiredSessionCookieError) asexcinfo:
593646
auth.verify_session_cookie(cookie, app=user_mgt_app)
594647
assertisinstance(excinfo.value, auth.InvalidSessionCookieError)
@@ -621,6 +674,10 @@ def test_custom_token(self, auth_app):
621674

622675
deftest_certificate_request_failure(self, user_mgt_app):
623676
_overwrite_cert_request(user_mgt_app, testutils.MockRequest(404, 'not found'))
677+
if_is_emulated():
678+
# Shouldn't fetch certificates in emulator mode.
679+
auth.verify_session_cookie(TEST_SESSION_COOKIE, app=user_mgt_app)
680+
return
624681
withpytest.raises(auth.CertificateFetchError) asexcinfo:
625682
auth.verify_session_cookie(TEST_SESSION_COOKIE, app=user_mgt_app)
626683
assert'Could not fetch certificates'instr(excinfo.value)
@@ -637,9 +694,11 @@ def test_certificate_caching(self, user_mgt_app, httpserver):
637694
verifier.cookie_verifier.cert_url=httpserver.url
638695
verifier.id_token_verifier.cert_url=httpserver.url
639696
verifier.verify_session_cookie(TEST_SESSION_COOKIE)
640-
assertlen(httpserver.requests) ==1
697+
# No requests should be made in emulated mode
698+
request_count=0if_is_emulated() else1
699+
assertlen(httpserver.requests) ==request_count
641700
# Subsequent requests should not fetch certs from the server
642701
verifier.verify_session_cookie(TEST_SESSION_COOKIE)
643-
assertlen(httpserver.requests) ==1
702+
assertlen(httpserver.requests) ==request_count
644703
verifier.verify_id_token(TEST_ID_TOKEN)
645-
assertlen(httpserver.requests) ==1
704+
assertlen(httpserver.requests) ==request_count

tests/test_user_mgt.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
'PREFIX': ID_TOOLKIT_URL+URL_PROJECT_SUFFIX,
6363
}
6464

65-
@pytest.fixture(scope='module', params=[{'emulated': False}, {'emulated': True}])
65+
@pytest.fixture(params=[{'emulated': False}, {'emulated': True}])
6666
defuser_mgt_app(request):
6767
monkeypatch=testutils.new_monkeypatch()
6868
ifrequest.param['emulated']:
@@ -75,7 +75,7 @@ def user_mgt_app(request):
7575
firebase_admin.delete_app(app)
7676
monkeypatch.undo()
7777

78-
@pytest.fixture(scope='module')
78+
@pytest.fixture
7979
defuser_mgt_app_with_timeout():
8080
app=firebase_admin.initialize_app(
8181
testutils.MockCredential(),

0 commit comments

Comments
 (0)
close