blob: 54686df594bd57efa99c7ec4ea407fb2e0baae9e [file] [log] [blame]
C.J. Collier37141e42020-02-13 13:49:49 -08001# Copyright 2016 Google LLC
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -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
16import json
Christophe Tatonb649b432018-02-08 14:12:23 -080017import os
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070018
19import mock
20import pytest
21import six
22from six.moves import http_client
23from six.moves import urllib
24
Christophe Tatonb649b432018-02-08 14:12:23 -080025from google.auth import _helpers
26from google.auth import crypt
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070027from google.auth import exceptions
Christophe Tatonb649b432018-02-08 14:12:23 -080028from google.auth import jwt
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070029from google.auth import transport
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070030from google.oauth2 import _client
31
32
Bu Sun Kim9eec0912019-10-21 17:04:21 -070033DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data")
Christophe Tatonb649b432018-02-08 14:12:23 -080034
Bu Sun Kim9eec0912019-10-21 17:04:21 -070035with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh:
Christophe Tatonb649b432018-02-08 14:12:23 -080036 PRIVATE_KEY_BYTES = fh.read()
37
Bu Sun Kim9eec0912019-10-21 17:04:21 -070038SIGNER = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, "1")
Christophe Tatonb649b432018-02-08 14:12:23 -080039
Bu Sun Kim9eec0912019-10-21 17:04:21 -070040SCOPES_AS_LIST = [
41 "https://www.googleapis.com/auth/pubsub",
42 "https://www.googleapis.com/auth/logging.write",
43]
44SCOPES_AS_STRING = (
45 "https://www.googleapis.com/auth/pubsub"
46 " https://www.googleapis.com/auth/logging.write"
47)
Eugene W. Foley49a18c42019-05-22 13:50:38 -040048
Christophe Tatonb649b432018-02-08 14:12:23 -080049
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070050def test__handle_error_response():
arithmetic172882293fe2021-04-14 11:22:13 -070051 response_data = {"error": "help", "error_description": "I'm alive"}
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070052
53 with pytest.raises(exceptions.RefreshError) as excinfo:
54 _client._handle_error_response(response_data)
55
Bu Sun Kim9eec0912019-10-21 17:04:21 -070056 assert excinfo.match(r"help: I\'m alive")
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070057
58
59def test__handle_error_response_non_json():
arithmetic172882293fe2021-04-14 11:22:13 -070060 response_data = {"foo": "bar"}
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070061
62 with pytest.raises(exceptions.RefreshError) as excinfo:
63 _client._handle_error_response(response_data)
64
arithmetic172882293fe2021-04-14 11:22:13 -070065 assert excinfo.match(r"{\"foo\": \"bar\"}")
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070066
67
Bu Sun Kim9eec0912019-10-21 17:04:21 -070068@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070069def test__parse_expiry(unused_utcnow):
Bu Sun Kim9eec0912019-10-21 17:04:21 -070070 result = _client._parse_expiry({"expires_in": 500})
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070071 assert result == datetime.datetime.min + datetime.timedelta(seconds=500)
72
73
74def test__parse_expiry_none():
75 assert _client._parse_expiry({}) is None
76
77
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070078def make_request(response_data, status=http_client.OK):
79 response = mock.create_autospec(transport.Response, instance=True)
80 response.status = status
Bu Sun Kim9eec0912019-10-21 17:04:21 -070081 response.data = json.dumps(response_data).encode("utf-8")
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070082 request = mock.create_autospec(transport.Request)
83 request.return_value = response
84 return request
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070085
86
87def test__token_endpoint_request():
Bu Sun Kim9eec0912019-10-21 17:04:21 -070088 request = make_request({"test": "response"})
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070089
90 result = _client._token_endpoint_request(
Bu Sun Kim9eec0912019-10-21 17:04:21 -070091 request, "http://example.com", {"test": "params"}
92 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070093
94 # Check request call
95 request.assert_called_with(
Bu Sun Kim9eec0912019-10-21 17:04:21 -070096 method="POST",
97 url="http://example.com",
arithmetic172882293fe2021-04-14 11:22:13 -070098 headers={"Content-Type": "application/x-www-form-urlencoded"},
Bu Sun Kima57a7702020-01-10 13:17:34 -080099 body="test=params".encode("utf-8"),
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700100 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700101
102 # Check result
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700103 assert result == {"test": "response"}
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700104
105
arithmetic172882293fe2021-04-14 11:22:13 -0700106def test__token_endpoint_request_use_json():
107 request = make_request({"test": "response"})
108
109 result = _client._token_endpoint_request(
110 request,
111 "http://example.com",
112 {"test": "params"},
113 access_token="access_token",
114 use_json=True,
115 )
116
117 # Check request call
118 request.assert_called_with(
119 method="POST",
120 url="http://example.com",
121 headers={
122 "Content-Type": "application/json",
123 "Authorization": "Bearer access_token",
124 },
125 body=b'{"test": "params"}',
126 )
127
128 # Check result
129 assert result == {"test": "response"}
130
131
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700132def test__token_endpoint_request_error():
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700133 request = make_request({}, status=http_client.BAD_REQUEST)
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700134
135 with pytest.raises(exceptions.RefreshError):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700136 _client._token_endpoint_request(request, "http://example.com", {})
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700137
138
Anjali Doneriaeae1dcb2019-09-09 16:36:10 -0700139def test__token_endpoint_request_internal_failure_error():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700140 request = make_request(
Georgy Savva46bb58e2019-11-13 22:21:57 +0300141 {"error_description": "internal_failure"}, status=http_client.BAD_REQUEST
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700142 )
Anjali Doneriaeae1dcb2019-09-09 16:36:10 -0700143
144 with pytest.raises(exceptions.RefreshError):
145 _client._token_endpoint_request(
Georgy Savva46bb58e2019-11-13 22:21:57 +0300146 request, "http://example.com", {"error_description": "internal_failure"}
147 )
148
149 request = make_request(
150 {"error": "internal_failure"}, status=http_client.BAD_REQUEST
151 )
152
153 with pytest.raises(exceptions.RefreshError):
154 _client._token_endpoint_request(
155 request, "http://example.com", {"error": "internal_failure"}
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700156 )
Anjali Doneriaeae1dcb2019-09-09 16:36:10 -0700157
158
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700159def verify_request_params(request, params):
Bu Sun Kima57a7702020-01-10 13:17:34 -0800160 request_body = request.call_args[1]["body"].decode("utf-8")
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700161 request_params = urllib.parse.parse_qs(request_body)
162
163 for key, value in six.iteritems(params):
164 assert request_params[key][0] == value
165
166
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700167@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700168def test_jwt_grant(utcnow):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700169 request = make_request(
170 {"access_token": "token", "expires_in": 500, "extra": "data"}
171 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700172
173 token, expiry, extra_data = _client.jwt_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700174 request, "http://example.com", "assertion_value"
175 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700176
177 # Check request call
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700178 verify_request_params(
179 request, {"grant_type": _client._JWT_GRANT_TYPE, "assertion": "assertion_value"}
180 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700181
182 # Check result
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700183 assert token == "token"
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700184 assert expiry == utcnow() + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700185 assert extra_data["extra"] == "data"
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700186
187
188def test_jwt_grant_no_access_token():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700189 request = make_request(
190 {
191 # No access token.
192 "expires_in": 500,
193 "extra": "data",
194 }
195 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700196
197 with pytest.raises(exceptions.RefreshError):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700198 _client.jwt_grant(request, "http://example.com", "assertion_value")
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700199
200
Christophe Tatonb649b432018-02-08 14:12:23 -0800201def test_id_token_jwt_grant():
202 now = _helpers.utcnow()
203 id_token_expiry = _helpers.datetime_to_secs(now)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700204 id_token = jwt.encode(SIGNER, {"exp": id_token_expiry}).decode("utf-8")
205 request = make_request({"id_token": id_token, "extra": "data"})
Christophe Tatonb649b432018-02-08 14:12:23 -0800206
207 token, expiry, extra_data = _client.id_token_jwt_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700208 request, "http://example.com", "assertion_value"
209 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800210
211 # Check request call
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700212 verify_request_params(
213 request, {"grant_type": _client._JWT_GRANT_TYPE, "assertion": "assertion_value"}
214 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800215
216 # Check result
217 assert token == id_token
218 # JWT does not store microseconds
219 now = now.replace(microsecond=0)
220 assert expiry == now
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700221 assert extra_data["extra"] == "data"
Christophe Tatonb649b432018-02-08 14:12:23 -0800222
223
224def test_id_token_jwt_grant_no_access_token():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700225 request = make_request(
226 {
227 # No access token.
228 "expires_in": 500,
229 "extra": "data",
230 }
231 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800232
233 with pytest.raises(exceptions.RefreshError):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700234 _client.id_token_jwt_grant(request, "http://example.com", "assertion_value")
Christophe Tatonb649b432018-02-08 14:12:23 -0800235
236
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700237@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700238def test_refresh_grant(unused_utcnow):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700239 request = make_request(
240 {
241 "access_token": "token",
242 "refresh_token": "new_refresh_token",
243 "expires_in": 500,
244 "extra": "data",
245 }
246 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700247
248 token, refresh_token, expiry, extra_data = _client.refresh_grant(
arithmetic172882293fe2021-04-14 11:22:13 -0700249 request,
250 "http://example.com",
251 "refresh_token",
252 "client_id",
253 "client_secret",
254 rapt_token="rapt_token",
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700255 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700256
257 # Check request call
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700258 verify_request_params(
259 request,
260 {
261 "grant_type": _client._REFRESH_GRANT_TYPE,
262 "refresh_token": "refresh_token",
263 "client_id": "client_id",
264 "client_secret": "client_secret",
arithmetic172882293fe2021-04-14 11:22:13 -0700265 "rapt": "rapt_token",
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700266 },
267 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700268
269 # Check result
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700270 assert token == "token"
271 assert refresh_token == "new_refresh_token"
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700272 assert expiry == datetime.datetime.min + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700273 assert extra_data["extra"] == "data"
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700274
275
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700276@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400277def test_refresh_grant_with_scopes(unused_utcnow):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700278 request = make_request(
279 {
280 "access_token": "token",
281 "refresh_token": "new_refresh_token",
282 "expires_in": 500,
283 "extra": "data",
284 "scope": SCOPES_AS_STRING,
285 }
286 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400287
288 token, refresh_token, expiry, extra_data = _client.refresh_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700289 request,
290 "http://example.com",
291 "refresh_token",
292 "client_id",
293 "client_secret",
294 SCOPES_AS_LIST,
295 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400296
297 # Check request call.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700298 verify_request_params(
299 request,
300 {
301 "grant_type": _client._REFRESH_GRANT_TYPE,
302 "refresh_token": "refresh_token",
303 "client_id": "client_id",
304 "client_secret": "client_secret",
305 "scope": SCOPES_AS_STRING,
306 },
307 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400308
309 # Check result.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700310 assert token == "token"
311 assert refresh_token == "new_refresh_token"
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400312 assert expiry == datetime.datetime.min + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700313 assert extra_data["extra"] == "data"
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400314
315
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700316def test_refresh_grant_no_access_token():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700317 request = make_request(
318 {
319 # No access token.
320 "refresh_token": "new_refresh_token",
321 "expires_in": 500,
322 "extra": "data",
323 }
324 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700325
326 with pytest.raises(exceptions.RefreshError):
327 _client.refresh_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700328 request, "http://example.com", "refresh_token", "client_id", "client_secret"
329 )