blob: 4a7f66e7f495312aea397864c9e0378f821fb066 [file] [log] [blame]
C.J. Collier37141e42020-02-13 13:49:49 -08001# Copyright 2016 Google LLC
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -07002#
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 datetime
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -080016import json
17import os
Bu Sun Kim32d71a52019-12-18 11:30:46 -080018import pickle
19import sys
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070020
21import mock
Thea Flowers118c0482018-05-24 13:34:07 -070022import pytest
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070023
24from google.auth import _helpers
Thea Flowers118c0482018-05-24 13:34:07 -070025from google.auth import exceptions
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070026from google.auth import transport
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070027from google.oauth2 import credentials
28
29
Bu Sun Kim9eec0912019-10-21 17:04:21 -070030DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data")
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -080031
Bu Sun Kim9eec0912019-10-21 17:04:21 -070032AUTH_USER_JSON_FILE = os.path.join(DATA_DIR, "authorized_user.json")
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -080033
Bu Sun Kim9eec0912019-10-21 17:04:21 -070034with open(AUTH_USER_JSON_FILE, "r") as fh:
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -080035 AUTH_USER_INFO = json.load(fh)
36
37
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070038class TestCredentials(object):
Bu Sun Kim9eec0912019-10-21 17:04:21 -070039 TOKEN_URI = "https://example.com/oauth2/token"
40 REFRESH_TOKEN = "refresh_token"
arithmetic172882293fe2021-04-14 11:22:13 -070041 RAPT_TOKEN = "rapt_token"
Bu Sun Kim9eec0912019-10-21 17:04:21 -070042 CLIENT_ID = "client_id"
43 CLIENT_SECRET = "client_secret"
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070044
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070045 @classmethod
46 def make_credentials(cls):
47 return credentials.Credentials(
Bu Sun Kim9eec0912019-10-21 17:04:21 -070048 token=None,
49 refresh_token=cls.REFRESH_TOKEN,
50 token_uri=cls.TOKEN_URI,
51 client_id=cls.CLIENT_ID,
52 client_secret=cls.CLIENT_SECRET,
arithmetic172882293fe2021-04-14 11:22:13 -070053 rapt_token=cls.RAPT_TOKEN,
Bu Sun Kim9eec0912019-10-21 17:04:21 -070054 )
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070055
56 def test_default_state(self):
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070057 credentials = self.make_credentials()
58 assert not credentials.valid
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070059 # Expiration hasn't been set yet
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070060 assert not credentials.expired
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070061 # Scopes aren't required for these credentials
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070062 assert not credentials.requires_scopes
Jon Wayne Parrott2d0549a2017-03-01 09:27:16 -080063 # Test properties
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070064 assert credentials.refresh_token == self.REFRESH_TOKEN
65 assert credentials.token_uri == self.TOKEN_URI
66 assert credentials.client_id == self.CLIENT_ID
67 assert credentials.client_secret == self.CLIENT_SECRET
arithmetic172882293fe2021-04-14 11:22:13 -070068 assert credentials.rapt_token == self.RAPT_TOKEN
bojeil-googleec2fb182021-07-22 10:01:31 -070069 assert credentials.refresh_handler is None
70
71 def test_refresh_handler_setter_and_getter(self):
72 scopes = ["email", "profile"]
73 original_refresh_handler = mock.Mock(return_value=("ACCESS_TOKEN_1", None))
74 updated_refresh_handler = mock.Mock(return_value=("ACCESS_TOKEN_2", None))
75 creds = credentials.Credentials(
76 token=None,
77 refresh_token=None,
78 token_uri=None,
79 client_id=None,
80 client_secret=None,
81 rapt_token=None,
82 scopes=scopes,
83 default_scopes=None,
84 refresh_handler=original_refresh_handler,
85 )
86
87 assert creds.refresh_handler is original_refresh_handler
88
89 creds.refresh_handler = updated_refresh_handler
90
91 assert creds.refresh_handler is updated_refresh_handler
92
93 creds.refresh_handler = None
94
95 assert creds.refresh_handler is None
96
97 def test_invalid_refresh_handler(self):
98 scopes = ["email", "profile"]
99 with pytest.raises(TypeError) as excinfo:
100 credentials.Credentials(
101 token=None,
102 refresh_token=None,
103 token_uri=None,
104 client_id=None,
105 client_secret=None,
106 rapt_token=None,
107 scopes=scopes,
108 default_scopes=None,
109 refresh_handler=object(),
110 )
111
112 assert excinfo.match("The provided refresh_handler is not a callable or None.")
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700113
arithmetic172882293fe2021-04-14 11:22:13 -0700114 @mock.patch("google.oauth2.reauth.refresh_grant", autospec=True)
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700115 @mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700116 "google.auth._helpers.utcnow",
117 return_value=datetime.datetime.min + _helpers.CLOCK_SKEW,
118 )
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700119 def test_refresh_success(self, unused_utcnow, refresh_grant):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700120 token = "token"
arithmetic172882293fe2021-04-14 11:22:13 -0700121 new_rapt_token = "new_rapt_token"
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700122 expiry = _helpers.utcnow() + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700123 grant_response = {"id_token": mock.sentinel.id_token}
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700124 refresh_grant.return_value = (
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700125 # Access token
126 token,
127 # New refresh token
128 None,
129 # Expiry,
130 expiry,
131 # Extra data
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700132 grant_response,
arithmetic172882293fe2021-04-14 11:22:13 -0700133 # rapt_token
134 new_rapt_token,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700135 )
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700136
137 request = mock.create_autospec(transport.Request)
138 credentials = self.make_credentials()
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700139
140 # Refresh credentials
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700141 credentials.refresh(request)
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700142
143 # Check jwt grant call.
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700144 refresh_grant.assert_called_with(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700145 request,
146 self.TOKEN_URI,
147 self.REFRESH_TOKEN,
148 self.CLIENT_ID,
149 self.CLIENT_SECRET,
150 None,
arithmetic172882293fe2021-04-14 11:22:13 -0700151 self.RAPT_TOKEN,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700152 )
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700153
154 # Check that the credentials have the token and expiry
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700155 assert credentials.token == token
156 assert credentials.expiry == expiry
157 assert credentials.id_token == mock.sentinel.id_token
arithmetic172882293fe2021-04-14 11:22:13 -0700158 assert credentials.rapt_token == new_rapt_token
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700159
160 # Check that the credentials are valid (have a token and are not
161 # expired)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700162 assert credentials.valid
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800163
Thea Flowers118c0482018-05-24 13:34:07 -0700164 def test_refresh_no_refresh_token(self):
165 request = mock.create_autospec(transport.Request)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700166 credentials_ = credentials.Credentials(token=None, refresh_token=None)
Thea Flowers118c0482018-05-24 13:34:07 -0700167
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700168 with pytest.raises(exceptions.RefreshError, match="necessary fields"):
Thea Flowers118c0482018-05-24 13:34:07 -0700169 credentials_.refresh(request)
170
171 request.assert_not_called()
172
arithmetic172882293fe2021-04-14 11:22:13 -0700173 @mock.patch("google.oauth2.reauth.refresh_grant", autospec=True)
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400174 @mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700175 "google.auth._helpers.utcnow",
176 return_value=datetime.datetime.min + _helpers.CLOCK_SKEW,
177 )
bojeil-googleec2fb182021-07-22 10:01:31 -0700178 def test_refresh_with_refresh_token_and_refresh_handler(
179 self, unused_utcnow, refresh_grant
180 ):
181 token = "token"
182 new_rapt_token = "new_rapt_token"
183 expiry = _helpers.utcnow() + datetime.timedelta(seconds=500)
184 grant_response = {"id_token": mock.sentinel.id_token}
185 refresh_grant.return_value = (
186 # Access token
187 token,
188 # New refresh token
189 None,
190 # Expiry,
191 expiry,
192 # Extra data
193 grant_response,
194 # rapt_token
195 new_rapt_token,
196 )
197
198 refresh_handler = mock.Mock()
199 request = mock.create_autospec(transport.Request)
200 creds = credentials.Credentials(
201 token=None,
202 refresh_token=self.REFRESH_TOKEN,
203 token_uri=self.TOKEN_URI,
204 client_id=self.CLIENT_ID,
205 client_secret=self.CLIENT_SECRET,
206 rapt_token=self.RAPT_TOKEN,
207 refresh_handler=refresh_handler,
208 )
209
210 # Refresh credentials
211 creds.refresh(request)
212
213 # Check jwt grant call.
214 refresh_grant.assert_called_with(
215 request,
216 self.TOKEN_URI,
217 self.REFRESH_TOKEN,
218 self.CLIENT_ID,
219 self.CLIENT_SECRET,
220 None,
221 self.RAPT_TOKEN,
222 )
223
224 # Check that the credentials have the token and expiry
225 assert creds.token == token
226 assert creds.expiry == expiry
227 assert creds.id_token == mock.sentinel.id_token
228 assert creds.rapt_token == new_rapt_token
229
230 # Check that the credentials are valid (have a token and are not
231 # expired)
232 assert creds.valid
233
234 # Assert refresh handler not called as the refresh token has
235 # higher priority.
236 refresh_handler.assert_not_called()
237
238 @mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
239 def test_refresh_with_refresh_handler_success_scopes(self, unused_utcnow):
240 expected_expiry = datetime.datetime.min + datetime.timedelta(seconds=2800)
241 refresh_handler = mock.Mock(return_value=("ACCESS_TOKEN", expected_expiry))
242 scopes = ["email", "profile"]
243 default_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
244 request = mock.create_autospec(transport.Request)
245 creds = credentials.Credentials(
246 token=None,
247 refresh_token=None,
248 token_uri=None,
249 client_id=None,
250 client_secret=None,
251 rapt_token=None,
252 scopes=scopes,
253 default_scopes=default_scopes,
254 refresh_handler=refresh_handler,
255 )
256
257 creds.refresh(request)
258
259 assert creds.token == "ACCESS_TOKEN"
260 assert creds.expiry == expected_expiry
261 assert creds.valid
262 assert not creds.expired
263 # Confirm refresh handler called with the expected arguments.
264 refresh_handler.assert_called_with(request, scopes=scopes)
265
266 @mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
267 def test_refresh_with_refresh_handler_success_default_scopes(self, unused_utcnow):
268 expected_expiry = datetime.datetime.min + datetime.timedelta(seconds=2800)
269 original_refresh_handler = mock.Mock(
270 return_value=("UNUSED_TOKEN", expected_expiry)
271 )
272 refresh_handler = mock.Mock(return_value=("ACCESS_TOKEN", expected_expiry))
273 default_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
274 request = mock.create_autospec(transport.Request)
275 creds = credentials.Credentials(
276 token=None,
277 refresh_token=None,
278 token_uri=None,
279 client_id=None,
280 client_secret=None,
281 rapt_token=None,
282 scopes=None,
283 default_scopes=default_scopes,
284 refresh_handler=original_refresh_handler,
285 )
286
287 # Test newly set refresh_handler is used instead of the original one.
288 creds.refresh_handler = refresh_handler
289 creds.refresh(request)
290
291 assert creds.token == "ACCESS_TOKEN"
292 assert creds.expiry == expected_expiry
293 assert creds.valid
294 assert not creds.expired
295 # default_scopes should be used since no developer provided scopes
296 # are provided.
297 refresh_handler.assert_called_with(request, scopes=default_scopes)
298
299 @mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
300 def test_refresh_with_refresh_handler_invalid_token(self, unused_utcnow):
301 expected_expiry = datetime.datetime.min + datetime.timedelta(seconds=2800)
302 # Simulate refresh handler does not return a valid token.
303 refresh_handler = mock.Mock(return_value=(None, expected_expiry))
304 scopes = ["email", "profile"]
305 default_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
306 request = mock.create_autospec(transport.Request)
307 creds = credentials.Credentials(
308 token=None,
309 refresh_token=None,
310 token_uri=None,
311 client_id=None,
312 client_secret=None,
313 rapt_token=None,
314 scopes=scopes,
315 default_scopes=default_scopes,
316 refresh_handler=refresh_handler,
317 )
318
319 with pytest.raises(
320 exceptions.RefreshError, match="returned token is not a string"
321 ):
322 creds.refresh(request)
323
324 assert creds.token is None
325 assert creds.expiry is None
326 assert not creds.valid
327 # Confirm refresh handler called with the expected arguments.
328 refresh_handler.assert_called_with(request, scopes=scopes)
329
330 def test_refresh_with_refresh_handler_invalid_expiry(self):
331 # Simulate refresh handler returns expiration time in an invalid unit.
332 refresh_handler = mock.Mock(return_value=("TOKEN", 2800))
333 scopes = ["email", "profile"]
334 default_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
335 request = mock.create_autospec(transport.Request)
336 creds = credentials.Credentials(
337 token=None,
338 refresh_token=None,
339 token_uri=None,
340 client_id=None,
341 client_secret=None,
342 rapt_token=None,
343 scopes=scopes,
344 default_scopes=default_scopes,
345 refresh_handler=refresh_handler,
346 )
347
348 with pytest.raises(
349 exceptions.RefreshError, match="returned expiry is not a datetime object"
350 ):
351 creds.refresh(request)
352
353 assert creds.token is None
354 assert creds.expiry is None
355 assert not creds.valid
356 # Confirm refresh handler called with the expected arguments.
357 refresh_handler.assert_called_with(request, scopes=scopes)
358
359 @mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
360 def test_refresh_with_refresh_handler_expired_token(self, unused_utcnow):
361 expected_expiry = datetime.datetime.min + _helpers.CLOCK_SKEW
362 # Simulate refresh handler returns an expired token.
363 refresh_handler = mock.Mock(return_value=("TOKEN", expected_expiry))
364 scopes = ["email", "profile"]
365 default_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
366 request = mock.create_autospec(transport.Request)
367 creds = credentials.Credentials(
368 token=None,
369 refresh_token=None,
370 token_uri=None,
371 client_id=None,
372 client_secret=None,
373 rapt_token=None,
374 scopes=scopes,
375 default_scopes=default_scopes,
376 refresh_handler=refresh_handler,
377 )
378
379 with pytest.raises(exceptions.RefreshError, match="already expired"):
380 creds.refresh(request)
381
382 assert creds.token is None
383 assert creds.expiry is None
384 assert not creds.valid
385 # Confirm refresh handler called with the expected arguments.
386 refresh_handler.assert_called_with(request, scopes=scopes)
387
388 @mock.patch("google.oauth2.reauth.refresh_grant", autospec=True)
389 @mock.patch(
390 "google.auth._helpers.utcnow",
391 return_value=datetime.datetime.min + _helpers.CLOCK_SKEW,
392 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400393 def test_credentials_with_scopes_requested_refresh_success(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700394 self, unused_utcnow, refresh_grant
395 ):
396 scopes = ["email", "profile"]
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700397 default_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700398 token = "token"
arithmetic172882293fe2021-04-14 11:22:13 -0700399 new_rapt_token = "new_rapt_token"
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400400 expiry = _helpers.utcnow() + datetime.timedelta(seconds=500)
arithmetic172882293fe2021-04-14 11:22:13 -0700401 grant_response = {"id_token": mock.sentinel.id_token, "scope": "email profile"}
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400402 refresh_grant.return_value = (
403 # Access token
404 token,
405 # New refresh token
406 None,
407 # Expiry,
408 expiry,
409 # Extra data
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700410 grant_response,
arithmetic172882293fe2021-04-14 11:22:13 -0700411 # rapt token
412 new_rapt_token,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700413 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400414
415 request = mock.create_autospec(transport.Request)
416 creds = credentials.Credentials(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700417 token=None,
418 refresh_token=self.REFRESH_TOKEN,
419 token_uri=self.TOKEN_URI,
420 client_id=self.CLIENT_ID,
421 client_secret=self.CLIENT_SECRET,
422 scopes=scopes,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700423 default_scopes=default_scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700424 rapt_token=self.RAPT_TOKEN,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700425 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400426
427 # Refresh credentials
428 creds.refresh(request)
429
430 # Check jwt grant call.
431 refresh_grant.assert_called_with(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700432 request,
433 self.TOKEN_URI,
434 self.REFRESH_TOKEN,
435 self.CLIENT_ID,
436 self.CLIENT_SECRET,
437 scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700438 self.RAPT_TOKEN,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700439 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400440
441 # Check that the credentials have the token and expiry
442 assert creds.token == token
443 assert creds.expiry == expiry
444 assert creds.id_token == mock.sentinel.id_token
445 assert creds.has_scopes(scopes)
arithmetic172882293fe2021-04-14 11:22:13 -0700446 assert creds.rapt_token == new_rapt_token
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400447
448 # Check that the credentials are valid (have a token and are not
449 # expired.)
450 assert creds.valid
451
arithmetic172882293fe2021-04-14 11:22:13 -0700452 @mock.patch("google.oauth2.reauth.refresh_grant", autospec=True)
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400453 @mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700454 "google.auth._helpers.utcnow",
455 return_value=datetime.datetime.min + _helpers.CLOCK_SKEW,
456 )
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700457 def test_credentials_with_only_default_scopes_requested(
458 self, unused_utcnow, refresh_grant
459 ):
460 default_scopes = ["email", "profile"]
461 token = "token"
arithmetic172882293fe2021-04-14 11:22:13 -0700462 new_rapt_token = "new_rapt_token"
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700463 expiry = _helpers.utcnow() + datetime.timedelta(seconds=500)
464 grant_response = {"id_token": mock.sentinel.id_token}
465 refresh_grant.return_value = (
466 # Access token
467 token,
468 # New refresh token
469 None,
470 # Expiry,
471 expiry,
472 # Extra data
473 grant_response,
arithmetic172882293fe2021-04-14 11:22:13 -0700474 # rapt token
475 new_rapt_token,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700476 )
477
478 request = mock.create_autospec(transport.Request)
479 creds = credentials.Credentials(
480 token=None,
481 refresh_token=self.REFRESH_TOKEN,
482 token_uri=self.TOKEN_URI,
483 client_id=self.CLIENT_ID,
484 client_secret=self.CLIENT_SECRET,
485 default_scopes=default_scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700486 rapt_token=self.RAPT_TOKEN,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700487 )
488
489 # Refresh credentials
490 creds.refresh(request)
491
492 # Check jwt grant call.
493 refresh_grant.assert_called_with(
494 request,
495 self.TOKEN_URI,
496 self.REFRESH_TOKEN,
497 self.CLIENT_ID,
498 self.CLIENT_SECRET,
499 default_scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700500 self.RAPT_TOKEN,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700501 )
502
503 # Check that the credentials have the token and expiry
504 assert creds.token == token
505 assert creds.expiry == expiry
506 assert creds.id_token == mock.sentinel.id_token
507 assert creds.has_scopes(default_scopes)
arithmetic172882293fe2021-04-14 11:22:13 -0700508 assert creds.rapt_token == new_rapt_token
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700509
510 # Check that the credentials are valid (have a token and are not
511 # expired.)
512 assert creds.valid
513
arithmetic172882293fe2021-04-14 11:22:13 -0700514 @mock.patch("google.oauth2.reauth.refresh_grant", autospec=True)
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700515 @mock.patch(
516 "google.auth._helpers.utcnow",
517 return_value=datetime.datetime.min + _helpers.CLOCK_SKEW,
518 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400519 def test_credentials_with_scopes_returned_refresh_success(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700520 self, unused_utcnow, refresh_grant
521 ):
522 scopes = ["email", "profile"]
523 token = "token"
arithmetic172882293fe2021-04-14 11:22:13 -0700524 new_rapt_token = "new_rapt_token"
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400525 expiry = _helpers.utcnow() + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700526 grant_response = {
527 "id_token": mock.sentinel.id_token,
528 "scopes": " ".join(scopes),
529 }
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400530 refresh_grant.return_value = (
531 # Access token
532 token,
533 # New refresh token
534 None,
535 # Expiry,
536 expiry,
537 # Extra data
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700538 grant_response,
arithmetic172882293fe2021-04-14 11:22:13 -0700539 # rapt token
540 new_rapt_token,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700541 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400542
543 request = mock.create_autospec(transport.Request)
544 creds = credentials.Credentials(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700545 token=None,
546 refresh_token=self.REFRESH_TOKEN,
547 token_uri=self.TOKEN_URI,
548 client_id=self.CLIENT_ID,
549 client_secret=self.CLIENT_SECRET,
550 scopes=scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700551 rapt_token=self.RAPT_TOKEN,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700552 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400553
554 # Refresh credentials
555 creds.refresh(request)
556
557 # Check jwt grant call.
558 refresh_grant.assert_called_with(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700559 request,
560 self.TOKEN_URI,
561 self.REFRESH_TOKEN,
562 self.CLIENT_ID,
563 self.CLIENT_SECRET,
564 scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700565 self.RAPT_TOKEN,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700566 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400567
568 # Check that the credentials have the token and expiry
569 assert creds.token == token
570 assert creds.expiry == expiry
571 assert creds.id_token == mock.sentinel.id_token
572 assert creds.has_scopes(scopes)
arithmetic172882293fe2021-04-14 11:22:13 -0700573 assert creds.rapt_token == new_rapt_token
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400574
575 # Check that the credentials are valid (have a token and are not
576 # expired.)
577 assert creds.valid
578
arithmetic172882293fe2021-04-14 11:22:13 -0700579 @mock.patch("google.oauth2.reauth.refresh_grant", autospec=True)
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400580 @mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700581 "google.auth._helpers.utcnow",
582 return_value=datetime.datetime.min + _helpers.CLOCK_SKEW,
583 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400584 def test_credentials_with_scopes_refresh_failure_raises_refresh_error(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700585 self, unused_utcnow, refresh_grant
586 ):
587 scopes = ["email", "profile"]
588 scopes_returned = ["email"]
589 token = "token"
arithmetic172882293fe2021-04-14 11:22:13 -0700590 new_rapt_token = "new_rapt_token"
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400591 expiry = _helpers.utcnow() + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700592 grant_response = {
593 "id_token": mock.sentinel.id_token,
arithmetic172882293fe2021-04-14 11:22:13 -0700594 "scope": " ".join(scopes_returned),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700595 }
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400596 refresh_grant.return_value = (
597 # Access token
598 token,
599 # New refresh token
600 None,
601 # Expiry,
602 expiry,
603 # Extra data
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700604 grant_response,
arithmetic172882293fe2021-04-14 11:22:13 -0700605 # rapt token
606 new_rapt_token,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700607 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400608
609 request = mock.create_autospec(transport.Request)
610 creds = credentials.Credentials(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700611 token=None,
612 refresh_token=self.REFRESH_TOKEN,
613 token_uri=self.TOKEN_URI,
614 client_id=self.CLIENT_ID,
615 client_secret=self.CLIENT_SECRET,
616 scopes=scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700617 rapt_token=self.RAPT_TOKEN,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700618 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400619
620 # Refresh credentials
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700621 with pytest.raises(
622 exceptions.RefreshError, match="Not all requested scopes were granted"
623 ):
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400624 creds.refresh(request)
625
626 # Check jwt grant call.
627 refresh_grant.assert_called_with(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700628 request,
629 self.TOKEN_URI,
630 self.REFRESH_TOKEN,
631 self.CLIENT_ID,
632 self.CLIENT_SECRET,
633 scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700634 self.RAPT_TOKEN,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700635 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400636
637 # Check that the credentials have the token and expiry
638 assert creds.token == token
639 assert creds.expiry == expiry
640 assert creds.id_token == mock.sentinel.id_token
641 assert creds.has_scopes(scopes)
arithmetic172882293fe2021-04-14 11:22:13 -0700642 assert creds.rapt_token == new_rapt_token
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400643
644 # Check that the credentials are valid (have a token and are not
645 # expired.)
646 assert creds.valid
647
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800648 def test_apply_with_quota_project_id(self):
649 creds = credentials.Credentials(
650 token="token",
651 refresh_token=self.REFRESH_TOKEN,
652 token_uri=self.TOKEN_URI,
653 client_id=self.CLIENT_ID,
654 client_secret=self.CLIENT_SECRET,
655 quota_project_id="quota-project-123",
656 )
657
658 headers = {}
659 creds.apply(headers)
660 assert headers["x-goog-user-project"] == "quota-project-123"
Bu Sun Kim18d5ae62020-07-21 13:44:03 -0700661 assert "token" in headers["authorization"]
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800662
663 def test_apply_with_no_quota_project_id(self):
664 creds = credentials.Credentials(
665 token="token",
666 refresh_token=self.REFRESH_TOKEN,
667 token_uri=self.TOKEN_URI,
668 client_id=self.CLIENT_ID,
669 client_secret=self.CLIENT_SECRET,
670 )
671
672 headers = {}
673 creds.apply(headers)
674 assert "x-goog-user-project" not in headers
Bu Sun Kim18d5ae62020-07-21 13:44:03 -0700675 assert "token" in headers["authorization"]
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800676
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700677 def test_with_quota_project(self):
678 creds = credentials.Credentials(
679 token="token",
680 refresh_token=self.REFRESH_TOKEN,
681 token_uri=self.TOKEN_URI,
682 client_id=self.CLIENT_ID,
683 client_secret=self.CLIENT_SECRET,
684 quota_project_id="quota-project-123",
685 )
686
687 new_creds = creds.with_quota_project("new-project-456")
688 assert new_creds.quota_project_id == "new-project-456"
689 headers = {}
690 creds.apply(headers)
691 assert "x-goog-user-project" in headers
692
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800693 def test_from_authorized_user_info(self):
694 info = AUTH_USER_INFO.copy()
695
696 creds = credentials.Credentials.from_authorized_user_info(info)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700697 assert creds.client_secret == info["client_secret"]
698 assert creds.client_id == info["client_id"]
699 assert creds.refresh_token == info["refresh_token"]
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800700 assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT
701 assert creds.scopes is None
702
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700703 scopes = ["email", "profile"]
704 creds = credentials.Credentials.from_authorized_user_info(info, scopes)
705 assert creds.client_secret == info["client_secret"]
706 assert creds.client_id == info["client_id"]
707 assert creds.refresh_token == info["refresh_token"]
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800708 assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT
709 assert creds.scopes == scopes
710
wesley chund0e0aba2020-09-17 09:18:55 -0700711 info["scopes"] = "email" # single non-array scope from file
712 creds = credentials.Credentials.from_authorized_user_info(info)
713 assert creds.scopes == [info["scopes"]]
714
715 info["scopes"] = ["email", "profile"] # array scope from file
716 creds = credentials.Credentials.from_authorized_user_info(info)
717 assert creds.scopes == info["scopes"]
718
719 expiry = datetime.datetime(2020, 8, 14, 15, 54, 1)
720 info["expiry"] = expiry.isoformat() + "Z"
721 creds = credentials.Credentials.from_authorized_user_info(info)
722 assert creds.expiry == expiry
723 assert creds.expired
724
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800725 def test_from_authorized_user_file(self):
726 info = AUTH_USER_INFO.copy()
727
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700728 creds = credentials.Credentials.from_authorized_user_file(AUTH_USER_JSON_FILE)
729 assert creds.client_secret == info["client_secret"]
730 assert creds.client_id == info["client_id"]
731 assert creds.refresh_token == info["refresh_token"]
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800732 assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT
733 assert creds.scopes is None
734
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700735 scopes = ["email", "profile"]
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800736 creds = credentials.Credentials.from_authorized_user_file(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700737 AUTH_USER_JSON_FILE, scopes
738 )
739 assert creds.client_secret == info["client_secret"]
740 assert creds.client_id == info["client_id"]
741 assert creds.refresh_token == info["refresh_token"]
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800742 assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT
743 assert creds.scopes == scopes
patkasperbfb1f8c2019-12-05 22:03:44 +0100744
745 def test_to_json(self):
746 info = AUTH_USER_INFO.copy()
wesley chund0e0aba2020-09-17 09:18:55 -0700747 expiry = datetime.datetime(2020, 8, 14, 15, 54, 1)
748 info["expiry"] = expiry.isoformat() + "Z"
patkasperbfb1f8c2019-12-05 22:03:44 +0100749 creds = credentials.Credentials.from_authorized_user_info(info)
wesley chund0e0aba2020-09-17 09:18:55 -0700750 assert creds.expiry == expiry
patkasperbfb1f8c2019-12-05 22:03:44 +0100751
752 # Test with no `strip` arg
753 json_output = creds.to_json()
754 json_asdict = json.loads(json_output)
755 assert json_asdict.get("token") == creds.token
756 assert json_asdict.get("refresh_token") == creds.refresh_token
757 assert json_asdict.get("token_uri") == creds.token_uri
758 assert json_asdict.get("client_id") == creds.client_id
759 assert json_asdict.get("scopes") == creds.scopes
760 assert json_asdict.get("client_secret") == creds.client_secret
wesley chund0e0aba2020-09-17 09:18:55 -0700761 assert json_asdict.get("expiry") == info["expiry"]
patkasperbfb1f8c2019-12-05 22:03:44 +0100762
763 # Test with a `strip` arg
764 json_output = creds.to_json(strip=["client_secret"])
765 json_asdict = json.loads(json_output)
766 assert json_asdict.get("token") == creds.token
767 assert json_asdict.get("refresh_token") == creds.refresh_token
768 assert json_asdict.get("token_uri") == creds.token_uri
769 assert json_asdict.get("client_id") == creds.client_id
770 assert json_asdict.get("scopes") == creds.scopes
771 assert json_asdict.get("client_secret") is None
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800772
wesley chund0e0aba2020-09-17 09:18:55 -0700773 # Test with no expiry
774 creds.expiry = None
775 json_output = creds.to_json()
776 json_asdict = json.loads(json_output)
777 assert json_asdict.get("expiry") is None
778
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800779 def test_pickle_and_unpickle(self):
780 creds = self.make_credentials()
781 unpickled = pickle.loads(pickle.dumps(creds))
782
783 # make sure attributes aren't lost during pickling
784 assert list(creds.__dict__).sort() == list(unpickled.__dict__).sort()
785
786 for attr in list(creds.__dict__):
787 assert getattr(creds, attr) == getattr(unpickled, attr)
788
bojeil-googleec2fb182021-07-22 10:01:31 -0700789 def test_pickle_and_unpickle_with_refresh_handler(self):
790 expected_expiry = _helpers.utcnow() + datetime.timedelta(seconds=2800)
791 refresh_handler = mock.Mock(return_value=("TOKEN", expected_expiry))
792
793 creds = credentials.Credentials(
794 token=None,
795 refresh_token=None,
796 token_uri=None,
797 client_id=None,
798 client_secret=None,
799 rapt_token=None,
800 refresh_handler=refresh_handler,
801 )
802 unpickled = pickle.loads(pickle.dumps(creds))
803
804 # make sure attributes aren't lost during pickling
805 assert list(creds.__dict__).sort() == list(unpickled.__dict__).sort()
806
807 for attr in list(creds.__dict__):
808 # For the _refresh_handler property, the unpickled creds should be
809 # set to None.
810 if attr == "_refresh_handler":
811 assert getattr(unpickled, attr) is None
812 else:
813 assert getattr(creds, attr) == getattr(unpickled, attr)
814
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800815 def test_pickle_with_missing_attribute(self):
816 creds = self.make_credentials()
817
818 # remove an optional attribute before pickling
819 # this mimics a pickle created with a previous class definition with
820 # fewer attributes
821 del creds.__dict__["_quota_project_id"]
822
823 unpickled = pickle.loads(pickle.dumps(creds))
824
825 # Attribute should be initialized by `__setstate__`
826 assert unpickled.quota_project_id is None
827
828 # pickles are not compatible across versions
829 @pytest.mark.skipif(
830 sys.version_info < (3, 5),
831 reason="pickle file can only be loaded with Python >= 3.5",
832 )
833 def test_unpickle_old_credentials_pickle(self):
834 # make sure a credentials file pickled with an older
835 # library version (google-auth==1.5.1) can be unpickled
836 with open(
837 os.path.join(DATA_DIR, "old_oauth_credentials_py3.pickle"), "rb"
838 ) as f:
839 credentials = pickle.load(f)
840 assert credentials.quota_project_id is None
arithmetic1728772dac62020-03-27 14:34:13 -0700841
842
843class TestUserAccessTokenCredentials(object):
844 def test_instance(self):
845 cred = credentials.UserAccessTokenCredentials()
846 assert cred._account is None
847
848 cred = cred.with_account("account")
849 assert cred._account == "account"
850
851 @mock.patch("google.auth._cloud_sdk.get_auth_access_token", autospec=True)
852 def test_refresh(self, get_auth_access_token):
853 get_auth_access_token.return_value = "access_token"
854 cred = credentials.UserAccessTokenCredentials()
855 cred.refresh(None)
856 assert cred.token == "access_token"
857
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700858 def test_with_quota_project(self):
859 cred = credentials.UserAccessTokenCredentials()
860 quota_project_cred = cred.with_quota_project("project-foo")
861
862 assert quota_project_cred._quota_project_id == "project-foo"
863 assert quota_project_cred._account == cred._account
864
arithmetic1728772dac62020-03-27 14:34:13 -0700865 @mock.patch(
866 "google.oauth2.credentials.UserAccessTokenCredentials.apply", autospec=True
867 )
868 @mock.patch(
869 "google.oauth2.credentials.UserAccessTokenCredentials.refresh", autospec=True
870 )
871 def test_before_request(self, refresh, apply):
872 cred = credentials.UserAccessTokenCredentials()
873 cred.before_request(mock.Mock(), "GET", "https://example.com", {})
874 refresh.assert_called()
875 apply.assert_called()