| 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
 | 
| 
 |