blob: b6a80e3d071db0913539660c2d0acf45f10a38ba [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,
arithmetic172813aed5f2021-09-07 16:24:45 -070054 enable_reauth_refresh=True,
Bu Sun Kim9eec0912019-10-21 17:04:21 -070055 )
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070056
57 def test_default_state(self):
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070058 credentials = self.make_credentials()
59 assert not credentials.valid
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070060 # Expiration hasn't been set yet
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070061 assert not credentials.expired
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -070062 # Scopes aren't required for these credentials
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070063 assert not credentials.requires_scopes
Jon Wayne Parrott2d0549a2017-03-01 09:27:16 -080064 # Test properties
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070065 assert credentials.refresh_token == self.REFRESH_TOKEN
66 assert credentials.token_uri == self.TOKEN_URI
67 assert credentials.client_id == self.CLIENT_ID
68 assert credentials.client_secret == self.CLIENT_SECRET
arithmetic172882293fe2021-04-14 11:22:13 -070069 assert credentials.rapt_token == self.RAPT_TOKEN
bojeil-googleec2fb182021-07-22 10:01:31 -070070 assert credentials.refresh_handler is None
71
72 def test_refresh_handler_setter_and_getter(self):
73 scopes = ["email", "profile"]
74 original_refresh_handler = mock.Mock(return_value=("ACCESS_TOKEN_1", None))
75 updated_refresh_handler = mock.Mock(return_value=("ACCESS_TOKEN_2", None))
76 creds = credentials.Credentials(
77 token=None,
78 refresh_token=None,
79 token_uri=None,
80 client_id=None,
81 client_secret=None,
82 rapt_token=None,
83 scopes=scopes,
84 default_scopes=None,
85 refresh_handler=original_refresh_handler,
86 )
87
88 assert creds.refresh_handler is original_refresh_handler
89
90 creds.refresh_handler = updated_refresh_handler
91
92 assert creds.refresh_handler is updated_refresh_handler
93
94 creds.refresh_handler = None
95
96 assert creds.refresh_handler is None
97
98 def test_invalid_refresh_handler(self):
99 scopes = ["email", "profile"]
100 with pytest.raises(TypeError) as excinfo:
101 credentials.Credentials(
102 token=None,
103 refresh_token=None,
104 token_uri=None,
105 client_id=None,
106 client_secret=None,
107 rapt_token=None,
108 scopes=scopes,
109 default_scopes=None,
110 refresh_handler=object(),
111 )
112
113 assert excinfo.match("The provided refresh_handler is not a callable or None.")
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700114
arithmetic172882293fe2021-04-14 11:22:13 -0700115 @mock.patch("google.oauth2.reauth.refresh_grant", autospec=True)
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700116 @mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700117 "google.auth._helpers.utcnow",
118 return_value=datetime.datetime.min + _helpers.CLOCK_SKEW,
119 )
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700120 def test_refresh_success(self, unused_utcnow, refresh_grant):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700121 token = "token"
arithmetic172882293fe2021-04-14 11:22:13 -0700122 new_rapt_token = "new_rapt_token"
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700123 expiry = _helpers.utcnow() + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700124 grant_response = {"id_token": mock.sentinel.id_token}
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700125 refresh_grant.return_value = (
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700126 # Access token
127 token,
128 # New refresh token
129 None,
130 # Expiry,
131 expiry,
132 # Extra data
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700133 grant_response,
arithmetic172882293fe2021-04-14 11:22:13 -0700134 # rapt_token
135 new_rapt_token,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700136 )
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700137
138 request = mock.create_autospec(transport.Request)
139 credentials = self.make_credentials()
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700140
141 # Refresh credentials
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700142 credentials.refresh(request)
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700143
144 # Check jwt grant call.
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700145 refresh_grant.assert_called_with(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700146 request,
147 self.TOKEN_URI,
148 self.REFRESH_TOKEN,
149 self.CLIENT_ID,
150 self.CLIENT_SECRET,
151 None,
arithmetic172882293fe2021-04-14 11:22:13 -0700152 self.RAPT_TOKEN,
arithmetic172813aed5f2021-09-07 16:24:45 -0700153 True,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700154 )
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700155
156 # Check that the credentials have the token and expiry
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700157 assert credentials.token == token
158 assert credentials.expiry == expiry
159 assert credentials.id_token == mock.sentinel.id_token
arithmetic172882293fe2021-04-14 11:22:13 -0700160 assert credentials.rapt_token == new_rapt_token
Jon Wayne Parrott10ec7e92016-10-17 10:46:38 -0700161
162 # Check that the credentials are valid (have a token and are not
163 # expired)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700164 assert credentials.valid
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800165
Thea Flowers118c0482018-05-24 13:34:07 -0700166 def test_refresh_no_refresh_token(self):
167 request = mock.create_autospec(transport.Request)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700168 credentials_ = credentials.Credentials(token=None, refresh_token=None)
Thea Flowers118c0482018-05-24 13:34:07 -0700169
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700170 with pytest.raises(exceptions.RefreshError, match="necessary fields"):
Thea Flowers118c0482018-05-24 13:34:07 -0700171 credentials_.refresh(request)
172
173 request.assert_not_called()
174
arithmetic172882293fe2021-04-14 11:22:13 -0700175 @mock.patch("google.oauth2.reauth.refresh_grant", autospec=True)
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400176 @mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700177 "google.auth._helpers.utcnow",
178 return_value=datetime.datetime.min + _helpers.CLOCK_SKEW,
179 )
bojeil-googleec2fb182021-07-22 10:01:31 -0700180 def test_refresh_with_refresh_token_and_refresh_handler(
181 self, unused_utcnow, refresh_grant
182 ):
183 token = "token"
184 new_rapt_token = "new_rapt_token"
185 expiry = _helpers.utcnow() + datetime.timedelta(seconds=500)
186 grant_response = {"id_token": mock.sentinel.id_token}
187 refresh_grant.return_value = (
188 # Access token
189 token,
190 # New refresh token
191 None,
192 # Expiry,
193 expiry,
194 # Extra data
195 grant_response,
196 # rapt_token
197 new_rapt_token,
198 )
199
200 refresh_handler = mock.Mock()
201 request = mock.create_autospec(transport.Request)
202 creds = credentials.Credentials(
203 token=None,
204 refresh_token=self.REFRESH_TOKEN,
205 token_uri=self.TOKEN_URI,
206 client_id=self.CLIENT_ID,
207 client_secret=self.CLIENT_SECRET,
208 rapt_token=self.RAPT_TOKEN,
209 refresh_handler=refresh_handler,
210 )
211
212 # Refresh credentials
213 creds.refresh(request)
214
215 # Check jwt grant call.
216 refresh_grant.assert_called_with(
217 request,
218 self.TOKEN_URI,
219 self.REFRESH_TOKEN,
220 self.CLIENT_ID,
221 self.CLIENT_SECRET,
222 None,
223 self.RAPT_TOKEN,
arithmetic172813aed5f2021-09-07 16:24:45 -0700224 False,
bojeil-googleec2fb182021-07-22 10:01:31 -0700225 )
226
227 # Check that the credentials have the token and expiry
228 assert creds.token == token
229 assert creds.expiry == expiry
230 assert creds.id_token == mock.sentinel.id_token
231 assert creds.rapt_token == new_rapt_token
232
233 # Check that the credentials are valid (have a token and are not
234 # expired)
235 assert creds.valid
236
237 # Assert refresh handler not called as the refresh token has
238 # higher priority.
239 refresh_handler.assert_not_called()
240
241 @mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
242 def test_refresh_with_refresh_handler_success_scopes(self, unused_utcnow):
243 expected_expiry = datetime.datetime.min + datetime.timedelta(seconds=2800)
244 refresh_handler = mock.Mock(return_value=("ACCESS_TOKEN", expected_expiry))
245 scopes = ["email", "profile"]
246 default_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
247 request = mock.create_autospec(transport.Request)
248 creds = credentials.Credentials(
249 token=None,
250 refresh_token=None,
251 token_uri=None,
252 client_id=None,
253 client_secret=None,
254 rapt_token=None,
255 scopes=scopes,
256 default_scopes=default_scopes,
257 refresh_handler=refresh_handler,
258 )
259
260 creds.refresh(request)
261
262 assert creds.token == "ACCESS_TOKEN"
263 assert creds.expiry == expected_expiry
264 assert creds.valid
265 assert not creds.expired
266 # Confirm refresh handler called with the expected arguments.
267 refresh_handler.assert_called_with(request, scopes=scopes)
268
269 @mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
270 def test_refresh_with_refresh_handler_success_default_scopes(self, unused_utcnow):
271 expected_expiry = datetime.datetime.min + datetime.timedelta(seconds=2800)
272 original_refresh_handler = mock.Mock(
273 return_value=("UNUSED_TOKEN", expected_expiry)
274 )
275 refresh_handler = mock.Mock(return_value=("ACCESS_TOKEN", expected_expiry))
276 default_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
277 request = mock.create_autospec(transport.Request)
278 creds = credentials.Credentials(
279 token=None,
280 refresh_token=None,
281 token_uri=None,
282 client_id=None,
283 client_secret=None,
284 rapt_token=None,
285 scopes=None,
286 default_scopes=default_scopes,
287 refresh_handler=original_refresh_handler,
288 )
289
290 # Test newly set refresh_handler is used instead of the original one.
291 creds.refresh_handler = refresh_handler
292 creds.refresh(request)
293
294 assert creds.token == "ACCESS_TOKEN"
295 assert creds.expiry == expected_expiry
296 assert creds.valid
297 assert not creds.expired
298 # default_scopes should be used since no developer provided scopes
299 # are provided.
300 refresh_handler.assert_called_with(request, scopes=default_scopes)
301
302 @mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
303 def test_refresh_with_refresh_handler_invalid_token(self, unused_utcnow):
304 expected_expiry = datetime.datetime.min + datetime.timedelta(seconds=2800)
305 # Simulate refresh handler does not return a valid token.
306 refresh_handler = mock.Mock(return_value=(None, expected_expiry))
307 scopes = ["email", "profile"]
308 default_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
309 request = mock.create_autospec(transport.Request)
310 creds = credentials.Credentials(
311 token=None,
312 refresh_token=None,
313 token_uri=None,
314 client_id=None,
315 client_secret=None,
316 rapt_token=None,
317 scopes=scopes,
318 default_scopes=default_scopes,
319 refresh_handler=refresh_handler,
320 )
321
322 with pytest.raises(
323 exceptions.RefreshError, match="returned token is not a string"
324 ):
325 creds.refresh(request)
326
327 assert creds.token is None
328 assert creds.expiry is None
329 assert not creds.valid
330 # Confirm refresh handler called with the expected arguments.
331 refresh_handler.assert_called_with(request, scopes=scopes)
332
333 def test_refresh_with_refresh_handler_invalid_expiry(self):
334 # Simulate refresh handler returns expiration time in an invalid unit.
335 refresh_handler = mock.Mock(return_value=("TOKEN", 2800))
336 scopes = ["email", "profile"]
337 default_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
338 request = mock.create_autospec(transport.Request)
339 creds = credentials.Credentials(
340 token=None,
341 refresh_token=None,
342 token_uri=None,
343 client_id=None,
344 client_secret=None,
345 rapt_token=None,
346 scopes=scopes,
347 default_scopes=default_scopes,
348 refresh_handler=refresh_handler,
349 )
350
351 with pytest.raises(
352 exceptions.RefreshError, match="returned expiry is not a datetime object"
353 ):
354 creds.refresh(request)
355
356 assert creds.token is None
357 assert creds.expiry is None
358 assert not creds.valid
359 # Confirm refresh handler called with the expected arguments.
360 refresh_handler.assert_called_with(request, scopes=scopes)
361
362 @mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
363 def test_refresh_with_refresh_handler_expired_token(self, unused_utcnow):
364 expected_expiry = datetime.datetime.min + _helpers.CLOCK_SKEW
365 # Simulate refresh handler returns an expired token.
366 refresh_handler = mock.Mock(return_value=("TOKEN", expected_expiry))
367 scopes = ["email", "profile"]
368 default_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
369 request = mock.create_autospec(transport.Request)
370 creds = credentials.Credentials(
371 token=None,
372 refresh_token=None,
373 token_uri=None,
374 client_id=None,
375 client_secret=None,
376 rapt_token=None,
377 scopes=scopes,
378 default_scopes=default_scopes,
379 refresh_handler=refresh_handler,
380 )
381
382 with pytest.raises(exceptions.RefreshError, match="already expired"):
383 creds.refresh(request)
384
385 assert creds.token is None
386 assert creds.expiry is None
387 assert not creds.valid
388 # Confirm refresh handler called with the expected arguments.
389 refresh_handler.assert_called_with(request, scopes=scopes)
390
391 @mock.patch("google.oauth2.reauth.refresh_grant", autospec=True)
392 @mock.patch(
393 "google.auth._helpers.utcnow",
394 return_value=datetime.datetime.min + _helpers.CLOCK_SKEW,
395 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400396 def test_credentials_with_scopes_requested_refresh_success(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700397 self, unused_utcnow, refresh_grant
398 ):
399 scopes = ["email", "profile"]
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700400 default_scopes = ["https://www.googleapis.com/auth/cloud-platform"]
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700401 token = "token"
arithmetic172882293fe2021-04-14 11:22:13 -0700402 new_rapt_token = "new_rapt_token"
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400403 expiry = _helpers.utcnow() + datetime.timedelta(seconds=500)
arithmetic172882293fe2021-04-14 11:22:13 -0700404 grant_response = {"id_token": mock.sentinel.id_token, "scope": "email profile"}
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400405 refresh_grant.return_value = (
406 # Access token
407 token,
408 # New refresh token
409 None,
410 # Expiry,
411 expiry,
412 # Extra data
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700413 grant_response,
arithmetic172882293fe2021-04-14 11:22:13 -0700414 # rapt token
415 new_rapt_token,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700416 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400417
418 request = mock.create_autospec(transport.Request)
419 creds = credentials.Credentials(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700420 token=None,
421 refresh_token=self.REFRESH_TOKEN,
422 token_uri=self.TOKEN_URI,
423 client_id=self.CLIENT_ID,
424 client_secret=self.CLIENT_SECRET,
425 scopes=scopes,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700426 default_scopes=default_scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700427 rapt_token=self.RAPT_TOKEN,
arithmetic172813aed5f2021-09-07 16:24:45 -0700428 enable_reauth_refresh=True,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700429 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400430
431 # Refresh credentials
432 creds.refresh(request)
433
434 # Check jwt grant call.
435 refresh_grant.assert_called_with(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700436 request,
437 self.TOKEN_URI,
438 self.REFRESH_TOKEN,
439 self.CLIENT_ID,
440 self.CLIENT_SECRET,
441 scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700442 self.RAPT_TOKEN,
arithmetic172813aed5f2021-09-07 16:24:45 -0700443 True,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700444 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400445
446 # Check that the credentials have the token and expiry
447 assert creds.token == token
448 assert creds.expiry == expiry
449 assert creds.id_token == mock.sentinel.id_token
450 assert creds.has_scopes(scopes)
arithmetic172882293fe2021-04-14 11:22:13 -0700451 assert creds.rapt_token == new_rapt_token
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400452
453 # Check that the credentials are valid (have a token and are not
454 # expired.)
455 assert creds.valid
456
arithmetic172882293fe2021-04-14 11:22:13 -0700457 @mock.patch("google.oauth2.reauth.refresh_grant", autospec=True)
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400458 @mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700459 "google.auth._helpers.utcnow",
460 return_value=datetime.datetime.min + _helpers.CLOCK_SKEW,
461 )
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700462 def test_credentials_with_only_default_scopes_requested(
463 self, unused_utcnow, refresh_grant
464 ):
465 default_scopes = ["email", "profile"]
466 token = "token"
arithmetic172882293fe2021-04-14 11:22:13 -0700467 new_rapt_token = "new_rapt_token"
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700468 expiry = _helpers.utcnow() + datetime.timedelta(seconds=500)
469 grant_response = {"id_token": mock.sentinel.id_token}
470 refresh_grant.return_value = (
471 # Access token
472 token,
473 # New refresh token
474 None,
475 # Expiry,
476 expiry,
477 # Extra data
478 grant_response,
arithmetic172882293fe2021-04-14 11:22:13 -0700479 # rapt token
480 new_rapt_token,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700481 )
482
483 request = mock.create_autospec(transport.Request)
484 creds = credentials.Credentials(
485 token=None,
486 refresh_token=self.REFRESH_TOKEN,
487 token_uri=self.TOKEN_URI,
488 client_id=self.CLIENT_ID,
489 client_secret=self.CLIENT_SECRET,
490 default_scopes=default_scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700491 rapt_token=self.RAPT_TOKEN,
arithmetic172813aed5f2021-09-07 16:24:45 -0700492 enable_reauth_refresh=True,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700493 )
494
495 # Refresh credentials
496 creds.refresh(request)
497
498 # Check jwt grant call.
499 refresh_grant.assert_called_with(
500 request,
501 self.TOKEN_URI,
502 self.REFRESH_TOKEN,
503 self.CLIENT_ID,
504 self.CLIENT_SECRET,
505 default_scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700506 self.RAPT_TOKEN,
arithmetic172813aed5f2021-09-07 16:24:45 -0700507 True,
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700508 )
509
510 # Check that the credentials have the token and expiry
511 assert creds.token == token
512 assert creds.expiry == expiry
513 assert creds.id_token == mock.sentinel.id_token
514 assert creds.has_scopes(default_scopes)
arithmetic172882293fe2021-04-14 11:22:13 -0700515 assert creds.rapt_token == new_rapt_token
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700516
517 # Check that the credentials are valid (have a token and are not
518 # expired.)
519 assert creds.valid
520
arithmetic172882293fe2021-04-14 11:22:13 -0700521 @mock.patch("google.oauth2.reauth.refresh_grant", autospec=True)
Bu Sun Kimbf5ce0c2021-02-01 15:17:49 -0700522 @mock.patch(
523 "google.auth._helpers.utcnow",
524 return_value=datetime.datetime.min + _helpers.CLOCK_SKEW,
525 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400526 def test_credentials_with_scopes_returned_refresh_success(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700527 self, unused_utcnow, refresh_grant
528 ):
529 scopes = ["email", "profile"]
530 token = "token"
arithmetic172882293fe2021-04-14 11:22:13 -0700531 new_rapt_token = "new_rapt_token"
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400532 expiry = _helpers.utcnow() + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700533 grant_response = {
534 "id_token": mock.sentinel.id_token,
535 "scopes": " ".join(scopes),
536 }
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400537 refresh_grant.return_value = (
538 # Access token
539 token,
540 # New refresh token
541 None,
542 # Expiry,
543 expiry,
544 # Extra data
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700545 grant_response,
arithmetic172882293fe2021-04-14 11:22:13 -0700546 # rapt token
547 new_rapt_token,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700548 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400549
550 request = mock.create_autospec(transport.Request)
551 creds = credentials.Credentials(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700552 token=None,
553 refresh_token=self.REFRESH_TOKEN,
554 token_uri=self.TOKEN_URI,
555 client_id=self.CLIENT_ID,
556 client_secret=self.CLIENT_SECRET,
557 scopes=scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700558 rapt_token=self.RAPT_TOKEN,
arithmetic172813aed5f2021-09-07 16:24:45 -0700559 enable_reauth_refresh=True,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700560 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400561
562 # Refresh credentials
563 creds.refresh(request)
564
565 # Check jwt grant call.
566 refresh_grant.assert_called_with(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700567 request,
568 self.TOKEN_URI,
569 self.REFRESH_TOKEN,
570 self.CLIENT_ID,
571 self.CLIENT_SECRET,
572 scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700573 self.RAPT_TOKEN,
arithmetic172813aed5f2021-09-07 16:24:45 -0700574 True,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700575 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400576
577 # Check that the credentials have the token and expiry
578 assert creds.token == token
579 assert creds.expiry == expiry
580 assert creds.id_token == mock.sentinel.id_token
581 assert creds.has_scopes(scopes)
arithmetic172882293fe2021-04-14 11:22:13 -0700582 assert creds.rapt_token == new_rapt_token
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400583
584 # Check that the credentials are valid (have a token and are not
585 # expired.)
586 assert creds.valid
587
arithmetic172882293fe2021-04-14 11:22:13 -0700588 @mock.patch("google.oauth2.reauth.refresh_grant", autospec=True)
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400589 @mock.patch(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700590 "google.auth._helpers.utcnow",
591 return_value=datetime.datetime.min + _helpers.CLOCK_SKEW,
592 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400593 def test_credentials_with_scopes_refresh_failure_raises_refresh_error(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700594 self, unused_utcnow, refresh_grant
595 ):
596 scopes = ["email", "profile"]
597 scopes_returned = ["email"]
598 token = "token"
arithmetic172882293fe2021-04-14 11:22:13 -0700599 new_rapt_token = "new_rapt_token"
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400600 expiry = _helpers.utcnow() + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700601 grant_response = {
602 "id_token": mock.sentinel.id_token,
arithmetic172882293fe2021-04-14 11:22:13 -0700603 "scope": " ".join(scopes_returned),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700604 }
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400605 refresh_grant.return_value = (
606 # Access token
607 token,
608 # New refresh token
609 None,
610 # Expiry,
611 expiry,
612 # Extra data
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700613 grant_response,
arithmetic172882293fe2021-04-14 11:22:13 -0700614 # rapt token
615 new_rapt_token,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700616 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400617
618 request = mock.create_autospec(transport.Request)
619 creds = credentials.Credentials(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700620 token=None,
621 refresh_token=self.REFRESH_TOKEN,
622 token_uri=self.TOKEN_URI,
623 client_id=self.CLIENT_ID,
624 client_secret=self.CLIENT_SECRET,
625 scopes=scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700626 rapt_token=self.RAPT_TOKEN,
arithmetic172813aed5f2021-09-07 16:24:45 -0700627 enable_reauth_refresh=True,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700628 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400629
630 # Refresh credentials
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700631 with pytest.raises(
632 exceptions.RefreshError, match="Not all requested scopes were granted"
633 ):
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400634 creds.refresh(request)
635
636 # Check jwt grant call.
637 refresh_grant.assert_called_with(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700638 request,
639 self.TOKEN_URI,
640 self.REFRESH_TOKEN,
641 self.CLIENT_ID,
642 self.CLIENT_SECRET,
643 scopes,
arithmetic172882293fe2021-04-14 11:22:13 -0700644 self.RAPT_TOKEN,
arithmetic172813aed5f2021-09-07 16:24:45 -0700645 True,
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700646 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400647
648 # Check that the credentials have the token and expiry
649 assert creds.token == token
650 assert creds.expiry == expiry
651 assert creds.id_token == mock.sentinel.id_token
652 assert creds.has_scopes(scopes)
arithmetic172882293fe2021-04-14 11:22:13 -0700653 assert creds.rapt_token == new_rapt_token
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400654
655 # Check that the credentials are valid (have a token and are not
656 # expired.)
657 assert creds.valid
658
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800659 def test_apply_with_quota_project_id(self):
660 creds = credentials.Credentials(
661 token="token",
662 refresh_token=self.REFRESH_TOKEN,
663 token_uri=self.TOKEN_URI,
664 client_id=self.CLIENT_ID,
665 client_secret=self.CLIENT_SECRET,
666 quota_project_id="quota-project-123",
667 )
668
669 headers = {}
670 creds.apply(headers)
671 assert headers["x-goog-user-project"] == "quota-project-123"
Bu Sun Kim18d5ae62020-07-21 13:44:03 -0700672 assert "token" in headers["authorization"]
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800673
674 def test_apply_with_no_quota_project_id(self):
675 creds = credentials.Credentials(
676 token="token",
677 refresh_token=self.REFRESH_TOKEN,
678 token_uri=self.TOKEN_URI,
679 client_id=self.CLIENT_ID,
680 client_secret=self.CLIENT_SECRET,
681 )
682
683 headers = {}
684 creds.apply(headers)
685 assert "x-goog-user-project" not in headers
Bu Sun Kim18d5ae62020-07-21 13:44:03 -0700686 assert "token" in headers["authorization"]
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800687
Bu Sun Kimb12488c2020-06-10 13:44:07 -0700688 def test_with_quota_project(self):
689 creds = credentials.Credentials(
690 token="token",
691 refresh_token=self.REFRESH_TOKEN,
692 token_uri=self.TOKEN_URI,
693 client_id=self.CLIENT_ID,
694 client_secret=self.CLIENT_SECRET,
695 quota_project_id="quota-project-123",
696 )
697
698 new_creds = creds.with_quota_project("new-project-456")
699 assert new_creds.quota_project_id == "new-project-456"
700 headers = {}
701 creds.apply(headers)
702 assert "x-goog-user-project" in headers
703
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800704 def test_from_authorized_user_info(self):
705 info = AUTH_USER_INFO.copy()
706
707 creds = credentials.Credentials.from_authorized_user_info(info)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700708 assert creds.client_secret == info["client_secret"]
709 assert creds.client_id == info["client_id"]
710 assert creds.refresh_token == info["refresh_token"]
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800711 assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT
712 assert creds.scopes is None
713
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700714 scopes = ["email", "profile"]
715 creds = credentials.Credentials.from_authorized_user_info(info, scopes)
716 assert creds.client_secret == info["client_secret"]
717 assert creds.client_id == info["client_id"]
718 assert creds.refresh_token == info["refresh_token"]
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800719 assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT
720 assert creds.scopes == scopes
721
wesley chund0e0aba2020-09-17 09:18:55 -0700722 info["scopes"] = "email" # single non-array scope from file
723 creds = credentials.Credentials.from_authorized_user_info(info)
724 assert creds.scopes == [info["scopes"]]
725
726 info["scopes"] = ["email", "profile"] # array scope from file
727 creds = credentials.Credentials.from_authorized_user_info(info)
728 assert creds.scopes == info["scopes"]
729
730 expiry = datetime.datetime(2020, 8, 14, 15, 54, 1)
731 info["expiry"] = expiry.isoformat() + "Z"
732 creds = credentials.Credentials.from_authorized_user_info(info)
733 assert creds.expiry == expiry
734 assert creds.expired
735
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800736 def test_from_authorized_user_file(self):
737 info = AUTH_USER_INFO.copy()
738
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700739 creds = credentials.Credentials.from_authorized_user_file(AUTH_USER_JSON_FILE)
740 assert creds.client_secret == info["client_secret"]
741 assert creds.client_id == info["client_id"]
742 assert creds.refresh_token == info["refresh_token"]
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800743 assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT
744 assert creds.scopes is None
arithmetic172813aed5f2021-09-07 16:24:45 -0700745 assert creds.rapt_token is None
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800746
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700747 scopes = ["email", "profile"]
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800748 creds = credentials.Credentials.from_authorized_user_file(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700749 AUTH_USER_JSON_FILE, scopes
750 )
751 assert creds.client_secret == info["client_secret"]
752 assert creds.client_id == info["client_id"]
753 assert creds.refresh_token == info["refresh_token"]
Hiranya Jayathilaka23c88f72017-12-05 09:29:59 -0800754 assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT
755 assert creds.scopes == scopes
patkasperbfb1f8c2019-12-05 22:03:44 +0100756
arithmetic172813aed5f2021-09-07 16:24:45 -0700757 def test_from_authorized_user_file_with_rapt_token(self):
758 info = AUTH_USER_INFO.copy()
759 file_path = os.path.join(DATA_DIR, "authorized_user_with_rapt_token.json")
760
761 creds = credentials.Credentials.from_authorized_user_file(file_path)
762 assert creds.client_secret == info["client_secret"]
763 assert creds.client_id == info["client_id"]
764 assert creds.refresh_token == info["refresh_token"]
765 assert creds.token_uri == credentials._GOOGLE_OAUTH2_TOKEN_ENDPOINT
766 assert creds.scopes is None
767 assert creds.rapt_token == "rapt"
768
patkasperbfb1f8c2019-12-05 22:03:44 +0100769 def test_to_json(self):
770 info = AUTH_USER_INFO.copy()
wesley chund0e0aba2020-09-17 09:18:55 -0700771 expiry = datetime.datetime(2020, 8, 14, 15, 54, 1)
772 info["expiry"] = expiry.isoformat() + "Z"
patkasperbfb1f8c2019-12-05 22:03:44 +0100773 creds = credentials.Credentials.from_authorized_user_info(info)
wesley chund0e0aba2020-09-17 09:18:55 -0700774 assert creds.expiry == expiry
patkasperbfb1f8c2019-12-05 22:03:44 +0100775
776 # Test with no `strip` arg
777 json_output = creds.to_json()
778 json_asdict = json.loads(json_output)
779 assert json_asdict.get("token") == creds.token
780 assert json_asdict.get("refresh_token") == creds.refresh_token
781 assert json_asdict.get("token_uri") == creds.token_uri
782 assert json_asdict.get("client_id") == creds.client_id
783 assert json_asdict.get("scopes") == creds.scopes
784 assert json_asdict.get("client_secret") == creds.client_secret
wesley chund0e0aba2020-09-17 09:18:55 -0700785 assert json_asdict.get("expiry") == info["expiry"]
patkasperbfb1f8c2019-12-05 22:03:44 +0100786
787 # Test with a `strip` arg
788 json_output = creds.to_json(strip=["client_secret"])
789 json_asdict = json.loads(json_output)
790 assert json_asdict.get("token") == creds.token
791 assert json_asdict.get("refresh_token") == creds.refresh_token
792 assert json_asdict.get("token_uri") == creds.token_uri
793 assert json_asdict.get("client_id") == creds.client_id
794 assert json_asdict.get("scopes") == creds.scopes
795 assert json_asdict.get("client_secret") is None
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800796
wesley chund0e0aba2020-09-17 09:18:55 -0700797 # Test with no expiry
798 creds.expiry = None
799 json_output = creds.to_json()
800 json_asdict = json.loads(json_output)
801 assert json_asdict.get("expiry") is None
802
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800803 def test_pickle_and_unpickle(self):
804 creds = self.make_credentials()
805 unpickled = pickle.loads(pickle.dumps(creds))
806
807 # make sure attributes aren't lost during pickling
808 assert list(creds.__dict__).sort() == list(unpickled.__dict__).sort()
809
810 for attr in list(creds.__dict__):
811 assert getattr(creds, attr) == getattr(unpickled, attr)
812
bojeil-googleec2fb182021-07-22 10:01:31 -0700813 def test_pickle_and_unpickle_with_refresh_handler(self):
814 expected_expiry = _helpers.utcnow() + datetime.timedelta(seconds=2800)
815 refresh_handler = mock.Mock(return_value=("TOKEN", expected_expiry))
816
817 creds = credentials.Credentials(
818 token=None,
819 refresh_token=None,
820 token_uri=None,
821 client_id=None,
822 client_secret=None,
823 rapt_token=None,
824 refresh_handler=refresh_handler,
825 )
826 unpickled = pickle.loads(pickle.dumps(creds))
827
828 # make sure attributes aren't lost during pickling
829 assert list(creds.__dict__).sort() == list(unpickled.__dict__).sort()
830
831 for attr in list(creds.__dict__):
832 # For the _refresh_handler property, the unpickled creds should be
833 # set to None.
834 if attr == "_refresh_handler":
835 assert getattr(unpickled, attr) is None
836 else:
837 assert getattr(creds, attr) == getattr(unpickled, attr)
838
Bu Sun Kim32d71a52019-12-18 11:30:46 -0800839 def test_pickle_with_missing_attribute(self):
840 creds = self.make_credentials()
841
842 # remove an optional attribute before pickling
843 # this mimics a pickle created with a previous class definition with
844 # fewer attributes
845 del creds.__dict__["_quota_project_id"]
846
847 unpickled = pickle.loads(pickle.dumps(creds))
848
849 # Attribute should be initialized by `__setstate__`
850 assert unpickled.quota_project_id is None
851
852 # pickles are not compatible across versions
853 @pytest.mark.skipif(
854 sys.version_info < (3, 5),
855 reason="pickle file can only be loaded with Python >= 3.5",
856 )
857 def test_unpickle_old_credentials_pickle(self):
858 # make sure a credentials file pickled with an older
859 # library version (google-auth==1.5.1) can be unpickled
860 with open(
861 os.path.join(DATA_DIR, "old_oauth_credentials_py3.pickle"), "rb"
862 ) as f:
863 credentials = pickle.load(f)
864 assert credentials.quota_project_id is None
arithmetic1728772dac62020-03-27 14:34:13 -0700865
866
867class TestUserAccessTokenCredentials(object):
868 def test_instance(self):
869 cred = credentials.UserAccessTokenCredentials()
870 assert cred._account is None
871
872 cred = cred.with_account("account")
873 assert cred._account == "account"
874
875 @mock.patch("google.auth._cloud_sdk.get_auth_access_token", autospec=True)
876 def test_refresh(self, get_auth_access_token):
877 get_auth_access_token.return_value = "access_token"
878 cred = credentials.UserAccessTokenCredentials()
879 cred.refresh(None)
880 assert cred.token == "access_token"
881
Bu Sun Kim3dda7b22020-07-09 10:39:39 -0700882 def test_with_quota_project(self):
883 cred = credentials.UserAccessTokenCredentials()
884 quota_project_cred = cred.with_quota_project("project-foo")
885
886 assert quota_project_cred._quota_project_id == "project-foo"
887 assert quota_project_cred._account == cred._account
888
arithmetic1728772dac62020-03-27 14:34:13 -0700889 @mock.patch(
890 "google.oauth2.credentials.UserAccessTokenCredentials.apply", autospec=True
891 )
892 @mock.patch(
893 "google.oauth2.credentials.UserAccessTokenCredentials.refresh", autospec=True
894 )
895 def test_before_request(self, refresh, apply):
896 cred = credentials.UserAccessTokenCredentials()
897 cred.before_request(mock.Mock(), "GET", "https://example.com", {})
898 refresh.assert_called()
899 apply.assert_called()