123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- # Copyright 2020 Google LLC
- #
- # 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 Utilities.
- This module provides implementations for various OAuth 2.0 utilities.
- This includes `OAuth error handling`_ and
- `Client authentication for OAuth flows`_.
- OAuth error handling
- --------------------
- This will define interfaces for handling OAuth related error responses as
- stated in `RFC 6749 section 5.2`_.
- This will include a common function to convert these HTTP error responses to a
- :class:`google.auth.exceptions.OAuthError` exception.
- Client authentication for OAuth flows
- -------------------------------------
- We introduce an interface for defining client authentication credentials based
- on `RFC 6749 section 2.3.1`_. This will expose the following
- capabilities:
- * Ability to support basic authentication via request header.
- * Ability to support bearer token authentication via request header.
- * Ability to support client ID / secret authentication via request body.
- .. _RFC 6749 section 2.3.1: https://tools.ietf.org/html/rfc6749#section-2.3.1
- .. _RFC 6749 section 5.2: https://tools.ietf.org/html/rfc6749#section-5.2
- """
- import abc
- import base64
- import enum
- import json
- from google.auth import exceptions
- # OAuth client authentication based on
- # https://tools.ietf.org/html/rfc6749#section-2.3.
- class ClientAuthType(enum.Enum):
- basic = 1
- request_body = 2
- class ClientAuthentication(object):
- """Defines the client authentication credentials for basic and request-body
- types based on https://tools.ietf.org/html/rfc6749#section-2.3.1.
- """
- def __init__(self, client_auth_type, client_id, client_secret=None):
- """Instantiates a client authentication object containing the client ID
- and secret credentials for basic and response-body auth.
- Args:
- client_auth_type (google.oauth2.oauth_utils.ClientAuthType): The
- client authentication type.
- client_id (str): The client ID.
- client_secret (Optional[str]): The client secret.
- """
- self.client_auth_type = client_auth_type
- self.client_id = client_id
- self.client_secret = client_secret
- class OAuthClientAuthHandler(metaclass=abc.ABCMeta):
- """Abstract class for handling client authentication in OAuth-based
- operations.
- """
- def __init__(self, client_authentication=None):
- """Instantiates an OAuth client authentication handler.
- Args:
- client_authentication (Optional[google.oauth2.utils.ClientAuthentication]):
- The OAuth client authentication credentials if available.
- """
- super(OAuthClientAuthHandler, self).__init__()
- self._client_authentication = client_authentication
- def apply_client_authentication_options(
- self, headers, request_body=None, bearer_token=None
- ):
- """Applies client authentication on the OAuth request's headers or POST
- body.
- Args:
- headers (Mapping[str, str]): The HTTP request header.
- request_body (Optional[Mapping[str, str]]): The HTTP request body
- dictionary. For requests that do not support request body, this
- is None and will be ignored.
- bearer_token (Optional[str]): The optional bearer token.
- """
- # Inject authenticated header.
- self._inject_authenticated_headers(headers, bearer_token)
- # Inject authenticated request body.
- if bearer_token is None:
- self._inject_authenticated_request_body(request_body)
- def _inject_authenticated_headers(self, headers, bearer_token=None):
- if bearer_token is not None:
- headers["Authorization"] = "Bearer %s" % bearer_token
- elif (
- self._client_authentication is not None
- and self._client_authentication.client_auth_type is ClientAuthType.basic
- ):
- username = self._client_authentication.client_id
- password = self._client_authentication.client_secret or ""
- credentials = base64.b64encode(
- ("%s:%s" % (username, password)).encode()
- ).decode()
- headers["Authorization"] = "Basic %s" % credentials
- def _inject_authenticated_request_body(self, request_body):
- if (
- self._client_authentication is not None
- and self._client_authentication.client_auth_type
- is ClientAuthType.request_body
- ):
- if request_body is None:
- raise exceptions.OAuthError(
- "HTTP request does not support request-body"
- )
- else:
- request_body["client_id"] = self._client_authentication.client_id
- request_body["client_secret"] = (
- self._client_authentication.client_secret or ""
- )
- def handle_error_response(response_body):
- """Translates an error response from an OAuth operation into an
- OAuthError exception.
- Args:
- response_body (str): The decoded response data.
- Raises:
- google.auth.exceptions.OAuthError
- """
- try:
- error_components = []
- error_data = json.loads(response_body)
- error_components.append("Error code {}".format(error_data["error"]))
- if "error_description" in error_data:
- error_components.append(": {}".format(error_data["error_description"]))
- if "error_uri" in error_data:
- error_components.append(" - {}".format(error_data["error_uri"]))
- error_details = "".join(error_components)
- # If no details could be extracted, use the response data.
- except (KeyError, ValueError):
- error_details = response_body
- raise exceptions.OAuthError(error_details, response_body)
|