blob: 7aa031ec533d040abbca6b52ee3fbc7df2a4cbdf [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
76@pytest.fixture
Thea Flowerse290a3d2020-04-01 10:11:42 -070077def es256_signer():
78 return crypt.ES256Signer.from_string(EC_PRIVATE_KEY_BYTES, "1")
79
80
81def test_encode_basic_es256(es256_signer):
82 test_payload = {"test": "value"}
83 encoded = jwt.encode(es256_signer, test_payload)
84 header, payload, _, _ = jwt._unverified_decode(encoded)
85 assert payload == test_payload
86 assert header == {"typ": "JWT", "alg": "ES256", "kid": es256_signer.key_id}
87
88
89@pytest.fixture
90def token_factory(signer, es256_signer):
91 def factory(claims=None, key_id=None, use_es256_signer=False):
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070092 now = _helpers.datetime_to_secs(_helpers.utcnow())
93 payload = {
Bu Sun Kim9eec0912019-10-21 17:04:21 -070094 "aud": "audience@example.com",
95 "iat": now,
96 "exp": now + 300,
97 "user": "billy bob",
98 "metadata": {"meta": "data"},
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070099 }
100 payload.update(claims or {})
101
102 # False is specified to remove the signer's key id for testing
103 # headers without key ids.
104 if key_id is False:
Jon Wayne Parrott254befe2017-02-22 14:37:31 -0800105 signer._key_id = None
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700106 key_id = None
107
Thea Flowerse290a3d2020-04-01 10:11:42 -0700108 if use_es256_signer:
109 return jwt.encode(es256_signer, payload, key_id=key_id)
110 else:
111 return jwt.encode(signer, payload, key_id=key_id)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700112
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700113 return factory
114
115
116def test_decode_valid(token_factory):
117 payload = jwt.decode(token_factory(), certs=PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700118 assert payload["aud"] == "audience@example.com"
119 assert payload["user"] == "billy bob"
120 assert payload["metadata"]["meta"] == "data"
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700121
122
Thea Flowerse290a3d2020-04-01 10:11:42 -0700123def test_decode_valid_es256(token_factory):
124 payload = jwt.decode(
125 token_factory(use_es256_signer=True), certs=EC_PUBLIC_CERT_BYTES
126 )
127 assert payload["aud"] == "audience@example.com"
128 assert payload["user"] == "billy bob"
129 assert payload["metadata"]["meta"] == "data"
130
131
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700132def test_decode_valid_with_audience(token_factory):
133 payload = jwt.decode(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700134 token_factory(), certs=PUBLIC_CERT_BYTES, audience="audience@example.com"
135 )
136 assert payload["aud"] == "audience@example.com"
137 assert payload["user"] == "billy bob"
138 assert payload["metadata"]["meta"] == "data"
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700139
140
141def test_decode_valid_unverified(token_factory):
142 payload = jwt.decode(token_factory(), certs=OTHER_CERT_BYTES, verify=False)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700143 assert payload["aud"] == "audience@example.com"
144 assert payload["user"] == "billy bob"
145 assert payload["metadata"]["meta"] == "data"
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700146
147
148def test_decode_bad_token_wrong_number_of_segments():
149 with pytest.raises(ValueError) as excinfo:
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700150 jwt.decode("1.2", PUBLIC_CERT_BYTES)
151 assert excinfo.match(r"Wrong number of segments")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700152
153
154def test_decode_bad_token_not_base64():
155 with pytest.raises((ValueError, TypeError)) as excinfo:
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700156 jwt.decode("1.2.3", PUBLIC_CERT_BYTES)
157 assert excinfo.match(r"Incorrect padding|more than a multiple of 4")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700158
159
160def test_decode_bad_token_not_json():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700161 token = b".".join([base64.urlsafe_b64encode(b"123!")] * 3)
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700162 with pytest.raises(ValueError) as excinfo:
163 jwt.decode(token, PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700164 assert excinfo.match(r"Can\'t parse segment")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700165
166
167def test_decode_bad_token_no_iat_or_exp(signer):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700168 token = jwt.encode(signer, {"test": "value"})
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700169 with pytest.raises(ValueError) as excinfo:
170 jwt.decode(token, PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700171 assert excinfo.match(r"Token does not contain required claim")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700172
173
174def test_decode_bad_token_too_early(token_factory):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700175 token = token_factory(
176 claims={
177 "iat": _helpers.datetime_to_secs(
178 _helpers.utcnow() + datetime.timedelta(hours=1)
179 )
180 }
181 )
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700182 with pytest.raises(ValueError) as excinfo:
183 jwt.decode(token, PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700184 assert excinfo.match(r"Token used too early")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700185
186
187def test_decode_bad_token_expired(token_factory):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700188 token = token_factory(
189 claims={
190 "exp": _helpers.datetime_to_secs(
191 _helpers.utcnow() - datetime.timedelta(hours=1)
192 )
193 }
194 )
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700195 with pytest.raises(ValueError) as excinfo:
196 jwt.decode(token, PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700197 assert excinfo.match(r"Token expired")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700198
199
200def test_decode_bad_token_wrong_audience(token_factory):
201 token = token_factory()
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700202 audience = "audience2@example.com"
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700203 with pytest.raises(ValueError) as excinfo:
204 jwt.decode(token, PUBLIC_CERT_BYTES, audience=audience)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700205 assert excinfo.match(r"Token has wrong audience")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700206
207
208def test_decode_wrong_cert(token_factory):
209 with pytest.raises(ValueError) as excinfo:
210 jwt.decode(token_factory(), OTHER_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700211 assert excinfo.match(r"Could not verify token signature")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700212
213
214def test_decode_multicert_bad_cert(token_factory):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700215 certs = {"1": OTHER_CERT_BYTES, "2": PUBLIC_CERT_BYTES}
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700216 with pytest.raises(ValueError) as excinfo:
217 jwt.decode(token_factory(), certs)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700218 assert excinfo.match(r"Could not verify token signature")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700219
220
221def test_decode_no_cert(token_factory):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700222 certs = {"2": PUBLIC_CERT_BYTES}
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700223 with pytest.raises(ValueError) as excinfo:
224 jwt.decode(token_factory(), certs)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700225 assert excinfo.match(r"Certificate for key id 1 not found")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700226
227
228def test_decode_no_key_id(token_factory):
229 token = token_factory(key_id=False)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700230 certs = {"2": PUBLIC_CERT_BYTES}
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700231 payload = jwt.decode(token, certs)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700232 assert payload["user"] == "billy bob"
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700233
234
Thea Flowerse290a3d2020-04-01 10:11:42 -0700235def test_decode_unknown_alg():
236 headers = json.dumps({u"kid": u"1", u"alg": u"fakealg"})
237 token = b".".join(
238 map(lambda seg: base64.b64encode(seg.encode("utf-8")), [headers, u"{}", u"sig"])
239 )
240
241 with pytest.raises(ValueError) as excinfo:
242 jwt.decode(token)
243 assert excinfo.match(r"fakealg")
244
245
246def test_decode_missing_crytography_alg(monkeypatch):
247 monkeypatch.delitem(jwt._ALGORITHM_TO_VERIFIER_CLASS, "ES256")
248 headers = json.dumps({u"kid": u"1", u"alg": u"ES256"})
249 token = b".".join(
250 map(lambda seg: base64.b64encode(seg.encode("utf-8")), [headers, u"{}", u"sig"])
251 )
252
253 with pytest.raises(ValueError) as excinfo:
254 jwt.decode(token)
255 assert excinfo.match(r"cryptography")
256
257
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700258def test_roundtrip_explicit_key_id(token_factory):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700259 token = token_factory(key_id="3")
260 certs = {"2": OTHER_CERT_BYTES, "3": PUBLIC_CERT_BYTES}
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700261 payload = jwt.decode(token, certs)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700262 assert payload["user"] == "billy bob"
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700263
264
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700265class TestCredentials(object):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700266 SERVICE_ACCOUNT_EMAIL = "service-account@example.com"
267 SUBJECT = "subject"
268 AUDIENCE = "audience"
269 ADDITIONAL_CLAIMS = {"meta": "data"}
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700270 credentials = None
271
272 @pytest.fixture(autouse=True)
273 def credentials_fixture(self, signer):
274 self.credentials = jwt.Credentials(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700275 signer,
276 self.SERVICE_ACCOUNT_EMAIL,
277 self.SERVICE_ACCOUNT_EMAIL,
278 self.AUDIENCE,
279 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700280
281 def test_from_service_account_info(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700282 with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh:
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700283 info = json.load(fh)
284
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800285 credentials = jwt.Credentials.from_service_account_info(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700286 info, audience=self.AUDIENCE
287 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700288
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700289 assert credentials._signer.key_id == info["private_key_id"]
290 assert credentials._issuer == info["client_email"]
291 assert credentials._subject == info["client_email"]
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800292 assert credentials._audience == self.AUDIENCE
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700293
294 def test_from_service_account_info_args(self):
295 info = SERVICE_ACCOUNT_INFO.copy()
296
297 credentials = jwt.Credentials.from_service_account_info(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700298 info,
299 subject=self.SUBJECT,
300 audience=self.AUDIENCE,
301 additional_claims=self.ADDITIONAL_CLAIMS,
302 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700303
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700304 assert credentials._signer.key_id == info["private_key_id"]
305 assert credentials._issuer == info["client_email"]
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700306 assert credentials._subject == self.SUBJECT
307 assert credentials._audience == self.AUDIENCE
308 assert credentials._additional_claims == self.ADDITIONAL_CLAIMS
309
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700310 def test_from_service_account_file(self):
311 info = SERVICE_ACCOUNT_INFO.copy()
312
313 credentials = jwt.Credentials.from_service_account_file(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700314 SERVICE_ACCOUNT_JSON_FILE, audience=self.AUDIENCE
315 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700316
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700317 assert credentials._signer.key_id == info["private_key_id"]
318 assert credentials._issuer == info["client_email"]
319 assert credentials._subject == info["client_email"]
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800320 assert credentials._audience == self.AUDIENCE
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700321
322 def test_from_service_account_file_args(self):
323 info = SERVICE_ACCOUNT_INFO.copy()
324
325 credentials = jwt.Credentials.from_service_account_file(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700326 SERVICE_ACCOUNT_JSON_FILE,
327 subject=self.SUBJECT,
328 audience=self.AUDIENCE,
329 additional_claims=self.ADDITIONAL_CLAIMS,
330 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700331
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700332 assert credentials._signer.key_id == info["private_key_id"]
333 assert credentials._issuer == info["client_email"]
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700334 assert credentials._subject == self.SUBJECT
335 assert credentials._audience == self.AUDIENCE
336 assert credentials._additional_claims == self.ADDITIONAL_CLAIMS
337
Jon Wayne Parrottb8f48d02017-02-24 09:03:24 -0800338 def test_from_signing_credentials(self):
339 jwt_from_signing = self.credentials.from_signing_credentials(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700340 self.credentials, audience=mock.sentinel.new_audience
341 )
Jon Wayne Parrottb8f48d02017-02-24 09:03:24 -0800342 jwt_from_info = jwt.Credentials.from_service_account_info(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700343 SERVICE_ACCOUNT_INFO, audience=mock.sentinel.new_audience
344 )
Jon Wayne Parrottb8f48d02017-02-24 09:03:24 -0800345
346 assert isinstance(jwt_from_signing, jwt.Credentials)
347 assert jwt_from_signing._signer.key_id == jwt_from_info._signer.key_id
348 assert jwt_from_signing._issuer == jwt_from_info._issuer
349 assert jwt_from_signing._subject == jwt_from_info._subject
350 assert jwt_from_signing._audience == jwt_from_info._audience
351
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700352 def test_default_state(self):
353 assert not self.credentials.valid
354 # Expiration hasn't been set yet
355 assert not self.credentials.expired
356
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800357 def test_with_claims(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700358 new_audience = "new_audience"
359 new_credentials = self.credentials.with_claims(audience=new_audience)
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800360
361 assert new_credentials._signer == self.credentials._signer
362 assert new_credentials._issuer == self.credentials._issuer
363 assert new_credentials._subject == self.credentials._subject
364 assert new_credentials._audience == new_audience
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700365 assert new_credentials._additional_claims == self.credentials._additional_claims
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700366 assert new_credentials._quota_project_id == self.credentials._quota_project_id
367
368 def test_with_quota_project(self):
369 quota_project_id = "project-foo"
370
371 new_credentials = self.credentials.with_quota_project(quota_project_id)
372 assert new_credentials._signer == self.credentials._signer
373 assert new_credentials._issuer == self.credentials._issuer
374 assert new_credentials._subject == self.credentials._subject
375 assert new_credentials._audience == self.credentials._audience
376 assert new_credentials._additional_claims == self.credentials._additional_claims
377 assert new_credentials._quota_project_id == quota_project_id
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800378
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700379 def test_sign_bytes(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700380 to_sign = b"123"
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700381 signature = self.credentials.sign_bytes(to_sign)
382 assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES)
383
Jon Wayne Parrottd7221672017-02-16 09:05:11 -0800384 def test_signer(self):
Jon Wayne Parrott254befe2017-02-22 14:37:31 -0800385 assert isinstance(self.credentials.signer, crypt.RSASigner)
Jon Wayne Parrottd7221672017-02-16 09:05:11 -0800386
Jon Wayne Parrott4c883f02016-12-02 14:26:33 -0800387 def test_signer_email(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700388 assert self.credentials.signer_email == SERVICE_ACCOUNT_INFO["client_email"]
Jon Wayne Parrott4c883f02016-12-02 14:26:33 -0800389
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700390 def _verify_token(self, token):
391 payload = jwt.decode(token, PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700392 assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700393 return payload
394
395 def test_refresh(self):
396 self.credentials.refresh(None)
397 assert self.credentials.valid
398 assert not self.credentials.expired
399
400 def test_expired(self):
401 assert not self.credentials.expired
402
403 self.credentials.refresh(None)
404 assert not self.credentials.expired
405
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700406 with mock.patch("google.auth._helpers.utcnow") as now:
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700407 one_day = datetime.timedelta(days=1)
408 now.return_value = self.credentials.expiry + one_day
409 assert self.credentials.expired
410
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800411 def test_before_request(self):
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700412 headers = {}
413
414 self.credentials.refresh(None)
415 self.credentials.before_request(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700416 None, "GET", "http://example.com?a=1#3", headers
417 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700418
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700419 header_value = headers["authorization"]
420 _, token = header_value.split(" ")
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700421
422 # Since the audience is set, it should use the existing token.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700423 assert token.encode("utf-8") == self.credentials.token
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700424
425 payload = self._verify_token(token)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700426 assert payload["aud"] == self.AUDIENCE
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700427
428 def test_before_request_refreshes(self):
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800429 assert not self.credentials.valid
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700430 self.credentials.before_request(None, "GET", "http://example.com?a=1#3", {})
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800431 assert self.credentials.valid
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700432
433
434class TestOnDemandCredentials(object):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700435 SERVICE_ACCOUNT_EMAIL = "service-account@example.com"
436 SUBJECT = "subject"
437 ADDITIONAL_CLAIMS = {"meta": "data"}
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700438 credentials = None
439
440 @pytest.fixture(autouse=True)
441 def credentials_fixture(self, signer):
442 self.credentials = jwt.OnDemandCredentials(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700443 signer,
444 self.SERVICE_ACCOUNT_EMAIL,
445 self.SERVICE_ACCOUNT_EMAIL,
446 max_cache_size=2,
447 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700448
449 def test_from_service_account_info(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700450 with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh:
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700451 info = json.load(fh)
452
453 credentials = jwt.OnDemandCredentials.from_service_account_info(info)
454
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700455 assert credentials._signer.key_id == info["private_key_id"]
456 assert credentials._issuer == info["client_email"]
457 assert credentials._subject == info["client_email"]
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700458
459 def test_from_service_account_info_args(self):
460 info = SERVICE_ACCOUNT_INFO.copy()
461
462 credentials = jwt.OnDemandCredentials.from_service_account_info(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700463 info, subject=self.SUBJECT, additional_claims=self.ADDITIONAL_CLAIMS
464 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700465
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700466 assert credentials._signer.key_id == info["private_key_id"]
467 assert credentials._issuer == info["client_email"]
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700468 assert credentials._subject == self.SUBJECT
469 assert credentials._additional_claims == self.ADDITIONAL_CLAIMS
470
471 def test_from_service_account_file(self):
472 info = SERVICE_ACCOUNT_INFO.copy()
473
474 credentials = jwt.OnDemandCredentials.from_service_account_file(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700475 SERVICE_ACCOUNT_JSON_FILE
476 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700477
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700478 assert credentials._signer.key_id == info["private_key_id"]
479 assert credentials._issuer == info["client_email"]
480 assert credentials._subject == info["client_email"]
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700481
482 def test_from_service_account_file_args(self):
483 info = SERVICE_ACCOUNT_INFO.copy()
484
485 credentials = jwt.OnDemandCredentials.from_service_account_file(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700486 SERVICE_ACCOUNT_JSON_FILE,
487 subject=self.SUBJECT,
488 additional_claims=self.ADDITIONAL_CLAIMS,
489 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700490
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700491 assert credentials._signer.key_id == info["private_key_id"]
492 assert credentials._issuer == info["client_email"]
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700493 assert credentials._subject == self.SUBJECT
494 assert credentials._additional_claims == self.ADDITIONAL_CLAIMS
495
496 def test_from_signing_credentials(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700497 jwt_from_signing = self.credentials.from_signing_credentials(self.credentials)
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700498 jwt_from_info = jwt.OnDemandCredentials.from_service_account_info(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700499 SERVICE_ACCOUNT_INFO
500 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700501
502 assert isinstance(jwt_from_signing, jwt.OnDemandCredentials)
503 assert jwt_from_signing._signer.key_id == jwt_from_info._signer.key_id
504 assert jwt_from_signing._issuer == jwt_from_info._issuer
505 assert jwt_from_signing._subject == jwt_from_info._subject
506
507 def test_default_state(self):
508 # Credentials are *always* valid.
509 assert self.credentials.valid
510 # Credentials *never* expire.
511 assert not self.credentials.expired
512
513 def test_with_claims(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700514 new_claims = {"meep": "moop"}
515 new_credentials = self.credentials.with_claims(additional_claims=new_claims)
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700516
517 assert new_credentials._signer == self.credentials._signer
518 assert new_credentials._issuer == self.credentials._issuer
519 assert new_credentials._subject == self.credentials._subject
520 assert new_credentials._additional_claims == new_claims
521
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700522 def test_with_quota_project(self):
523 quota_project_id = "project-foo"
524 new_credentials = self.credentials.with_quota_project(quota_project_id)
525
526 assert new_credentials._signer == self.credentials._signer
527 assert new_credentials._issuer == self.credentials._issuer
528 assert new_credentials._subject == self.credentials._subject
529 assert new_credentials._additional_claims == self.credentials._additional_claims
530 assert new_credentials._quota_project_id == quota_project_id
531
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700532 def test_sign_bytes(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700533 to_sign = b"123"
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700534 signature = self.credentials.sign_bytes(to_sign)
535 assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES)
536
537 def test_signer(self):
538 assert isinstance(self.credentials.signer, crypt.RSASigner)
539
540 def test_signer_email(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700541 assert self.credentials.signer_email == SERVICE_ACCOUNT_INFO["client_email"]
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700542
543 def _verify_token(self, token):
544 payload = jwt.decode(token, PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700545 assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700546 return payload
547
548 def test_refresh(self):
549 with pytest.raises(exceptions.RefreshError):
550 self.credentials.refresh(None)
551
552 def test_before_request(self):
553 headers = {}
554
555 self.credentials.before_request(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700556 None, "GET", "http://example.com?a=1#3", headers
557 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700558
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700559 _, token = headers["authorization"].split(" ")
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700560 payload = self._verify_token(token)
561
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700562 assert payload["aud"] == "http://example.com"
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700563
564 # Making another request should re-use the same token.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700565 self.credentials.before_request(None, "GET", "http://example.com?b=2", headers)
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700566
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700567 _, new_token = headers["authorization"].split(" ")
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700568
569 assert new_token == token
570
571 def test_expired_token(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700572 self.credentials._cache["audience"] = (
573 mock.sentinel.token,
574 datetime.datetime.min,
575 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700576
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700577 token = self.credentials._get_jwt_for_audience("audience")
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700578
579 assert token != mock.sentinel.token