blob: b0c6e48e9d1eff9a7b27e8a3e8016d73d60257e5 [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
Bu Sun Kim9eec0912019-10-21 17:04:21 -070040SERVICE_ACCOUNT_JSON_FILE = os.path.join(DATA_DIR, "service_account.json")
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -070041
Bu Sun Kim9eec0912019-10-21 17:04:21 -070042with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh:
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -070043 SERVICE_ACCOUNT_INFO = json.load(fh)
44
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070045
46@pytest.fixture
47def signer():
Bu Sun Kim9eec0912019-10-21 17:04:21 -070048 return crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, "1")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070049
50
51def test_encode_basic(signer):
Bu Sun Kim9eec0912019-10-21 17:04:21 -070052 test_payload = {"test": "value"}
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070053 encoded = jwt.encode(signer, test_payload)
54 header, payload, _, _ = jwt._unverified_decode(encoded)
55 assert payload == test_payload
Bu Sun Kim9eec0912019-10-21 17:04:21 -070056 assert header == {"typ": "JWT", "alg": "RS256", "kid": signer.key_id}
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070057
58
59def test_encode_extra_headers(signer):
Bu Sun Kim9eec0912019-10-21 17:04:21 -070060 encoded = jwt.encode(signer, {}, header={"extra": "value"})
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070061 header = jwt.decode_header(encoded)
62 assert header == {
Bu Sun Kim9eec0912019-10-21 17:04:21 -070063 "typ": "JWT",
64 "alg": "RS256",
65 "kid": signer.key_id,
66 "extra": "value",
67 }
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070068
69
70@pytest.fixture
71def token_factory(signer):
72 def factory(claims=None, key_id=None):
73 now = _helpers.datetime_to_secs(_helpers.utcnow())
74 payload = {
Bu Sun Kim9eec0912019-10-21 17:04:21 -070075 "aud": "audience@example.com",
76 "iat": now,
77 "exp": now + 300,
78 "user": "billy bob",
79 "metadata": {"meta": "data"},
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070080 }
81 payload.update(claims or {})
82
83 # False is specified to remove the signer's key id for testing
84 # headers without key ids.
85 if key_id is False:
Jon Wayne Parrott254befe2017-02-22 14:37:31 -080086 signer._key_id = None
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070087 key_id = None
88
89 return jwt.encode(signer, payload, key_id=key_id)
Bu Sun Kim9eec0912019-10-21 17:04:21 -070090
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070091 return factory
92
93
94def test_decode_valid(token_factory):
95 payload = jwt.decode(token_factory(), certs=PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -070096 assert payload["aud"] == "audience@example.com"
97 assert payload["user"] == "billy bob"
98 assert payload["metadata"]["meta"] == "data"
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -070099
100
101def test_decode_valid_with_audience(token_factory):
102 payload = jwt.decode(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700103 token_factory(), certs=PUBLIC_CERT_BYTES, audience="audience@example.com"
104 )
105 assert payload["aud"] == "audience@example.com"
106 assert payload["user"] == "billy bob"
107 assert payload["metadata"]["meta"] == "data"
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700108
109
110def test_decode_valid_unverified(token_factory):
111 payload = jwt.decode(token_factory(), certs=OTHER_CERT_BYTES, verify=False)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700112 assert payload["aud"] == "audience@example.com"
113 assert payload["user"] == "billy bob"
114 assert payload["metadata"]["meta"] == "data"
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700115
116
117def test_decode_bad_token_wrong_number_of_segments():
118 with pytest.raises(ValueError) as excinfo:
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700119 jwt.decode("1.2", PUBLIC_CERT_BYTES)
120 assert excinfo.match(r"Wrong number of segments")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700121
122
123def test_decode_bad_token_not_base64():
124 with pytest.raises((ValueError, TypeError)) as excinfo:
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700125 jwt.decode("1.2.3", PUBLIC_CERT_BYTES)
126 assert excinfo.match(r"Incorrect padding|more than a multiple of 4")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700127
128
129def test_decode_bad_token_not_json():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700130 token = b".".join([base64.urlsafe_b64encode(b"123!")] * 3)
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700131 with pytest.raises(ValueError) as excinfo:
132 jwt.decode(token, PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700133 assert excinfo.match(r"Can\'t parse segment")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700134
135
136def test_decode_bad_token_no_iat_or_exp(signer):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700137 token = jwt.encode(signer, {"test": "value"})
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700138 with pytest.raises(ValueError) as excinfo:
139 jwt.decode(token, PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700140 assert excinfo.match(r"Token does not contain required claim")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700141
142
143def test_decode_bad_token_too_early(token_factory):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700144 token = token_factory(
145 claims={
146 "iat": _helpers.datetime_to_secs(
147 _helpers.utcnow() + datetime.timedelta(hours=1)
148 )
149 }
150 )
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700151 with pytest.raises(ValueError) as excinfo:
152 jwt.decode(token, PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700153 assert excinfo.match(r"Token used too early")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700154
155
156def test_decode_bad_token_expired(token_factory):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700157 token = token_factory(
158 claims={
159 "exp": _helpers.datetime_to_secs(
160 _helpers.utcnow() - datetime.timedelta(hours=1)
161 )
162 }
163 )
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700164 with pytest.raises(ValueError) as excinfo:
165 jwt.decode(token, PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700166 assert excinfo.match(r"Token expired")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700167
168
169def test_decode_bad_token_wrong_audience(token_factory):
170 token = token_factory()
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700171 audience = "audience2@example.com"
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700172 with pytest.raises(ValueError) as excinfo:
173 jwt.decode(token, PUBLIC_CERT_BYTES, audience=audience)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700174 assert excinfo.match(r"Token has wrong audience")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700175
176
177def test_decode_wrong_cert(token_factory):
178 with pytest.raises(ValueError) as excinfo:
179 jwt.decode(token_factory(), OTHER_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700180 assert excinfo.match(r"Could not verify token signature")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700181
182
183def test_decode_multicert_bad_cert(token_factory):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700184 certs = {"1": OTHER_CERT_BYTES, "2": PUBLIC_CERT_BYTES}
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700185 with pytest.raises(ValueError) as excinfo:
186 jwt.decode(token_factory(), certs)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700187 assert excinfo.match(r"Could not verify token signature")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700188
189
190def test_decode_no_cert(token_factory):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700191 certs = {"2": PUBLIC_CERT_BYTES}
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700192 with pytest.raises(ValueError) as excinfo:
193 jwt.decode(token_factory(), certs)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700194 assert excinfo.match(r"Certificate for key id 1 not found")
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700195
196
197def test_decode_no_key_id(token_factory):
198 token = token_factory(key_id=False)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700199 certs = {"2": PUBLIC_CERT_BYTES}
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700200 payload = jwt.decode(token, certs)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700201 assert payload["user"] == "billy bob"
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700202
203
204def test_roundtrip_explicit_key_id(token_factory):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700205 token = token_factory(key_id="3")
206 certs = {"2": OTHER_CERT_BYTES, "3": PUBLIC_CERT_BYTES}
Jon Wayne Parrott5824ad82016-10-06 09:27:44 -0700207 payload = jwt.decode(token, certs)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700208 assert payload["user"] == "billy bob"
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700209
210
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700211class TestCredentials(object):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700212 SERVICE_ACCOUNT_EMAIL = "service-account@example.com"
213 SUBJECT = "subject"
214 AUDIENCE = "audience"
215 ADDITIONAL_CLAIMS = {"meta": "data"}
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700216 credentials = None
217
218 @pytest.fixture(autouse=True)
219 def credentials_fixture(self, signer):
220 self.credentials = jwt.Credentials(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700221 signer,
222 self.SERVICE_ACCOUNT_EMAIL,
223 self.SERVICE_ACCOUNT_EMAIL,
224 self.AUDIENCE,
225 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700226
227 def test_from_service_account_info(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700228 with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh:
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700229 info = json.load(fh)
230
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800231 credentials = jwt.Credentials.from_service_account_info(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700232 info, audience=self.AUDIENCE
233 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700234
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700235 assert credentials._signer.key_id == info["private_key_id"]
236 assert credentials._issuer == info["client_email"]
237 assert credentials._subject == info["client_email"]
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800238 assert credentials._audience == self.AUDIENCE
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700239
240 def test_from_service_account_info_args(self):
241 info = SERVICE_ACCOUNT_INFO.copy()
242
243 credentials = jwt.Credentials.from_service_account_info(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700244 info,
245 subject=self.SUBJECT,
246 audience=self.AUDIENCE,
247 additional_claims=self.ADDITIONAL_CLAIMS,
248 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700249
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700250 assert credentials._signer.key_id == info["private_key_id"]
251 assert credentials._issuer == info["client_email"]
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700252 assert credentials._subject == self.SUBJECT
253 assert credentials._audience == self.AUDIENCE
254 assert credentials._additional_claims == self.ADDITIONAL_CLAIMS
255
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700256 def test_from_service_account_file(self):
257 info = SERVICE_ACCOUNT_INFO.copy()
258
259 credentials = jwt.Credentials.from_service_account_file(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700260 SERVICE_ACCOUNT_JSON_FILE, audience=self.AUDIENCE
261 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700262
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700263 assert credentials._signer.key_id == info["private_key_id"]
264 assert credentials._issuer == info["client_email"]
265 assert credentials._subject == info["client_email"]
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800266 assert credentials._audience == self.AUDIENCE
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700267
268 def test_from_service_account_file_args(self):
269 info = SERVICE_ACCOUNT_INFO.copy()
270
271 credentials = jwt.Credentials.from_service_account_file(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700272 SERVICE_ACCOUNT_JSON_FILE,
273 subject=self.SUBJECT,
274 audience=self.AUDIENCE,
275 additional_claims=self.ADDITIONAL_CLAIMS,
276 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700277
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700278 assert credentials._signer.key_id == info["private_key_id"]
279 assert credentials._issuer == info["client_email"]
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700280 assert credentials._subject == self.SUBJECT
281 assert credentials._audience == self.AUDIENCE
282 assert credentials._additional_claims == self.ADDITIONAL_CLAIMS
283
Jon Wayne Parrottb8f48d02017-02-24 09:03:24 -0800284 def test_from_signing_credentials(self):
285 jwt_from_signing = self.credentials.from_signing_credentials(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700286 self.credentials, audience=mock.sentinel.new_audience
287 )
Jon Wayne Parrottb8f48d02017-02-24 09:03:24 -0800288 jwt_from_info = jwt.Credentials.from_service_account_info(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700289 SERVICE_ACCOUNT_INFO, audience=mock.sentinel.new_audience
290 )
Jon Wayne Parrottb8f48d02017-02-24 09:03:24 -0800291
292 assert isinstance(jwt_from_signing, jwt.Credentials)
293 assert jwt_from_signing._signer.key_id == jwt_from_info._signer.key_id
294 assert jwt_from_signing._issuer == jwt_from_info._issuer
295 assert jwt_from_signing._subject == jwt_from_info._subject
296 assert jwt_from_signing._audience == jwt_from_info._audience
297
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700298 def test_default_state(self):
299 assert not self.credentials.valid
300 # Expiration hasn't been set yet
301 assert not self.credentials.expired
302
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800303 def test_with_claims(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700304 new_audience = "new_audience"
305 new_credentials = self.credentials.with_claims(audience=new_audience)
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800306
307 assert new_credentials._signer == self.credentials._signer
308 assert new_credentials._issuer == self.credentials._issuer
309 assert new_credentials._subject == self.credentials._subject
310 assert new_credentials._audience == new_audience
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700311 assert new_credentials._additional_claims == self.credentials._additional_claims
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800312
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700313 def test_sign_bytes(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700314 to_sign = b"123"
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700315 signature = self.credentials.sign_bytes(to_sign)
316 assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES)
317
Jon Wayne Parrottd7221672017-02-16 09:05:11 -0800318 def test_signer(self):
Jon Wayne Parrott254befe2017-02-22 14:37:31 -0800319 assert isinstance(self.credentials.signer, crypt.RSASigner)
Jon Wayne Parrottd7221672017-02-16 09:05:11 -0800320
Jon Wayne Parrott4c883f02016-12-02 14:26:33 -0800321 def test_signer_email(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700322 assert self.credentials.signer_email == SERVICE_ACCOUNT_INFO["client_email"]
Jon Wayne Parrott4c883f02016-12-02 14:26:33 -0800323
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700324 def _verify_token(self, token):
325 payload = jwt.decode(token, PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700326 assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700327 return payload
328
329 def test_refresh(self):
330 self.credentials.refresh(None)
331 assert self.credentials.valid
332 assert not self.credentials.expired
333
334 def test_expired(self):
335 assert not self.credentials.expired
336
337 self.credentials.refresh(None)
338 assert not self.credentials.expired
339
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700340 with mock.patch("google.auth._helpers.utcnow") as now:
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700341 one_day = datetime.timedelta(days=1)
342 now.return_value = self.credentials.expiry + one_day
343 assert self.credentials.expired
344
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800345 def test_before_request(self):
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700346 headers = {}
347
348 self.credentials.refresh(None)
349 self.credentials.before_request(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700350 None, "GET", "http://example.com?a=1#3", headers
351 )
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700352
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700353 header_value = headers["authorization"]
354 _, token = header_value.split(" ")
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700355
356 # Since the audience is set, it should use the existing token.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700357 assert token.encode("utf-8") == self.credentials.token
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700358
359 payload = self._verify_token(token)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700360 assert payload["aud"] == self.AUDIENCE
Jon Wayne Parrottabcd3ed2016-10-17 11:23:47 -0700361
362 def test_before_request_refreshes(self):
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800363 assert not self.credentials.valid
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700364 self.credentials.before_request(None, "GET", "http://example.com?a=1#3", {})
Jon Wayne Parrottab086892017-02-23 09:20:14 -0800365 assert self.credentials.valid
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700366
367
368class TestOnDemandCredentials(object):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700369 SERVICE_ACCOUNT_EMAIL = "service-account@example.com"
370 SUBJECT = "subject"
371 ADDITIONAL_CLAIMS = {"meta": "data"}
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700372 credentials = None
373
374 @pytest.fixture(autouse=True)
375 def credentials_fixture(self, signer):
376 self.credentials = jwt.OnDemandCredentials(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700377 signer,
378 self.SERVICE_ACCOUNT_EMAIL,
379 self.SERVICE_ACCOUNT_EMAIL,
380 max_cache_size=2,
381 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700382
383 def test_from_service_account_info(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700384 with open(SERVICE_ACCOUNT_JSON_FILE, "r") as fh:
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700385 info = json.load(fh)
386
387 credentials = jwt.OnDemandCredentials.from_service_account_info(info)
388
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700389 assert credentials._signer.key_id == info["private_key_id"]
390 assert credentials._issuer == info["client_email"]
391 assert credentials._subject == info["client_email"]
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700392
393 def test_from_service_account_info_args(self):
394 info = SERVICE_ACCOUNT_INFO.copy()
395
396 credentials = jwt.OnDemandCredentials.from_service_account_info(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700397 info, subject=self.SUBJECT, additional_claims=self.ADDITIONAL_CLAIMS
398 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700399
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700400 assert credentials._signer.key_id == info["private_key_id"]
401 assert credentials._issuer == info["client_email"]
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700402 assert credentials._subject == self.SUBJECT
403 assert credentials._additional_claims == self.ADDITIONAL_CLAIMS
404
405 def test_from_service_account_file(self):
406 info = SERVICE_ACCOUNT_INFO.copy()
407
408 credentials = jwt.OnDemandCredentials.from_service_account_file(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700409 SERVICE_ACCOUNT_JSON_FILE
410 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700411
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700412 assert credentials._signer.key_id == info["private_key_id"]
413 assert credentials._issuer == info["client_email"]
414 assert credentials._subject == info["client_email"]
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700415
416 def test_from_service_account_file_args(self):
417 info = SERVICE_ACCOUNT_INFO.copy()
418
419 credentials = jwt.OnDemandCredentials.from_service_account_file(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700420 SERVICE_ACCOUNT_JSON_FILE,
421 subject=self.SUBJECT,
422 additional_claims=self.ADDITIONAL_CLAIMS,
423 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700424
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700425 assert credentials._signer.key_id == info["private_key_id"]
426 assert credentials._issuer == info["client_email"]
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700427 assert credentials._subject == self.SUBJECT
428 assert credentials._additional_claims == self.ADDITIONAL_CLAIMS
429
430 def test_from_signing_credentials(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700431 jwt_from_signing = self.credentials.from_signing_credentials(self.credentials)
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700432 jwt_from_info = jwt.OnDemandCredentials.from_service_account_info(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700433 SERVICE_ACCOUNT_INFO
434 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700435
436 assert isinstance(jwt_from_signing, jwt.OnDemandCredentials)
437 assert jwt_from_signing._signer.key_id == jwt_from_info._signer.key_id
438 assert jwt_from_signing._issuer == jwt_from_info._issuer
439 assert jwt_from_signing._subject == jwt_from_info._subject
440
441 def test_default_state(self):
442 # Credentials are *always* valid.
443 assert self.credentials.valid
444 # Credentials *never* expire.
445 assert not self.credentials.expired
446
447 def test_with_claims(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700448 new_claims = {"meep": "moop"}
449 new_credentials = self.credentials.with_claims(additional_claims=new_claims)
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700450
451 assert new_credentials._signer == self.credentials._signer
452 assert new_credentials._issuer == self.credentials._issuer
453 assert new_credentials._subject == self.credentials._subject
454 assert new_credentials._additional_claims == new_claims
455
456 def test_sign_bytes(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700457 to_sign = b"123"
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700458 signature = self.credentials.sign_bytes(to_sign)
459 assert crypt.verify_signature(to_sign, signature, PUBLIC_CERT_BYTES)
460
461 def test_signer(self):
462 assert isinstance(self.credentials.signer, crypt.RSASigner)
463
464 def test_signer_email(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700465 assert self.credentials.signer_email == SERVICE_ACCOUNT_INFO["client_email"]
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700466
467 def _verify_token(self, token):
468 payload = jwt.decode(token, PUBLIC_CERT_BYTES)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700469 assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700470 return payload
471
472 def test_refresh(self):
473 with pytest.raises(exceptions.RefreshError):
474 self.credentials.refresh(None)
475
476 def test_before_request(self):
477 headers = {}
478
479 self.credentials.before_request(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700480 None, "GET", "http://example.com?a=1#3", headers
481 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700482
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700483 _, token = headers["authorization"].split(" ")
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700484 payload = self._verify_token(token)
485
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700486 assert payload["aud"] == "http://example.com"
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700487
488 # Making another request should re-use the same token.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700489 self.credentials.before_request(None, "GET", "http://example.com?b=2", headers)
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700490
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700491 _, new_token = headers["authorization"].split(" ")
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700492
493 assert new_token == token
494
495 def test_expired_token(self):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700496 self.credentials._cache["audience"] = (
497 mock.sentinel.token,
498 datetime.datetime.min,
499 )
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700500
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700501 token = self.credentials._get_jwt_for_audience("audience")
Jon Wayne Parrottcfbfd252017-03-28 13:03:11 -0700502
503 assert token != mock.sentinel.token