test_jwt.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. # Copyright 2014 Google Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import base64
  15. import datetime
  16. import json
  17. import os
  18. import mock
  19. import pytest
  20. from google.auth import _helpers
  21. from google.auth import crypt
  22. from google.auth import exceptions
  23. from google.auth import jwt
  24. import yatest.common
  25. DATA_DIR = os.path.join(yatest.common.test_source_path(), "data")
  26. with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh:
  27. PRIVATE_KEY_BYTES = fh.read()
  28. with open(os.path.join(DATA_DIR, "public_cert.pem"), "rb") as fh:
  29. PUBLIC_CERT_BYTES = fh.read()
  30. with open(os.path.join(DATA_DIR, "other_cert.pem"), "rb") as fh:
  31. OTHER_CERT_BYTES = fh.read()
  32. with open(os.path.join(DATA_DIR, "es256_privatekey.pem"), "rb") as fh:
  33. EC_PRIVATE_KEY_BYTES = fh.read()
  34. with open(os.path.join(DATA_DIR, "es256_public_cert.pem"), "rb") as fh:
  35. EC_PUBLIC_CERT_BYTES = fh.read()
  36. SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, "service_account.json")
  37. with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh:
  38. SERVICE_ACCOUNT_INFO = json.load(fh)
  39. @pytest.fixture
  40. def signer():
  41. return crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, "1")
  42. def test_encode_basic(signer):
  43. test_payload = {"test": "value"}
  44. encoded = jwt.encode(signer, test_payload)
  45. header, payload, _, _ = jwt._unverified_decode(encoded)
  46. assert payload == test_payload
  47. assert header == {"typ": "JWT", "alg": "RS256", "kid": signer.key_id}
  48. def test_encode_extra_headers(signer):
  49. encoded = jwt.encode(signer, {}, header={"extra": "value"})
  50. header = jwt.decode_header(encoded)
  51. assert header == {
  52. "typ": "JWT",
  53. "alg": "RS256",
  54. "kid": signer.key_id,
  55. "extra": "value",
  56. }
  57. def test_encode_custom_alg_in_headers(signer):
  58. encoded = jwt.encode(signer, {}, header={"alg": "foo"})
  59. header = jwt.decode_header(encoded)
  60. assert header == {"typ": "JWT", "alg": "foo", "kid": signer.key_id}
  61. @pytest.fixture
  62. def es256_signer():
  63. return crypt.ES256Signer.from_string(EC_PRIVATE_KEY_BYTES, "1")
  64. def test_encode_basic_es256(es256_signer):
  65. test_payload = {"test": "value"}
  66. encoded = jwt.encode(es256_signer, test_payload)
  67. header, payload, _, _ = jwt._unverified_decode(encoded)
  68. assert payload == test_payload
  69. assert header == {"typ": "JWT", "alg": "ES256", "kid": es256_signer.key_id}
  70. @pytest.fixture
  71. def token_factory(signer, es256_signer):
  72. def factory(claims=None, key_id=None, use_es256_signer=False):
  73. now = _helpers.datetime_to_secs(_helpers.utcnow())
  74. payload = {
  75. "aud": "audience@example.com",
  76. "iat": now,
  77. "exp": now + 300,
  78. "user": "billy bob",
  79. "metadata": {"meta": "data"},
  80. }
  81. payload.update(claims or {})
  82. # False is specified to remove the signer's key id for testing
  83. # headers without key ids.
  84. if key_id is False:
  85. signer._key_id = None
  86. key_id = None
  87. if use_es256_signer:
  88. return jwt.encode(es256_signer, payload, key_id=key_id)
  89. else:
  90. return jwt.encode(signer, payload, key_id=key_id)
  91. return factory
  92. def test_decode_valid(token_factory):
  93. payload = jwt.decode(token_factory(), certs=PUBLIC_CERT_BYTES)
  94. assert payload["aud"] == "audience@example.com"
  95. assert payload["user"] == "billy bob"
  96. assert payload["metadata"]["meta"] == "data"
  97. def test_decode_valid_es256(token_factory):
  98. payload = jwt.decode(
  99. token_factory(use_es256_signer=True), certs=EC_PUBLIC_CERT_BYTES
  100. )
  101. assert payload["aud"] == "audience@example.com"
  102. assert payload["user"] == "billy bob"
  103. assert payload["metadata"]["meta"] == "data"
  104. def test_decode_valid_with_audience(token_factory):
  105. payload = jwt.decode(
  106. token_factory(), certs=PUBLIC_CERT_BYTES, audience="audience@example.com"
  107. )
  108. assert payload["aud"] == "audience@example.com"
  109. assert payload["user"] == "billy bob"
  110. assert payload["metadata"]["meta"] == "data"
  111. def test_decode_valid_with_audience_list(token_factory):
  112. payload = jwt.decode(
  113. token_factory(),
  114. certs=PUBLIC_CERT_BYTES,
  115. audience=["audience@example.com", "another_audience@example.com"],
  116. )
  117. assert payload["aud"] == "audience@example.com"
  118. assert payload["user"] == "billy bob"
  119. assert payload["metadata"]["meta"] == "data"
  120. def test_decode_valid_unverified(token_factory):
  121. payload = jwt.decode(token_factory(), certs=OTHER_CERT_BYTES, verify=False)
  122. assert payload["aud"] == "audience@example.com"
  123. assert payload["user"] == "billy bob"
  124. assert payload["metadata"]["meta"] == "data"
  125. def test_decode_bad_token_wrong_number_of_segments():
  126. with pytest.raises(ValueError) as excinfo:
  127. jwt.decode("1.2", PUBLIC_CERT_BYTES)
  128. assert excinfo.match(r"Wrong number of segments")
  129. def test_decode_bad_token_not_base64():
  130. with pytest.raises((ValueError, TypeError)) as excinfo:
  131. jwt.decode("1.2.3", PUBLIC_CERT_BYTES)
  132. assert excinfo.match(r"Incorrect padding|more than a multiple of 4")
  133. def test_decode_bad_token_not_json():
  134. token = b".".join([base64.urlsafe_b64encode(b"123!")] * 3)
  135. with pytest.raises(ValueError) as excinfo:
  136. jwt.decode(token, PUBLIC_CERT_BYTES)
  137. assert excinfo.match(r"Can\'t parse segment")
  138. def test_decode_bad_token_no_iat_or_exp(signer):
  139. token = jwt.encode(signer, {"test": "value"})
  140. with pytest.raises(ValueError) as excinfo:
  141. jwt.decode(token, PUBLIC_CERT_BYTES)
  142. assert excinfo.match(r"Token does not contain required claim")
  143. def test_decode_bad_token_too_early(token_factory):
  144. token = token_factory(
  145. claims={
  146. "iat": _helpers.datetime_to_secs(
  147. _helpers.utcnow() + datetime.timedelta(hours=1)
  148. )
  149. }
  150. )
  151. with pytest.raises(ValueError) as excinfo:
  152. jwt.decode(token, PUBLIC_CERT_BYTES)
  153. assert excinfo.match(r"Token used too early")
  154. def test_decode_bad_token_expired(token_factory):
  155. token = token_factory(
  156. claims={
  157. "exp": _helpers.datetime_to_secs(
  158. _helpers.utcnow() - datetime.timedelta(hours=1)
  159. )
  160. }
  161. )
  162. with pytest.raises(ValueError) as excinfo:
  163. jwt.decode(token, PUBLIC_CERT_BYTES)
  164. assert excinfo.match(r"Token expired")
  165. def test_decode_bad_token_wrong_audience(token_factory):
  166. token = token_factory()
  167. audience = "audience2@example.com"
  168. with pytest.raises(ValueError) as excinfo:
  169. jwt.decode(token, PUBLIC_CERT_BYTES, audience=audience)
  170. assert excinfo.match(r"Token has wrong audience")
  171. def test_decode_bad_token_wrong_audience_list(token_factory):
  172. token = token_factory()
  173. audience = ["audience2@example.com", "audience3@example.com"]
  174. with pytest.raises(ValueError) as excinfo:
  175. jwt.decode(token, PUBLIC_CERT_BYTES, audience=audience)
  176. assert excinfo.match(r"Token has wrong audience")
  177. def test_decode_wrong_cert(token_factory):
  178. with pytest.raises(ValueError) as excinfo:
  179. jwt.decode(token_factory(), OTHER_CERT_BYTES)
  180. assert excinfo.match(r"Could not verify token signature")
  181. def test_decode_multicert_bad_cert(token_factory):
  182. certs = {"1": OTHER_CERT_BYTES, "2": PUBLIC_CERT_BYTES}
  183. with pytest.raises(ValueError) as excinfo:
  184. jwt.decode(token_factory(), certs)
  185. assert excinfo.match(r"Could not verify token signature")
  186. def test_decode_no_cert(token_factory):
  187. certs = {"2": PUBLIC_CERT_BYTES}
  188. with pytest.raises(ValueError) as excinfo:
  189. jwt.decode(token_factory(), certs)
  190. assert excinfo.match(r"Certificate for key id 1 not found")
  191. def test_decode_no_key_id(token_factory):
  192. token = token_factory(key_id=False)
  193. certs = {"2": PUBLIC_CERT_BYTES}
  194. payload = jwt.decode(token, certs)
  195. assert payload["user"] == "billy bob"
  196. def test_decode_unknown_alg():
  197. headers = json.dumps({u"kid": u"1", u"alg": u"fakealg"})
  198. token = b".".join(
  199. map(lambda seg: base64.b64encode(seg.encode("utf-8")), [headers, u"{}", u"sig"])
  200. )
  201. with pytest.raises(ValueError) as excinfo:
  202. jwt.decode(token)
  203. assert excinfo.match(r"fakealg")
  204. def test_decode_missing_crytography_alg(monkeypatch):
  205. monkeypatch.delitem(jwt._ALGORITHM_TO_VERIFIER_CLASS, "ES256")
  206. headers = json.dumps({u"kid": u"1", u"alg": u"ES256"})
  207. token = b".".join(
  208. map(lambda seg: base64.b64encode(seg.encode("utf-8")), [headers, u"{}", u"sig"])
  209. )
  210. with pytest.raises(ValueError) as excinfo:
  211. jwt.decode(token)
  212. assert excinfo.match(r"cryptography")
  213. def test_roundtrip_explicit_key_id(token_factory):
  214. token = token_factory(key_id="3")
  215. certs = {"2": OTHER_CERT_BYTES, "3": PUBLIC_CERT_BYTES}
  216. payload = jwt.decode(token, certs)
  217. assert payload["user"] == "billy bob"
  218. class TestCredentials(object):
  219. SERVICE_ACCOUNT_EMAIL = "service-account@example.com"
  220. SUBJECT = "subject"
  221. AUDIENCE = "audience"
  222. ADDITIONAL_CLAIMS = {"meta": "data"}
  223. credentials = None
  224. @pytest.fixture(autouse=True)
  225. def credentials_fixture(self, signer):
  226. self.credentials = jwt.Credentials(
  227. signer,
  228. self.SERVICE_ACCOUNT_EMAIL,
  229. self.SERVICE_ACCOUNT_EMAIL,
  230. self.AUDIENCE,
  231. )
  232. def test_from_service_account_info(self):
  233. with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh:
  234. info = json.load(fh)
  235. credentials = jwt.Credentials.from_service_account_info(
  236. info, audience=self.AUDIENCE
  237. )
  238. assert credentials._signer.key_id == info["private_key_id"]
  239. assert credentials._issuer == info["client_email"]
  240. assert credentials._subject == info["client_email"]
  241. assert credentials._audience == self.AUDIENCE
  242. def test_from_service_account_info_args(self):
  243. info = SERVICE_ACCOUNT_INFO.copy()
  244. credentials = jwt.Credentials.from_service_account_info(
  245. info,
  246. subject=self.SUBJECT,
  247. audience=self.AUDIENCE,
  248. additional_claims=self.ADDITIONAL_CLAIMS,
  249. )
  250. assert credentials._signer.key_id == info["private_key_id"]
  251. assert credentials._issuer == info["client_email"]
  252. assert credentials._subject == self.SUBJECT
  253. assert credentials._audience == self.AUDIENCE
  254. assert credentials._additional_claims == self.ADDITIONAL_CLAIMS
  255. def test_from_service_account_file(self):
  256. info = SERVICE_ACCOUNT_INFO.copy()
  257. credentials = jwt.Credentials.from_service_account_file(
  258. SERVICE_ACCOUNT_JSON_FILE, audience=self.AUDIENCE
  259. )
  260. assert credentials._signer.key_id == info["private_key_id"]
  261. assert credentials._issuer == info["client_email"]
  262. assert credentials._subject == info["client_email"]
  263. assert credentials._audience == self.AUDIENCE
  264. def test_from_service_account_file_args(self):
  265. info = SERVICE_ACCOUNT_INFO.copy()
  266. credentials = jwt.Credentials.from_service_account_file(
  267. SERVICE_ACCOUNT_JSON_FILE,
  268. subject=self.SUBJECT,
  269. audience=self.AUDIENCE,
  270. additional_claims=self.ADDITIONAL_CLAIMS,
  271. )
  272. assert credentials._signer.key_id == info["private_key_id"]
  273. assert credentials._issuer == info["client_email"]
  274. assert credentials._subject == self.SUBJECT
  275. assert credentials._audience == self.AUDIENCE
  276. assert credentials._additional_claims == self.ADDITIONAL_CLAIMS
  277. def test_from_signing_credentials(self):
  278. jwt_from_signing = self.credentials.from_signing_credentials(
  279. self.credentials, audience=mock.sentinel.new_audience
  280. )
  281. jwt_from_info = jwt.Credentials.from_service_account_info(
  282. SERVICE_ACCOUNT_INFO, audience=mock.sentinel.new_audience
  283. )
  284. assert isinstance(jwt_from_signing, jwt.Credentials)
  285. assert jwt_from_signing._signer.key_id == jwt_from_info._signer.key_id
  286. assert jwt_from_signing._issuer == jwt_from_info._issuer
  287. assert jwt_from_signing._subject == jwt_from_info._subject
  288. assert jwt_from_signing._audience == jwt_from_info._audience
  289. def test_default_state(self):
  290. assert not self.credentials.valid
  291. # Expiration hasn't been set yet
  292. assert not self.credentials.expired
  293. def test_with_claims(self):
  294. new_audience = "new_audience"
  295. new_credentials = self.credentials.with_claims(audience=new_audience)
  296. assert new_credentials._signer == self.credentials._signer
  297. assert new_credentials._issuer == self.credentials._issuer
  298. assert new_credentials._subject == self.credentials._subject
  299. assert new_credentials._audience == new_audience
  300. assert new_credentials._additional_claims == self.credentials._additional_claims
  301. assert new_credentials._quota_project_id == self.credentials._quota_project_id
  302. def test_with_quota_project(self):
  303. quota_project_id = "project-foo"
  304. new_credentials = self.credentials.with_quota_project(quota_project_id)
  305. assert new_credentials._signer == self.credentials._signer
  306. assert new_credentials._issuer == self.credentials._issuer
  307. assert new_credentials._subject == self.credentials._subject
  308. assert new_credentials._audience == self.credentials._audience
  309. assert new_credentials._additional_claims == self.credentials._additional_claims
  310. assert new_credentials._quota_project_id == quota_project_id
  311. def test_sign_bytes(self):
  312. to_sign = b"123"
  313. signature = self.credentials.sign_bytes(to_sign)
  314. assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES)
  315. def test_signer(self):
  316. assert isinstance(self.credentials.signer, crypt.RSASigner)
  317. def test_signer_email(self):
  318. assert self.credentials.signer_email == SERVICE_ACCOUNT_INFO["client_email"]
  319. def _verify_token(self, token):
  320. payload = jwt.decode(token, PUBLIC_CERT_BYTES)
  321. assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL
  322. return payload
  323. def test_refresh(self):
  324. self.credentials.refresh(None)
  325. assert self.credentials.valid
  326. assert not self.credentials.expired
  327. def test_expired(self):
  328. assert not self.credentials.expired
  329. self.credentials.refresh(None)
  330. assert not self.credentials.expired
  331. with mock.patch("google.auth._helpers.utcnow") as now:
  332. one_day = datetime.timedelta(days=1)
  333. now.return_value = self.credentials.expiry + one_day
  334. assert self.credentials.expired
  335. def test_before_request(self):
  336. headers = {}
  337. self.credentials.refresh(None)
  338. self.credentials.before_request(
  339. None, "GET", "http://example.com?a=1#3", headers
  340. )
  341. header_value = headers["authorization"]
  342. _, token = header_value.split(" ")
  343. # Since the audience is set, it should use the existing token.
  344. assert token.encode("utf-8") == self.credentials.token
  345. payload = self._verify_token(token)
  346. assert payload["aud"] == self.AUDIENCE
  347. def test_before_request_refreshes(self):
  348. assert not self.credentials.valid
  349. self.credentials.before_request(None, "GET", "http://example.com?a=1#3", {})
  350. assert self.credentials.valid
  351. class TestOnDemandCredentials(object):
  352. SERVICE_ACCOUNT_EMAIL = "service-account@example.com"
  353. SUBJECT = "subject"
  354. ADDITIONAL_CLAIMS = {"meta": "data"}
  355. credentials = None
  356. @pytest.fixture(autouse=True)
  357. def credentials_fixture(self, signer):
  358. self.credentials = jwt.OnDemandCredentials(
  359. signer,
  360. self.SERVICE_ACCOUNT_EMAIL,
  361. self.SERVICE_ACCOUNT_EMAIL,
  362. max_cache_size=2,
  363. )
  364. def test_from_service_account_info(self):
  365. with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh:
  366. info = json.load(fh)
  367. credentials = jwt.OnDemandCredentials.from_service_account_info(info)
  368. assert credentials._signer.key_id == info["private_key_id"]
  369. assert credentials._issuer == info["client_email"]
  370. assert credentials._subject == info["client_email"]
  371. def test_from_service_account_info_args(self):
  372. info = SERVICE_ACCOUNT_INFO.copy()
  373. credentials = jwt.OnDemandCredentials.from_service_account_info(
  374. info, subject=self.SUBJECT, additional_claims=self.ADDITIONAL_CLAIMS
  375. )
  376. assert credentials._signer.key_id == info["private_key_id"]
  377. assert credentials._issuer == info["client_email"]
  378. assert credentials._subject == self.SUBJECT
  379. assert credentials._additional_claims == self.ADDITIONAL_CLAIMS
  380. def test_from_service_account_file(self):
  381. info = SERVICE_ACCOUNT_INFO.copy()
  382. credentials = jwt.OnDemandCredentials.from_service_account_file(
  383. SERVICE_ACCOUNT_JSON_FILE
  384. )
  385. assert credentials._signer.key_id == info["private_key_id"]
  386. assert credentials._issuer == info["client_email"]
  387. assert credentials._subject == info["client_email"]
  388. def test_from_service_account_file_args(self):
  389. info = SERVICE_ACCOUNT_INFO.copy()
  390. credentials = jwt.OnDemandCredentials.from_service_account_file(
  391. SERVICE_ACCOUNT_JSON_FILE,
  392. subject=self.SUBJECT,
  393. additional_claims=self.ADDITIONAL_CLAIMS,
  394. )
  395. assert credentials._signer.key_id == info["private_key_id"]
  396. assert credentials._issuer == info["client_email"]
  397. assert credentials._subject == self.SUBJECT
  398. assert credentials._additional_claims == self.ADDITIONAL_CLAIMS
  399. def test_from_signing_credentials(self):
  400. jwt_from_signing = self.credentials.from_signing_credentials(self.credentials)
  401. jwt_from_info = jwt.OnDemandCredentials.from_service_account_info(
  402. SERVICE_ACCOUNT_INFO
  403. )
  404. assert isinstance(jwt_from_signing, jwt.OnDemandCredentials)
  405. assert jwt_from_signing._signer.key_id == jwt_from_info._signer.key_id
  406. assert jwt_from_signing._issuer == jwt_from_info._issuer
  407. assert jwt_from_signing._subject == jwt_from_info._subject
  408. def test_default_state(self):
  409. # Credentials are *always* valid.
  410. assert self.credentials.valid
  411. # Credentials *never* expire.
  412. assert not self.credentials.expired
  413. def test_with_claims(self):
  414. new_claims = {"meep": "moop"}
  415. new_credentials = self.credentials.with_claims(additional_claims=new_claims)
  416. assert new_credentials._signer == self.credentials._signer
  417. assert new_credentials._issuer == self.credentials._issuer
  418. assert new_credentials._subject == self.credentials._subject
  419. assert new_credentials._additional_claims == new_claims
  420. def test_with_quota_project(self):
  421. quota_project_id = "project-foo"
  422. new_credentials = self.credentials.with_quota_project(quota_project_id)
  423. assert new_credentials._signer == self.credentials._signer
  424. assert new_credentials._issuer == self.credentials._issuer
  425. assert new_credentials._subject == self.credentials._subject
  426. assert new_credentials._additional_claims == self.credentials._additional_claims
  427. assert new_credentials._quota_project_id == quota_project_id
  428. def test_sign_bytes(self):
  429. to_sign = b"123"
  430. signature = self.credentials.sign_bytes(to_sign)
  431. assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES)
  432. def test_signer(self):
  433. assert isinstance(self.credentials.signer, crypt.RSASigner)
  434. def test_signer_email(self):
  435. assert self.credentials.signer_email == SERVICE_ACCOUNT_INFO["client_email"]
  436. def _verify_token(self, token):
  437. payload = jwt.decode(token, PUBLIC_CERT_BYTES)
  438. assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL
  439. return payload
  440. def test_refresh(self):
  441. with pytest.raises(exceptions.RefreshError):
  442. self.credentials.refresh(None)
  443. def test_before_request(self):
  444. headers = {}
  445. self.credentials.before_request(
  446. None, "GET", "http://example.com?a=1#3", headers
  447. )
  448. _, token = headers["authorization"].split(" ")
  449. payload = self._verify_token(token)
  450. assert payload["aud"] == "http://example.com"
  451. # Making another request should re-use the same token.
  452. self.credentials.before_request(None, "GET", "http://example.com?b=2", headers)
  453. _, new_token = headers["authorization"].split(" ")
  454. assert new_token == token
  455. def test_expired_token(self):
  456. self.credentials._cache["audience"] = (
  457. mock.sentinel.token,
  458. datetime.datetime.min,
  459. )
  460. token = self.credentials._get_jwt_for_audience("audience")
  461. assert token != mock.sentinel.token