blob: 690a87bc47e159504763ee3ac89a80d5fa6ca51c [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
Tres Seaver560cf1e2021-08-03 16:35:54 -040016import http.client
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070017import json
Christophe Tatonb649b432018-02-08 14:12:23 -080018import os
Tres Seaver560cf1e2021-08-03 16:35:54 -040019import urllib
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070020
21import mock
22import pytest
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070023
Christophe Tatonb649b432018-02-08 14:12:23 -080024from google.auth import _helpers
25from google.auth import crypt
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070026from google.auth import exceptions
Christophe Tatonb649b432018-02-08 14:12:23 -080027from google.auth import jwt
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070028from google.auth import transport
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070029from google.oauth2 import _client
30
31
Bu Sun Kim9eec0912019-10-21 17:04:21 -070032DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data")
Christophe Tatonb649b432018-02-08 14:12:23 -080033
Bu Sun Kim9eec0912019-10-21 17:04:21 -070034with open(os.path.join(DATA_DIR, "privatekey.pem"), "rb") as fh:
Christophe Tatonb649b432018-02-08 14:12:23 -080035 PRIVATE_KEY_BYTES = fh.read()
36
Bu Sun Kim9eec0912019-10-21 17:04:21 -070037SIGNER = crypt.RSASigner.from_string(PRIVATE_KEY_BYTES, "1")
Christophe Tatonb649b432018-02-08 14:12:23 -080038
Bu Sun Kim9eec0912019-10-21 17:04:21 -070039SCOPES_AS_LIST = [
40 "https://www.googleapis.com/auth/pubsub",
41 "https://www.googleapis.com/auth/logging.write",
42]
43SCOPES_AS_STRING = (
44 "https://www.googleapis.com/auth/pubsub"
45 " https://www.googleapis.com/auth/logging.write"
46)
Eugene W. Foley49a18c42019-05-22 13:50:38 -040047
Christophe Tatonb649b432018-02-08 14:12:23 -080048
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070049def test__handle_error_response():
arithmetic172882293fe2021-04-14 11:22:13 -070050 response_data = {"error": "help", "error_description": "I'm alive"}
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070051
52 with pytest.raises(exceptions.RefreshError) as excinfo:
53 _client._handle_error_response(response_data)
54
Bu Sun Kim9eec0912019-10-21 17:04:21 -070055 assert excinfo.match(r"help: I\'m alive")
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070056
57
58def test__handle_error_response_non_json():
arithmetic172882293fe2021-04-14 11:22:13 -070059 response_data = {"foo": "bar"}
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070060
61 with pytest.raises(exceptions.RefreshError) as excinfo:
62 _client._handle_error_response(response_data)
63
arithmetic172882293fe2021-04-14 11:22:13 -070064 assert excinfo.match(r"{\"foo\": \"bar\"}")
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070065
66
Bu Sun Kim9eec0912019-10-21 17:04:21 -070067@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070068def test__parse_expiry(unused_utcnow):
Bu Sun Kim9eec0912019-10-21 17:04:21 -070069 result = _client._parse_expiry({"expires_in": 500})
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070070 assert result == datetime.datetime.min + datetime.timedelta(seconds=500)
71
72
73def test__parse_expiry_none():
74 assert _client._parse_expiry({}) is None
75
76
Tres Seaver560cf1e2021-08-03 16:35:54 -040077def make_request(response_data, status=http.client.OK):
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070078 response = mock.create_autospec(transport.Response, instance=True)
79 response.status = status
Bu Sun Kim9eec0912019-10-21 17:04:21 -070080 response.data = json.dumps(response_data).encode("utf-8")
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -070081 request = mock.create_autospec(transport.Request)
82 request.return_value = response
83 return request
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070084
85
86def test__token_endpoint_request():
Bu Sun Kim9eec0912019-10-21 17:04:21 -070087 request = make_request({"test": "response"})
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070088
89 result = _client._token_endpoint_request(
Bu Sun Kim9eec0912019-10-21 17:04:21 -070090 request, "http://example.com", {"test": "params"}
91 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -070092
93 # Check request call
94 request.assert_called_with(
Bu Sun Kim9eec0912019-10-21 17:04:21 -070095 method="POST",
96 url="http://example.com",
arithmetic172882293fe2021-04-14 11:22:13 -070097 headers={"Content-Type": "application/x-www-form-urlencoded"},
Bu Sun Kima57a7702020-01-10 13:17:34 -080098 body="test=params".encode("utf-8"),
Bu Sun Kim9eec0912019-10-21 17:04:21 -070099 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700100
101 # Check result
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700102 assert result == {"test": "response"}
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700103
104
arithmetic172882293fe2021-04-14 11:22:13 -0700105def test__token_endpoint_request_use_json():
106 request = make_request({"test": "response"})
107
108 result = _client._token_endpoint_request(
109 request,
110 "http://example.com",
111 {"test": "params"},
112 access_token="access_token",
113 use_json=True,
114 )
115
116 # Check request call
117 request.assert_called_with(
118 method="POST",
119 url="http://example.com",
120 headers={
121 "Content-Type": "application/json",
122 "Authorization": "Bearer access_token",
123 },
124 body=b'{"test": "params"}',
125 )
126
127 # Check result
128 assert result == {"test": "response"}
129
130
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700131def test__token_endpoint_request_error():
Tres Seaver560cf1e2021-08-03 16:35:54 -0400132 request = make_request({}, status=http.client.BAD_REQUEST)
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700133
134 with pytest.raises(exceptions.RefreshError):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700135 _client._token_endpoint_request(request, "http://example.com", {})
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700136
137
Anjali Doneriaeae1dcb2019-09-09 16:36:10 -0700138def test__token_endpoint_request_internal_failure_error():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700139 request = make_request(
Tres Seaver560cf1e2021-08-03 16:35:54 -0400140 {"error_description": "internal_failure"}, status=http.client.BAD_REQUEST
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700141 )
Anjali Doneriaeae1dcb2019-09-09 16:36:10 -0700142
143 with pytest.raises(exceptions.RefreshError):
144 _client._token_endpoint_request(
Georgy Savva46bb58e2019-11-13 22:21:57 +0300145 request, "http://example.com", {"error_description": "internal_failure"}
146 )
147
148 request = make_request(
Tres Seaver560cf1e2021-08-03 16:35:54 -0400149 {"error": "internal_failure"}, status=http.client.BAD_REQUEST
Georgy Savva46bb58e2019-11-13 22:21:57 +0300150 )
151
152 with pytest.raises(exceptions.RefreshError):
153 _client._token_endpoint_request(
154 request, "http://example.com", {"error": "internal_failure"}
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700155 )
Anjali Doneriaeae1dcb2019-09-09 16:36:10 -0700156
157
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700158def verify_request_params(request, params):
Bu Sun Kima57a7702020-01-10 13:17:34 -0800159 request_body = request.call_args[1]["body"].decode("utf-8")
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700160 request_params = urllib.parse.parse_qs(request_body)
161
Tres Seaver560cf1e2021-08-03 16:35:54 -0400162 for key, value in params.items():
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700163 assert request_params[key][0] == value
164
165
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700166@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700167def test_jwt_grant(utcnow):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700168 request = make_request(
169 {"access_token": "token", "expires_in": 500, "extra": "data"}
170 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700171
172 token, expiry, extra_data = _client.jwt_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700173 request, "http://example.com", "assertion_value"
174 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700175
176 # Check request call
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700177 verify_request_params(
178 request, {"grant_type": _client._JWT_GRANT_TYPE, "assertion": "assertion_value"}
179 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700180
181 # Check result
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700182 assert token == "token"
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700183 assert expiry == utcnow() + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700184 assert extra_data["extra"] == "data"
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700185
186
187def test_jwt_grant_no_access_token():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700188 request = make_request(
189 {
190 # No access token.
191 "expires_in": 500,
192 "extra": "data",
193 }
194 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700195
196 with pytest.raises(exceptions.RefreshError):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700197 _client.jwt_grant(request, "http://example.com", "assertion_value")
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700198
199
Christophe Tatonb649b432018-02-08 14:12:23 -0800200def test_id_token_jwt_grant():
201 now = _helpers.utcnow()
202 id_token_expiry = _helpers.datetime_to_secs(now)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700203 id_token = jwt.encode(SIGNER, {"exp": id_token_expiry}).decode("utf-8")
204 request = make_request({"id_token": id_token, "extra": "data"})
Christophe Tatonb649b432018-02-08 14:12:23 -0800205
206 token, expiry, extra_data = _client.id_token_jwt_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700207 request, "http://example.com", "assertion_value"
208 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800209
210 # Check request call
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700211 verify_request_params(
212 request, {"grant_type": _client._JWT_GRANT_TYPE, "assertion": "assertion_value"}
213 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800214
215 # Check result
216 assert token == id_token
217 # JWT does not store microseconds
218 now = now.replace(microsecond=0)
219 assert expiry == now
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700220 assert extra_data["extra"] == "data"
Christophe Tatonb649b432018-02-08 14:12:23 -0800221
222
223def test_id_token_jwt_grant_no_access_token():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700224 request = make_request(
225 {
226 # No access token.
227 "expires_in": 500,
228 "extra": "data",
229 }
230 )
Christophe Tatonb649b432018-02-08 14:12:23 -0800231
232 with pytest.raises(exceptions.RefreshError):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700233 _client.id_token_jwt_grant(request, "http://example.com", "assertion_value")
Christophe Tatonb649b432018-02-08 14:12:23 -0800234
235
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700236@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
Jon Wayne Parrott78fec2c2017-06-30 10:25:08 -0700237def test_refresh_grant(unused_utcnow):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700238 request = make_request(
239 {
240 "access_token": "token",
241 "refresh_token": "new_refresh_token",
242 "expires_in": 500,
243 "extra": "data",
244 }
245 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700246
247 token, refresh_token, expiry, extra_data = _client.refresh_grant(
arithmetic172882293fe2021-04-14 11:22:13 -0700248 request,
249 "http://example.com",
250 "refresh_token",
251 "client_id",
252 "client_secret",
253 rapt_token="rapt_token",
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700254 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700255
256 # Check request call
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700257 verify_request_params(
258 request,
259 {
260 "grant_type": _client._REFRESH_GRANT_TYPE,
261 "refresh_token": "refresh_token",
262 "client_id": "client_id",
263 "client_secret": "client_secret",
arithmetic172882293fe2021-04-14 11:22:13 -0700264 "rapt": "rapt_token",
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700265 },
266 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700267
268 # Check result
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700269 assert token == "token"
270 assert refresh_token == "new_refresh_token"
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700271 assert expiry == datetime.datetime.min + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700272 assert extra_data["extra"] == "data"
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700273
274
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700275@mock.patch("google.auth._helpers.utcnow", return_value=datetime.datetime.min)
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400276def test_refresh_grant_with_scopes(unused_utcnow):
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700277 request = make_request(
278 {
279 "access_token": "token",
280 "refresh_token": "new_refresh_token",
281 "expires_in": 500,
282 "extra": "data",
283 "scope": SCOPES_AS_STRING,
284 }
285 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400286
287 token, refresh_token, expiry, extra_data = _client.refresh_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700288 request,
289 "http://example.com",
290 "refresh_token",
291 "client_id",
292 "client_secret",
293 SCOPES_AS_LIST,
294 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400295
296 # Check request call.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700297 verify_request_params(
298 request,
299 {
300 "grant_type": _client._REFRESH_GRANT_TYPE,
301 "refresh_token": "refresh_token",
302 "client_id": "client_id",
303 "client_secret": "client_secret",
304 "scope": SCOPES_AS_STRING,
305 },
306 )
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400307
308 # Check result.
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700309 assert token == "token"
310 assert refresh_token == "new_refresh_token"
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400311 assert expiry == datetime.datetime.min + datetime.timedelta(seconds=500)
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700312 assert extra_data["extra"] == "data"
Eugene W. Foley49a18c42019-05-22 13:50:38 -0400313
314
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700315def test_refresh_grant_no_access_token():
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700316 request = make_request(
317 {
318 # No access token.
319 "refresh_token": "new_refresh_token",
320 "expires_in": 500,
321 "extra": "data",
322 }
323 )
Jon Wayne Parrott123a48b2016-10-07 15:32:49 -0700324
325 with pytest.raises(exceptions.RefreshError):
326 _client.refresh_grant(
Bu Sun Kim9eec0912019-10-21 17:04:21 -0700327 request, "http://example.com", "refresh_token", "client_id", "client_secret"
328 )