Index: client/third_party/google/oauth2/_client.py |
diff --git a/client/third_party/google/oauth2/_client.py b/client/third_party/google/oauth2/_client.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..468cb7e889c4364dcc1d19be4cc736be03a1e974 |
--- /dev/null |
+++ b/client/third_party/google/oauth2/_client.py |
@@ -0,0 +1,200 @@ |
+# Copyright 2016 Google Inc. |
+# |
+# Licensed under the Apache License, Version 2.0 (the "License"); |
+# you may not use this file except in compliance with the License. |
+# You may obtain a copy of the License at |
+# |
+# http://www.apache.org/licenses/LICENSE-2.0 |
+# |
+# Unless required by applicable law or agreed to in writing, software |
+# distributed under the License is distributed on an "AS IS" BASIS, |
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+# See the License for the specific language governing permissions and |
+# limitations under the License. |
+ |
+"""OAuth 2.0 client. |
+ |
+This is a client for interacting with an OAuth 2.0 authorization server's |
+token endpoint. |
+ |
+For more information about the token endpoint, see |
+`Section 3.1 of rfc6749`_ |
+ |
+.. _Section 3.1 of rfc6749: https://tools.ietf.org/html/rfc6749#section-3.2 |
+""" |
+ |
+import datetime |
+import json |
+ |
+from six.moves import http_client |
+from six.moves import urllib |
+ |
+from google.auth import _helpers |
+from google.auth import exceptions |
+ |
+_URLENCODED_CONTENT_TYPE = 'application/x-www-form-urlencoded' |
+_JWT_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:jwt-bearer' |
+_REFRESH_GRANT_TYPE = 'refresh_token' |
+ |
+ |
+def _handle_error_response(response_body): |
+ """"Translates an error response into an exception. |
+ |
+ Args: |
+ response_body (str): The decoded response data. |
+ |
+ Raises: |
+ google.auth.exceptions.RefreshError |
+ """ |
+ try: |
+ error_data = json.loads(response_body) |
+ error_details = '{}: {}'.format( |
+ error_data['error'], |
+ error_data.get('error_description')) |
+ # If no details could be extracted, use the response data. |
+ except (KeyError, ValueError): |
+ error_details = response_body |
+ |
+ raise exceptions.RefreshError( |
+ error_details, response_body) |
+ |
+ |
+def _parse_expiry(response_data): |
+ """Parses the expiry field from a response into a datetime. |
+ |
+ Args: |
+ response_data (Mapping): The JSON-parsed response data. |
+ |
+ Returns: |
+ Optional[datetime]: The expiration or ``None`` if no expiration was |
+ specified. |
+ """ |
+ expires_in = response_data.get('expires_in', None) |
+ |
+ if expires_in is not None: |
+ return _helpers.utcnow() + datetime.timedelta( |
+ seconds=expires_in) |
+ else: |
+ return None |
+ |
+ |
+def _token_endpoint_request(request, token_uri, body): |
+ """Makes a request to the OAuth 2.0 authorization server's token endpoint. |
+ |
+ Args: |
+ request (google.auth.transport.Request): A callable used to make |
+ HTTP requests. |
+ token_uri (str): The OAuth 2.0 authorizations server's token endpoint |
+ URI. |
+ body (Mapping[str, str]): The parameters to send in the request body. |
+ |
+ Returns: |
+ Mapping[str, str]: The JSON-decoded response data. |
+ |
+ Raises: |
+ google.auth.exceptions.RefreshError: If the token endpoint returned |
+ an error. |
+ """ |
+ body = urllib.parse.urlencode(body) |
+ headers = { |
+ 'content-type': _URLENCODED_CONTENT_TYPE, |
+ } |
+ |
+ response = request( |
+ method='POST', url=token_uri, headers=headers, body=body) |
+ |
+ response_body = response.data.decode('utf-8') |
+ |
+ if response.status != http_client.OK: |
+ _handle_error_response(response_body) |
+ |
+ response_data = json.loads(response_body) |
+ |
+ return response_data |
+ |
+ |
+def jwt_grant(request, token_uri, assertion): |
+ """Implements the JWT Profile for OAuth 2.0 Authorization Grants. |
+ |
+ For more details, see `rfc7523 section 4`_. |
+ |
+ Args: |
+ request (google.auth.transport.Request): A callable used to make |
+ HTTP requests. |
+ token_uri (str): The OAuth 2.0 authorizations server's token endpoint |
+ URI. |
+ assertion (str): The OAuth 2.0 assertion. |
+ |
+ Returns: |
+ Tuple[str, Optional[datetime], Mapping[str, str]]: The access token, |
+ expiration, and additional data returned by the token endpoint. |
+ |
+ Raises: |
+ google.auth.exceptions.RefreshError: If the token endpoint returned |
+ an error. |
+ |
+ .. _rfc7523 section 4: https://tools.ietf.org/html/rfc7523#section-4 |
+ """ |
+ body = { |
+ 'assertion': assertion, |
+ 'grant_type': _JWT_GRANT_TYPE, |
+ } |
+ |
+ response_data = _token_endpoint_request(request, token_uri, body) |
+ |
+ try: |
+ access_token = response_data['access_token'] |
+ except KeyError: |
+ raise exceptions.RefreshError( |
+ 'No access token in response.', response_data) |
+ |
+ expiry = _parse_expiry(response_data) |
+ |
+ return access_token, expiry, response_data |
+ |
+ |
+def refresh_grant(request, token_uri, refresh_token, client_id, client_secret): |
+ """Implements the OAuth 2.0 refresh token grant. |
+ |
+ For more details, see `rfc678 section 6`_. |
+ |
+ Args: |
+ request (google.auth.transport.Request): A callable used to make |
+ HTTP requests. |
+ token_uri (str): The OAuth 2.0 authorizations server's token endpoint |
+ URI. |
+ refresh_token (str): The refresh token to use to get a new access |
+ token. |
+ client_id (str): The OAuth 2.0 application's client ID. |
+ client_secret (str): The Oauth 2.0 appliaction's client secret. |
+ |
+ Returns: |
+ Tuple[str, Optional[str], Optional[datetime], Mapping[str, str]]: The |
+ access token, new refresh token, expiration, and additional data |
+ returned by the token endpoint. |
+ |
+ Raises: |
+ google.auth.exceptions.RefreshError: If the token endpoint returned |
+ an error. |
+ |
+ .. _rfc6748 section 6: https://tools.ietf.org/html/rfc6749#section-6 |
+ """ |
+ body = { |
+ 'grant_type': _REFRESH_GRANT_TYPE, |
+ 'client_id': client_id, |
+ 'client_secret': client_secret, |
+ 'refresh_token': refresh_token, |
+ } |
+ |
+ response_data = _token_endpoint_request(request, token_uri, body) |
+ |
+ try: |
+ access_token = response_data['access_token'] |
+ except KeyError: |
+ raise exceptions.RefreshError( |
+ 'No access token in response.', response_data) |
+ |
+ refresh_token = response_data.get('refresh_token', refresh_token) |
+ expiry = _parse_expiry(response_data) |
+ |
+ return access_token, refresh_token, expiry, response_data |