blob: ba7277cdcb6e97f923dff06db578fee3491c45a8 [file] [log] [blame]
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -07001# 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
15import base64
16import datetime
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -070017import json
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070018import os
19
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -070020import mock
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070021import pytest
22
23from google.auth import _helpers
24from google.auth import crypt
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -070025from google.auth import exceptions
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070026from google.auth import jwt
27
28
Bu Sun Kim9eec0912019-10-21 17:04:21 -070029DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070030
Bu Sun Kim9eec0912019-10-21 17:04:21 -070031with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh:
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070032 PRIVATE_KEY_BYTES = fh.read()
33
Bu Sun Kim9eec0912019-10-21 17:04:21 -070034with open(os.path.join(DATA_DIR, "public_cert.pem"), "rb") as fh:
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070035 PUBLIC_CERT_BYTES = fh.read()
36
Bu Sun Kim9eec0912019-10-21 17:04:21 -070037with open(os.path.join(DATA_DIR, "other_cert.pem"), "rb") as fh:
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070038 OTHER_CERT_BYTES = fh.read()
39
Thea Flowerse290a3d2020-04-01 10:11:42 -070040with open(os.path.join(DATA_DIR, "es256_privatekey.pem"), "rb") as fh:
41 EC_PRIVATE_KEY_BYTES = fh.read()
42
43with open(os.path.join(DATA_DIR, "es256_public_cert.pem"), "rb") as fh:
44 EC_PUBLIC_CERT_BYTES = fh.read()
45
Bu Sun Kim9eec0912019-10-21 17:04:21 -070046SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, "service_account.json")
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -070047
Bu Sun Kim9eec0912019-10-21 17:04:21 -070048with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh:
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -070049 SERVICE_ACCOUNT_INFO = json.load(fh)
50
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070051
52@pytest.fixture
53def signer():
Bu Sun Kim9eec0912019-10-21 17:04:21 -070054 return crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, "1")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070055
56
57def test_encode_basic(signer):
Bu Sun Kim9eec0912019-10-21 17:04:21 -070058 test_payload = {"test": "value"}
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070059 encoded = jwt.encode(signer, test_payload)
60 header, payload, _, _ = jwt._unverified_decode(encoded)
61 assert payload == test_payload
Bu Sun Kim9eec0912019-10-21 17:04:21 -070062 assert header == {"typ": "JWT", "alg": "RS256", "kid": signer.key_id}
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070063
64
65def test_encode_extra_headers(signer):
Bu Sun Kim9eec0912019-10-21 17:04:21 -070066 encoded = jwt.encode(signer, {}, header={"extra": "value"})
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070067 header = jwt.decode_header(encoded)
68 assert header == {
Bu Sun Kim9eec0912019-10-21 17:04:21 -070069 "typ": "JWT",
70 "alg": "RS256",
71 "kid": signer.key_id,
72 "extra": "value",
73 }
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070074
75
arithmetic17280a837062021-04-08 10:58:38 -070076def test_encode_custom_alg_in_headers(signer):
77 encoded = jwt.encode(signer, {}, header={"alg": "foo"})
78 header = jwt.decode_header(encoded)
79 assert header == {"typ": "JWT", "alg": "foo", "kid": signer.key_id}
80
81
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070082@pytest.fixture
Thea Flowerse290a3d2020-04-01 10:11:42 -070083def es256_signer():
84 return crypt.ES256Signer.from_string(EC_PRIVATE_KEY_BYTES, "1")
85
86
87def test_encode_basic_es256(es256_signer):
88 test_payload = {"test": "value"}
89 encoded = jwt.encode(es256_signer, test_payload)
90 header, payload, _, _ = jwt._unverified_decode(encoded)
91 assert payload == test_payload
92 assert header == {"typ": "JWT", "alg": "ES256", "kid": es256_signer.key_id}
93
94
95@pytest.fixture
96def token_factory(signer, es256_signer):
97 def factory(claims=None, key_id=None, use_es256_signer=False):
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070098 now = _helpers.datetime_to_secs(_helpers.utcnow())
99 payload = {
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700100 "aud": "audience@example.com",
101 "iat": now,
102 "exp": now + 300,
103 "user": "billy bob",
104 "metadata": {"meta": "data"},
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700105 }
106 payload.update(claims or {})
107
108 # False is specified to remove the signer's key id for testing
109 # headers without key ids.
110 if key_id is False:
Jon Wayne Parrott254befe2017-02-22 14:37:31 -0800111 signer._key_id = None
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700112 key_id = None
113
Thea Flowerse290a3d2020-04-01 10:11:42 -0700114 if use_es256_signer:
115 return jwt.encode(es256_signer, payload, key_id=key_id)
116 else:
117 return jwt.encode(signer, payload, key_id=key_id)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700118
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700119 return factory
120
121
122def test_decode_valid(token_factory):
123 payload = jwt.decode(token_factory(), certs=PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700124 assert payload["aud"] == "audience@example.com"
125 assert payload["user"] == "billy bob"
126 assert payload["metadata"]["meta"] == "data"
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700127
128
Thea Flowerse290a3d2020-04-01 10:11:42 -0700129def test_decode_valid_es256(token_factory):
130 payload = jwt.decode(
131 token_factory(use_es256_signer=True), certs=EC_PUBLIC_CERT_BYTES
132 )
133 assert payload["aud"] == "audience@example.com"
134 assert payload["user"] == "billy bob"
135 assert payload["metadata"]["meta"] == "data"
136
137
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700138def test_decode_valid_with_audience(token_factory):
139 payload = jwt.decode(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700140 token_factory(), certs=PUBLIC_CERT_BYTES, audience="audience@example.com"
141 )
142 assert payload["aud"] == "audience@example.com"
143 assert payload["user"] == "billy bob"
144 assert payload["metadata"]["meta"] == "data"
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700145
146
Jonathan Beaulieu56c39462021-04-15 04:28:04 -0400147def test_decode_valid_with_audience_list(token_factory):
148 payload = jwt.decode(
149 token_factory(),
150 certs=PUBLIC_CERT_BYTES,
151 audience=["audience@example.com", "another_audience@example.com"],
152 )
153 assert payload["aud"] == "audience@example.com"
154 assert payload["user"] == "billy bob"
155 assert payload["metadata"]["meta"] == "data"
156
157
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700158def test_decode_valid_unverified(token_factory):
159 payload = jwt.decode(token_factory(), certs=OTHER_CERT_BYTES, verify=False)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700160 assert payload["aud"] == "audience@example.com"
161 assert payload["user"] == "billy bob"
162 assert payload["metadata"]["meta"] == "data"
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700163
164
165def test_decode_bad_token_wrong_number_of_segments():
166 with pytest.raises(ValueError) as excinfo:
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700167 jwt.decode("1.2", PUBLIC_CERT_BYTES)
168 assert excinfo.match(r"Wrong number of segments")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700169
170
171def test_decode_bad_token_not_base64():
172 with pytest.raises((ValueError, TypeError)) as excinfo:
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700173 jwt.decode("1.2.3", PUBLIC_CERT_BYTES)
174 assert excinfo.match(r"Incorrect padding|more than a multiple of 4")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700175
176
177def test_decode_bad_token_not_json():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700178 token = b".".join([base64.urlsafe_b64encode(b"123!")] * 3)
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700179 with pytest.raises(ValueError) as excinfo:
180 jwt.decode(token, PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700181 assert excinfo.match(r"Can\'t parse segment")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700182
183
184def test_decode_bad_token_no_iat_or_exp(signer):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700185 token = jwt.encode(signer, {"test": "value"})
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700186 with pytest.raises(ValueError) as excinfo:
187 jwt.decode(token, PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700188 assert excinfo.match(r"Token does not contain required claim")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700189
190
191def test_decode_bad_token_too_early(token_factory):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700192 token = token_factory(
193 claims={
194 "iat": _helpers.datetime_to_secs(
195 _helpers.utcnow() + datetime.timedelta(hours=1)
196 )
197 }
198 )
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700199 with pytest.raises(ValueError) as excinfo:
arithmetic1728738611b2021-09-09 17:09:54 -0700200 jwt.decode(token, PUBLIC_CERT_BYTES, clock_skew_in_seconds=59)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700201 assert excinfo.match(r"Token used too early")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700202
203
204def test_decode_bad_token_expired(token_factory):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700205 token = token_factory(
206 claims={
207 "exp": _helpers.datetime_to_secs(
208 _helpers.utcnow() - datetime.timedelta(hours=1)
209 )
210 }
211 )
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700212 with pytest.raises(ValueError) as excinfo:
arithmetic1728738611b2021-09-09 17:09:54 -0700213 jwt.decode(token, PUBLIC_CERT_BYTES, clock_skew_in_seconds=59)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700214 assert excinfo.match(r"Token expired")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700215
216
arithmetic1728738611b2021-09-09 17:09:54 -0700217def test_decode_success_with_no_clock_skew(token_factory):
218 token = token_factory(
219 claims={
220 "exp": _helpers.datetime_to_secs(
221 _helpers.utcnow() + datetime.timedelta(seconds=1)
222 ),
223 "iat": _helpers.datetime_to_secs(
224 _helpers.utcnow() - datetime.timedelta(seconds=1)
225 ),
226 }
227 )
228
229 jwt.decode(token, PUBLIC_CERT_BYTES)
230
231
232def test_decode_success_with_custom_clock_skew(token_factory):
233 token = token_factory(
234 claims={
235 "exp": _helpers.datetime_to_secs(
236 _helpers.utcnow() + datetime.timedelta(seconds=2)
237 ),
238 "iat": _helpers.datetime_to_secs(
239 _helpers.utcnow() - datetime.timedelta(seconds=2)
240 ),
241 }
242 )
243
244 jwt.decode(token, PUBLIC_CERT_BYTES, clock_skew_in_seconds=1)
245
246
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700247def test_decode_bad_token_wrong_audience(token_factory):
248 token = token_factory()
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700249 audience = "audience2@example.com"
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700250 with pytest.raises(ValueError) as excinfo:
251 jwt.decode(token, PUBLIC_CERT_BYTES, audience=audience)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700252 assert excinfo.match(r"Token has wrong audience")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700253
254
Jonathan Beaulieu56c39462021-04-15 04:28:04 -0400255def test_decode_bad_token_wrong_audience_list(token_factory):
256 token = token_factory()
257 audience = ["audience2@example.com", "audience3@example.com"]
258 with pytest.raises(ValueError) as excinfo:
259 jwt.decode(token, PUBLIC_CERT_BYTES, audience=audience)
260 assert excinfo.match(r"Token has wrong audience")
261
262
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700263def test_decode_wrong_cert(token_factory):
264 with pytest.raises(ValueError) as excinfo:
265 jwt.decode(token_factory(), OTHER_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700266 assert excinfo.match(r"Could not verify token signature")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700267
268
269def test_decode_multicert_bad_cert(token_factory):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700270 certs = {"1": OTHER_CERT_BYTES, "2": PUBLIC_CERT_BYTES}
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700271 with pytest.raises(ValueError) as excinfo:
272 jwt.decode(token_factory(), certs)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700273 assert excinfo.match(r"Could not verify token signature")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700274
275
276def test_decode_no_cert(token_factory):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700277 certs = {"2": PUBLIC_CERT_BYTES}
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700278 with pytest.raises(ValueError) as excinfo:
279 jwt.decode(token_factory(), certs)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700280 assert excinfo.match(r"Certificate for key id 1 not found")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700281
282
283def test_decode_no_key_id(token_factory):
284 token = token_factory(key_id=False)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700285 certs = {"2": PUBLIC_CERT_BYTES}
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700286 payload = jwt.decode(token, certs)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700287 assert payload["user"] == "billy bob"
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700288
289
Thea Flowerse290a3d2020-04-01 10:11:42 -0700290def test_decode_unknown_alg():
Tres Seaver560cf1e2021-08-03 16:35:54 -0400291 headers = json.dumps({"kid": "1", "alg": "fakealg"})
Thea Flowerse290a3d2020-04-01 10:11:42 -0700292 token = b".".join(
Tres Seaver560cf1e2021-08-03 16:35:54 -0400293 map(lambda seg: base64.b64encode(seg.encode("utf-8")), [headers, "{}", "sig"])
Thea Flowerse290a3d2020-04-01 10:11:42 -0700294 )
295
296 with pytest.raises(ValueError) as excinfo:
297 jwt.decode(token)
298 assert excinfo.match(r"fakealg")
299
300
301def test_decode_missing_crytography_alg(monkeypatch):
302 monkeypatch.delitem(jwt._ALGORITHM_TO_VERIFIER_CLASS, "ES256")
Tres Seaver560cf1e2021-08-03 16:35:54 -0400303 headers = json.dumps({"kid": "1", "alg": "ES256"})
Thea Flowerse290a3d2020-04-01 10:11:42 -0700304 token = b".".join(
Tres Seaver560cf1e2021-08-03 16:35:54 -0400305 map(lambda seg: base64.b64encode(seg.encode("utf-8")), [headers, "{}", "sig"])
Thea Flowerse290a3d2020-04-01 10:11:42 -0700306 )
307
308 with pytest.raises(ValueError) as excinfo:
309 jwt.decode(token)
310 assert excinfo.match(r"cryptography")
311
312
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700313def test_roundtrip_explicit_key_id(token_factory):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700314 token = token_factory(key_id="3")
315 certs = {"2": OTHER_CERT_BYTES, "3": PUBLIC_CERT_BYTES}
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700316 payload = jwt.decode(token, certs)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700317 assert payload["user"] == "billy bob"
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700318
319
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700320class TestCredentials(object):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700321 SERVICE_ACCOUNT_EMAIL = "service-account@example.com"
322 SUBJECT = "subject"
323 AUDIENCE = "audience"
324 ADDITIONAL_CLAIMS = {"meta": "data"}
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700325 credentials = None
326
327 @pytest.fixture(autouse=True)
328 def credentials_fixture(self, signer):
329 self.credentials = jwt.Credentials(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700330 signer,
331 self.SERVICE_ACCOUNT_EMAIL,
332 self.SERVICE_ACCOUNT_EMAIL,
333 self.AUDIENCE,
334 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700335
336 def test_from_service_account_info(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700337 with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh:
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700338 info = json.load(fh)
339
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800340 credentials = jwt.Credentials.from_service_account_info(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700341 info, audience=self.AUDIENCE
342 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700343
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700344 assert credentials._signer.key_id == info["private_key_id"]
345 assert credentials._issuer == info["client_email"]
346 assert credentials._subject == info["client_email"]
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800347 assert credentials._audience == self.AUDIENCE
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700348
349 def test_from_service_account_info_args(self):
350 info = SERVICE_ACCOUNT_INFO.copy()
351
352 credentials = jwt.Credentials.from_service_account_info(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700353 info,
354 subject=self.SUBJECT,
355 audience=self.AUDIENCE,
356 additional_claims=self.ADDITIONAL_CLAIMS,
357 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700358
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700359 assert credentials._signer.key_id == info["private_key_id"]
360 assert credentials._issuer == info["client_email"]
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700361 assert credentials._subject == self.SUBJECT
362 assert credentials._audience == self.AUDIENCE
363 assert credentials._additional_claims == self.ADDITIONAL_CLAIMS
364
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700365 def test_from_service_account_file(self):
366 info = SERVICE_ACCOUNT_INFO.copy()
367
368 credentials = jwt.Credentials.from_service_account_file(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700369 SERVICE_ACCOUNT_JSON_FILE, audience=self.AUDIENCE
370 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700371
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700372 assert credentials._signer.key_id == info["private_key_id"]
373 assert credentials._issuer == info["client_email"]
374 assert credentials._subject == info["client_email"]
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800375 assert credentials._audience == self.AUDIENCE
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700376
377 def test_from_service_account_file_args(self):
378 info = SERVICE_ACCOUNT_INFO.copy()
379
380 credentials = jwt.Credentials.from_service_account_file(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700381 SERVICE_ACCOUNT_JSON_FILE,
382 subject=self.SUBJECT,
383 audience=self.AUDIENCE,
384 additional_claims=self.ADDITIONAL_CLAIMS,
385 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700386
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700387 assert credentials._signer.key_id == info["private_key_id"]
388 assert credentials._issuer == info["client_email"]
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700389 assert credentials._subject == self.SUBJECT
390 assert credentials._audience == self.AUDIENCE
391 assert credentials._additional_claims == self.ADDITIONAL_CLAIMS
392
Jon Wayne Parrottb8f48d02017-02-24 09:03:24 -0800393 def test_from_signing_credentials(self):
394 jwt_from_signing = self.credentials.from_signing_credentials(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700395 self.credentials, audience=mock.sentinel.new_audience
396 )
Jon Wayne Parrottb8f48d02017-02-24 09:03:24 -0800397 jwt_from_info = jwt.Credentials.from_service_account_info(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700398 SERVICE_ACCOUNT_INFO, audience=mock.sentinel.new_audience
399 )
Jon Wayne Parrottb8f48d02017-02-24 09:03:24 -0800400
401 assert isinstance(jwt_from_signing, jwt.Credentials)
402 assert jwt_from_signing._signer.key_id == jwt_from_info._signer.key_id
403 assert jwt_from_signing._issuer == jwt_from_info._issuer
404 assert jwt_from_signing._subject == jwt_from_info._subject
405 assert jwt_from_signing._audience == jwt_from_info._audience
406
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700407 def test_default_state(self):
408 assert not self.credentials.valid
409 # Expiration hasn't been set yet
410 assert not self.credentials.expired
411
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800412 def test_with_claims(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700413 new_audience = "new_audience"
414 new_credentials = self.credentials.with_claims(audience=new_audience)
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800415
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._audience == new_audience
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700420 assert new_credentials._additional_claims == self.credentials._additional_claims
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700421 assert new_credentials._quota_project_id == self.credentials._quota_project_id
422
arithmetic17282cfe6552021-06-16 15:30:36 -0700423 def test__make_jwt_without_audience(self):
424 cred = jwt.Credentials.from_service_account_info(
425 SERVICE_ACCOUNT_INFO.copy(),
426 subject=self.SUBJECT,
427 audience=None,
428 additional_claims={"scope": "foo bar"},
429 )
430 token, _ = cred._make_jwt()
431 payload = jwt.decode(token, PUBLIC_CERT_BYTES)
432 assert payload["scope"] == "foo bar"
433 assert "aud" not in payload
434
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700435 def test_with_quota_project(self):
436 quota_project_id = "project-foo"
437
438 new_credentials = self.credentials.with_quota_project(quota_project_id)
439 assert new_credentials._signer == self.credentials._signer
440 assert new_credentials._issuer == self.credentials._issuer
441 assert new_credentials._subject == self.credentials._subject
442 assert new_credentials._audience == self.credentials._audience
443 assert new_credentials._additional_claims == self.credentials._additional_claims
444 assert new_credentials._quota_project_id == quota_project_id
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800445
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700446 def test_sign_bytes(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700447 to_sign = b"123"
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700448 signature = self.credentials.sign_bytes(to_sign)
449 assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES)
450
Jon Wayne Parrottd7221672017-02-16 09:05:11 -0800451 def test_signer(self):
Jon Wayne Parrott254befe2017-02-22 14:37:31 -0800452 assert isinstance(self.credentials.signer, crypt.RSASigner)
Jon Wayne Parrottd7221672017-02-16 09:05:11 -0800453
Jon Wayne Parrott4c883f02016-12-02 14:26:33 -0800454 def test_signer_email(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700455 assert self.credentials.signer_email == SERVICE_ACCOUNT_INFO["client_email"]
Jon Wayne Parrott4c883f02016-12-02 14:26:33 -0800456
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700457 def _verify_token(self, token):
458 payload = jwt.decode(token, PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700459 assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700460 return payload
461
462 def test_refresh(self):
463 self.credentials.refresh(None)
464 assert self.credentials.valid
465 assert not self.credentials.expired
466
467 def test_expired(self):
468 assert not self.credentials.expired
469
470 self.credentials.refresh(None)
471 assert not self.credentials.expired
472
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700473 with mock.patch("google.auth._helpers.utcnow") as now:
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700474 one_day = datetime.timedelta(days=1)
475 now.return_value = self.credentials.expiry + one_day
476 assert self.credentials.expired
477
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800478 def test_before_request(self):
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700479 headers = {}
480
481 self.credentials.refresh(None)
482 self.credentials.before_request(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700483 None, "GET", "http://example.com?a=1#3", headers
484 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700485
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700486 header_value = headers["authorization"]
487 _, token = header_value.split(" ")
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700488
489 # Since the audience is set, it should use the existing token.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700490 assert token.encode("utf-8") == self.credentials.token
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700491
492 payload = self._verify_token(token)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700493 assert payload["aud"] == self.AUDIENCE
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700494
495 def test_before_request_refreshes(self):
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800496 assert not self.credentials.valid
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700497 self.credentials.before_request(None, "GET", "http://example.com?a=1#3", {})
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800498 assert self.credentials.valid
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700499
500
501class TestOnDemandCredentials(object):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700502 SERVICE_ACCOUNT_EMAIL = "service-account@example.com"
503 SUBJECT = "subject"
504 ADDITIONAL_CLAIMS = {"meta": "data"}
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700505 credentials = None
506
507 @pytest.fixture(autouse=True)
508 def credentials_fixture(self, signer):
509 self.credentials = jwt.OnDemandCredentials(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700510 signer,
511 self.SERVICE_ACCOUNT_EMAIL,
512 self.SERVICE_ACCOUNT_EMAIL,
513 max_cache_size=2,
514 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700515
516 def test_from_service_account_info(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700517 with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh:
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700518 info = json.load(fh)
519
520 credentials = jwt.OnDemandCredentials.from_service_account_info(info)
521
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700522 assert credentials._signer.key_id == info["private_key_id"]
523 assert credentials._issuer == info["client_email"]
524 assert credentials._subject == info["client_email"]
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700525
526 def test_from_service_account_info_args(self):
527 info = SERVICE_ACCOUNT_INFO.copy()
528
529 credentials = jwt.OnDemandCredentials.from_service_account_info(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700530 info, subject=self.SUBJECT, additional_claims=self.ADDITIONAL_CLAIMS
531 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700532
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700533 assert credentials._signer.key_id == info["private_key_id"]
534 assert credentials._issuer == info["client_email"]
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700535 assert credentials._subject == self.SUBJECT
536 assert credentials._additional_claims == self.ADDITIONAL_CLAIMS
537
538 def test_from_service_account_file(self):
539 info = SERVICE_ACCOUNT_INFO.copy()
540
541 credentials = jwt.OnDemandCredentials.from_service_account_file(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700542 SERVICE_ACCOUNT_JSON_FILE
543 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700544
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700545 assert credentials._signer.key_id == info["private_key_id"]
546 assert credentials._issuer == info["client_email"]
547 assert credentials._subject == info["client_email"]
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700548
549 def test_from_service_account_file_args(self):
550 info = SERVICE_ACCOUNT_INFO.copy()
551
552 credentials = jwt.OnDemandCredentials.from_service_account_file(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700553 SERVICE_ACCOUNT_JSON_FILE,
554 subject=self.SUBJECT,
555 additional_claims=self.ADDITIONAL_CLAIMS,
556 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700557
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700558 assert credentials._signer.key_id == info["private_key_id"]
559 assert credentials._issuer == info["client_email"]
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700560 assert credentials._subject == self.SUBJECT
561 assert credentials._additional_claims == self.ADDITIONAL_CLAIMS
562
563 def test_from_signing_credentials(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700564 jwt_from_signing = self.credentials.from_signing_credentials(self.credentials)
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700565 jwt_from_info = jwt.OnDemandCredentials.from_service_account_info(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700566 SERVICE_ACCOUNT_INFO
567 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700568
569 assert isinstance(jwt_from_signing, jwt.OnDemandCredentials)
570 assert jwt_from_signing._signer.key_id == jwt_from_info._signer.key_id
571 assert jwt_from_signing._issuer == jwt_from_info._issuer
572 assert jwt_from_signing._subject == jwt_from_info._subject
573
574 def test_default_state(self):
575 # Credentials are *always* valid.
576 assert self.credentials.valid
577 # Credentials *never* expire.
578 assert not self.credentials.expired
579
580 def test_with_claims(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700581 new_claims = {"meep": "moop"}
582 new_credentials = self.credentials.with_claims(additional_claims=new_claims)
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700583
584 assert new_credentials._signer == self.credentials._signer
585 assert new_credentials._issuer == self.credentials._issuer
586 assert new_credentials._subject == self.credentials._subject
587 assert new_credentials._additional_claims == new_claims
588
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700589 def test_with_quota_project(self):
590 quota_project_id = "project-foo"
591 new_credentials = self.credentials.with_quota_project(quota_project_id)
592
593 assert new_credentials._signer == self.credentials._signer
594 assert new_credentials._issuer == self.credentials._issuer
595 assert new_credentials._subject == self.credentials._subject
596 assert new_credentials._additional_claims == self.credentials._additional_claims
597 assert new_credentials._quota_project_id == quota_project_id
598
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700599 def test_sign_bytes(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700600 to_sign = b"123"
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700601 signature = self.credentials.sign_bytes(to_sign)
602 assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES)
603
604 def test_signer(self):
605 assert isinstance(self.credentials.signer, crypt.RSASigner)
606
607 def test_signer_email(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700608 assert self.credentials.signer_email == SERVICE_ACCOUNT_INFO["client_email"]
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700609
610 def _verify_token(self, token):
611 payload = jwt.decode(token, PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700612 assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700613 return payload
614
615 def test_refresh(self):
616 with pytest.raises(exceptions.RefreshError):
617 self.credentials.refresh(None)
618
619 def test_before_request(self):
620 headers = {}
621
622 self.credentials.before_request(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700623 None, "GET", "http://example.com?a=1#3", headers
624 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700625
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700626 _, token = headers["authorization"].split(" ")
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700627 payload = self._verify_token(token)
628
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700629 assert payload["aud"] == "http://example.com"
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700630
631 # Making another request should re-use the same token.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700632 self.credentials.before_request(None, "GET", "http://example.com?b=2", headers)
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700633
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700634 _, new_token = headers["authorization"].split(" ")
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700635
636 assert new_token == token
637
638 def test_expired_token(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700639 self.credentials._cache["audience"] = (
640 mock.sentinel.token,
641 datetime.datetime.min,
642 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700643
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700644 token = self.credentials._get_jwt_for_audience("audience")
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700645
646 assert token != mock.sentinel.token