fix: add back python 2.7 for gcloud usage only (#892)

* fix: add back python 2.7 for gcloud

* fix: fix setup and tests

* fix: add enum34 for python 2.7

* fix: add app engine app and fix noxfile

* fix: move test_app_engine.py

* fix: fix downscoped

* fix: fix downscoped

* fix: remove py2 from classifiers
diff --git a/google/auth/_cloud_sdk.py b/google/auth/_cloud_sdk.py
index 1f13ad4..40e6aec 100644
--- a/google/auth/_cloud_sdk.py
+++ b/google/auth/_cloud_sdk.py
@@ -18,6 +18,8 @@
 import os
 import subprocess
 
+import six
+
 from google.auth import environment_vars
 from google.auth import exceptions
 
@@ -154,4 +156,4 @@
         new_exc = exceptions.UserAccessTokenError(
             "Failed to obtain access token", caught_exc
         )
-        raise new_exc from caught_exc
+        six.raise_from(new_exc, caught_exc)
diff --git a/google/auth/_credentials_async.py b/google/auth/_credentials_async.py
index 760758d..d4d4e2c 100644
--- a/google/auth/_credentials_async.py
+++ b/google/auth/_credentials_async.py
@@ -18,10 +18,13 @@
 import abc
 import inspect
 
+import six
+
 from google.auth import credentials
 
 
-class Credentials(credentials.Credentials, metaclass=abc.ABCMeta):
[email protected]_metaclass(abc.ABCMeta)
+class Credentials(credentials.Credentials):
     """Async inherited credentials class from google.auth.credentials.
     The added functionality is the before_request call which requires
     async/await syntax.
@@ -81,7 +84,8 @@
     """
 
 
-class ReadOnlyScoped(credentials.ReadOnlyScoped, metaclass=abc.ABCMeta):
[email protected]_metaclass(abc.ABCMeta)
+class ReadOnlyScoped(credentials.ReadOnlyScoped):
     """Interface for credentials whose scopes can be queried.
 
     OAuth 2.0-based credentials allow limiting access using scopes as described
@@ -167,5 +171,6 @@
         return credentials
 
 
-class Signing(credentials.Signing, metaclass=abc.ABCMeta):
[email protected]_metaclass(abc.ABCMeta)
+class Signing(credentials.Signing):
     """Interface for credentials that can cryptographically sign messages."""
diff --git a/google/auth/_default.py b/google/auth/_default.py
index 8b0573b..4ae7c8c 100644
--- a/google/auth/_default.py
+++ b/google/auth/_default.py
@@ -23,6 +23,8 @@
 import os
 import warnings
 
+import six
+
 from google.auth import environment_vars
 from google.auth import exceptions
 import google.auth.transport._http_client
@@ -116,7 +118,7 @@
             new_exc = exceptions.DefaultCredentialsError(
                 "File {} is not a valid json file.".format(filename), caught_exc
             )
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
     # The type key should indicate that the file is either a service account
     # credentials file or an authorized user credentials file.
@@ -132,7 +134,7 @@
         except ValueError as caught_exc:
             msg = "Failed to load authorized user credentials from {}".format(filename)
             new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
         if quota_project_id:
             credentials = credentials.with_quota_project(quota_project_id)
         if not credentials.quota_project_id:
@@ -149,7 +151,7 @@
         except ValueError as caught_exc:
             msg = "Failed to load service account credentials from {}".format(filename)
             new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
         if quota_project_id:
             credentials = credentials.with_quota_project(quota_project_id)
         return credentials, info.get("project_id")
diff --git a/google/auth/_default_async.py b/google/auth/_default_async.py
index 3fa125b..fb277c5 100644
--- a/google/auth/_default_async.py
+++ b/google/auth/_default_async.py
@@ -21,6 +21,8 @@
 import json
 import os
 
+import six
+
 from google.auth import _default
 from google.auth import environment_vars
 from google.auth import exceptions
@@ -61,7 +63,7 @@
             new_exc = exceptions.DefaultCredentialsError(
                 "File {} is not a valid json file.".format(filename), caught_exc
             )
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
     # The type key should indicate that the file is either a service account
     # credentials file or an authorized user credentials file.
@@ -77,7 +79,7 @@
         except ValueError as caught_exc:
             msg = "Failed to load authorized user credentials from {}".format(filename)
             new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
         if quota_project_id:
             credentials = credentials.with_quota_project(quota_project_id)
         if not credentials.quota_project_id:
@@ -94,7 +96,7 @@
         except ValueError as caught_exc:
             msg = "Failed to load service account credentials from {}".format(filename)
             new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
         return credentials, info.get("project_id")
 
     else:
diff --git a/google/auth/_helpers.py b/google/auth/_helpers.py
index 55adf5b..b239fcd 100644
--- a/google/auth/_helpers.py
+++ b/google/auth/_helpers.py
@@ -17,7 +17,9 @@
 import base64
 import calendar
 import datetime
-import urllib
+
+import six
+from six.moves import urllib
 
 
 # Token server doesn't provide a new a token when doing refresh unless the
@@ -85,6 +87,9 @@
 def to_bytes(value, encoding="utf-8"):
     """Converts a string value to bytes, if necessary.
 
+    Unfortunately, ``six.b`` is insufficient for this task since in
+    Python 2 because it does not modify ``unicode`` objects.
+
     Args:
         value (Union[str, bytes]): The value to be converted.
         encoding (str): The encoding to use to convert unicode to bytes.
@@ -97,8 +102,8 @@
     Raises:
         ValueError: If the value could not be converted to bytes.
     """
-    result = value.encode(encoding) if isinstance(value, str) else value
-    if isinstance(result, bytes):
+    result = value.encode(encoding) if isinstance(value, six.text_type) else value
+    if isinstance(result, six.binary_type):
         return result
     else:
         raise ValueError("{0!r} could not be converted to bytes".format(value))
@@ -117,8 +122,8 @@
     Raises:
         ValueError: If the value could not be converted to unicode.
     """
-    result = value.decode("utf-8") if isinstance(value, bytes) else value
-    if isinstance(result, str):
+    result = value.decode("utf-8") if isinstance(value, six.binary_type) else value
+    if isinstance(result, six.text_type):
         return result
     else:
         raise ValueError("{0!r} could not be converted to unicode".format(value))
@@ -160,7 +165,7 @@
     query_params.update(params)
     # Remove any values specified in remove.
     query_params = {
-        key: value for key, value in query_params.items() if key not in remove
+        key: value for key, value in six.iteritems(query_params) if key not in remove
     }
     # Re-encoded the query string.
     new_query = urllib.parse.urlencode(query_params, doseq=True)
diff --git a/google/auth/_oauth2client.py b/google/auth/_oauth2client.py
index 3512e1d..95a9876 100644
--- a/google/auth/_oauth2client.py
+++ b/google/auth/_oauth2client.py
@@ -21,6 +21,8 @@
 
 from __future__ import absolute_import
 
+import six
+
 from google.auth import _helpers
 import google.auth.app_engine
 import google.auth.compute_engine
@@ -32,7 +34,7 @@
     import oauth2client.contrib.gce
     import oauth2client.service_account
 except ImportError as caught_exc:
-    raise ImportError("oauth2client is not installed.") from caught_exc
+    six.raise_from(ImportError("oauth2client is not installed."), caught_exc)
 
 try:
     import oauth2client.contrib.appengine  # pytype: disable=import-error
@@ -164,4 +166,4 @@
         return _CLASS_CONVERSION_MAP[credentials_class](credentials)
     except KeyError as caught_exc:
         new_exc = ValueError(_CONVERT_ERROR_TMPL.format(credentials_class))
-        raise new_exc from caught_exc
+        six.raise_from(new_exc, caught_exc)
diff --git a/google/auth/_service_account_info.py b/google/auth/_service_account_info.py
index 54a40e9..3d340c7 100644
--- a/google/auth/_service_account_info.py
+++ b/google/auth/_service_account_info.py
@@ -17,6 +17,8 @@
 import io
 import json
 
+import six
+
 from google.auth import crypt
 
 
@@ -41,7 +43,7 @@
     """
     keys_needed = set(require if require is not None else [])
 
-    missing = keys_needed.difference(data)
+    missing = keys_needed.difference(six.iterkeys(data))
 
     if missing:
         raise ValueError(
diff --git a/google/auth/aws.py b/google/auth/aws.py
index c8deee7..925b1dd 100644
--- a/google/auth/aws.py
+++ b/google/auth/aws.py
@@ -39,13 +39,20 @@
 
 import hashlib
 import hmac
-import http.client
 import io
 import json
 import os
+import posixpath
 import re
-import urllib
-from urllib.parse import urljoin
+
+try:
+    from urllib.parse import urljoin
+# Python 2.7 compatibility
+except ImportError:  # pragma: NO COVER
+    from urlparse import urljoin
+
+from six.moves import http_client
+from six.moves import urllib
 
 from google.auth import _helpers
 from google.auth import environment_vars
@@ -116,7 +123,9 @@
         # Normalize the URL path. This is needed for the canonical_uri.
         # os.path.normpath can't be used since it normalizes "/" paths
         # to "\\" in Windows OS.
-        normalized_uri = urllib.parse.urlparse(urljoin(url, uri.path))
+        normalized_uri = urllib.parse.urlparse(
+            urljoin(url, posixpath.normpath(uri.path))
+        )
         # Validate provided URL.
         if not uri.hostname or uri.scheme != "https":
             raise ValueError("Invalid AWS service URL")
@@ -631,7 +640,7 @@
             else response.data
         )
 
-        if response.status != http.client.OK:
+        if response.status != http_client.OK:
             raise exceptions.RefreshError(
                 "Unable to retrieve AWS security credentials", response_body
             )
@@ -670,7 +679,7 @@
             else response.data
         )
 
-        if response.status != http.client.OK:
+        if response.status != http_client.OK:
             raise exceptions.RefreshError(
                 "Unable to retrieve AWS role name", response_body
             )
diff --git a/google/auth/compute_engine/_metadata.py b/google/auth/compute_engine/_metadata.py
index af0d849..9db7bea 100644
--- a/google/auth/compute_engine/_metadata.py
+++ b/google/auth/compute_engine/_metadata.py
@@ -18,11 +18,13 @@
 """
 
 import datetime
-import http.client
 import json
 import logging
 import os
-from urllib import parse as urlparse
+
+import six
+from six.moves import http_client
+from six.moves.urllib import parse as urlparse
 
 from google.auth import _helpers
 from google.auth import environment_vars
@@ -89,7 +91,7 @@
 
             metadata_flavor = response.headers.get(_METADATA_FLAVOR_HEADER)
             return (
-                response.status == http.client.OK
+                response.status == http_client.OK
                 and metadata_flavor == _METADATA_FLAVOR_VALUE
             )
 
@@ -163,7 +165,7 @@
             "metadata service. Compute Engine Metadata server unavailable".format(url)
         )
 
-    if response.status == http.client.OK:
+    if response.status == http_client.OK:
         content = _helpers.from_bytes(response.data)
         if response.headers["content-type"] == "application/json":
             try:
@@ -173,7 +175,7 @@
                     "Received invalid JSON from the Google Compute Engine"
                     "metadata service: {:.20}".format(content)
                 )
-                raise new_exc from caught_exc
+                six.raise_from(new_exc, caught_exc)
         else:
             return content
     else:
diff --git a/google/auth/compute_engine/credentials.py b/google/auth/compute_engine/credentials.py
index cb4e0f0..b39ac50 100644
--- a/google/auth/compute_engine/credentials.py
+++ b/google/auth/compute_engine/credentials.py
@@ -21,6 +21,8 @@
 
 import datetime
 
+import six
+
 from google.auth import _helpers
 from google.auth import credentials
 from google.auth import exceptions
@@ -112,7 +114,7 @@
             )
         except exceptions.TransportError as caught_exc:
             new_exc = exceptions.RefreshError(caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
     @property
     def service_account_email(self):
@@ -350,7 +352,7 @@
             id_token = _metadata.get(request, path, params=params)
         except exceptions.TransportError as caught_exc:
             new_exc = exceptions.RefreshError(caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
         _, payload, _, _ = jwt._unverified_decode(id_token)
         return id_token, datetime.datetime.fromtimestamp(payload["exp"])
diff --git a/google/auth/credentials.py b/google/auth/credentials.py
index 8d9974c..ec21a27 100644
--- a/google/auth/credentials.py
+++ b/google/auth/credentials.py
@@ -17,10 +17,13 @@
 
 import abc
 
+import six
+
 from google.auth import _helpers
 
 
-class Credentials(object, metaclass=abc.ABCMeta):
[email protected]_metaclass(abc.ABCMeta)
+class Credentials(object):
     """Base class for all credentials.
 
     All credentials have a :attr:`token` that is used for authentication and
@@ -184,7 +187,8 @@
         """Anonymous credentials do nothing to the request."""
 
 
-class ReadOnlyScoped(object, metaclass=abc.ABCMeta):
[email protected]_metaclass(abc.ABCMeta)
+class ReadOnlyScoped(object):
     """Interface for credentials whose scopes can be queried.
 
     OAuth 2.0-based credentials allow limiting access using scopes as described
@@ -325,7 +329,8 @@
         return credentials
 
 
-class Signing(object, metaclass=abc.ABCMeta):
[email protected]_metaclass(abc.ABCMeta)
+class Signing(object):
     """Interface for credentials that can cryptographically sign messages."""
 
     @abc.abstractmethod
diff --git a/google/auth/crypt/__init__.py b/google/auth/crypt/__init__.py
index 97e9d81..15ac950 100644
--- a/google/auth/crypt/__init__.py
+++ b/google/auth/crypt/__init__.py
@@ -37,6 +37,8 @@
 version is at least 1.4.0.
 """
 
+import six
+
 from google.auth.crypt import base
 from google.auth.crypt import rsa
 
@@ -88,7 +90,7 @@
     Returns:
         bool: True if the signature is valid, otherwise False.
     """
-    if isinstance(certs, (str, bytes)):
+    if isinstance(certs, (six.text_type, six.binary_type)):
         certs = [certs]
 
     for cert in certs:
diff --git a/google/auth/crypt/_python_rsa.py b/google/auth/crypt/_python_rsa.py
index 1c4a9da..ec30dd0 100644
--- a/google/auth/crypt/_python_rsa.py
+++ b/google/auth/crypt/_python_rsa.py
@@ -21,13 +21,12 @@
 
 from __future__ import absolute_import
 
-import io
-
 from pyasn1.codec.der import decoder
 from pyasn1_modules import pem
 from pyasn1_modules.rfc2459 import Certificate
 from pyasn1_modules.rfc5208 import PrivateKeyInfo
 import rsa
+import six
 
 from google.auth import _helpers
 from google.auth.crypt import base
@@ -53,9 +52,9 @@
     """
     num_bits = len(bit_list)
     byte_vals = bytearray()
-    for start in range(0, num_bits, 8):
+    for start in six.moves.xrange(0, num_bits, 8):
         curr_bits = bit_list[start : start + 8]
-        char_val = sum(val * digit for val, digit in zip(_POW2, curr_bits))
+        char_val = sum(val * digit for val, digit in six.moves.zip(_POW2, curr_bits))
         byte_vals.append(char_val)
     return bytes(byte_vals)
 
@@ -153,7 +152,7 @@
         """
         key = _helpers.from_bytes(key)  # PEM expects str in Python 3
         marker_id, key_bytes = pem.readPemBlocksFromFile(
-            io.StringIO(key), _PKCS1_MARKER, _PKCS8_MARKER
+            six.StringIO(key), _PKCS1_MARKER, _PKCS8_MARKER
         )
 
         # Key is in pkcs1 format.
diff --git a/google/auth/crypt/base.py b/google/auth/crypt/base.py
index 0bda9c3..c98d5bf 100644
--- a/google/auth/crypt/base.py
+++ b/google/auth/crypt/base.py
@@ -18,12 +18,15 @@
 import io
 import json
 
+import six
+
 
 _JSON_FILE_PRIVATE_KEY = "private_key"
 _JSON_FILE_PRIVATE_KEY_ID = "private_key_id"
 
 
-class Verifier(object, metaclass=abc.ABCMeta):
[email protected]_metaclass(abc.ABCMeta)
+class Verifier(object):
     """Abstract base class for crytographic signature verifiers."""
 
     @abc.abstractmethod
@@ -43,7 +46,8 @@
         raise NotImplementedError("Verify must be implemented")
 
 
-class Signer(object, metaclass=abc.ABCMeta):
[email protected]_metaclass(abc.ABCMeta)
+class Signer(object):
     """Abstract base class for cryptographic signers."""
 
     @abc.abstractproperty
@@ -66,7 +70,8 @@
         raise NotImplementedError("Sign must be implemented")
 
 
-class FromServiceAccountMixin(object, metaclass=abc.ABCMeta):
[email protected]_metaclass(abc.ABCMeta)
+class FromServiceAccountMixin(object):
     """Mix-in to enable factory constructors for a Signer."""
 
     @abc.abstractmethod
diff --git a/google/auth/crypt/es256.py b/google/auth/crypt/es256.py
index 71dcbfc..c6d6176 100644
--- a/google/auth/crypt/es256.py
+++ b/google/auth/crypt/es256.py
@@ -15,6 +15,7 @@
 """ECDSA (ES256) verifier and signer that use the ``cryptography`` library.
 """
 
+from cryptography import utils
 import cryptography.exceptions
 from cryptography.hazmat import backends
 from cryptography.hazmat.primitives import hashes
@@ -52,8 +53,8 @@
         sig_bytes = _helpers.to_bytes(signature)
         if len(sig_bytes) != 64:
             return False
-        r = int.from_bytes(sig_bytes[:32], byteorder="big")
-        s = int.from_bytes(sig_bytes[32:], byteorder="big")
+        r = utils.int_from_bytes(sig_bytes[:32], byteorder="big")
+        s = utils.int_from_bytes(sig_bytes[32:], byteorder="big")
         asn1_sig = encode_dss_signature(r, s)
 
         message = _helpers.to_bytes(message)
@@ -120,7 +121,7 @@
 
         # Convert ASN1 encoded signature to (r||s) raw signature.
         (r, s) = decode_dss_signature(asn1_signature)
-        return r.to_bytes(32, byteorder="big") + s.to_bytes(32, byteorder="big")
+        return utils.int_to_bytes(r, 32) + utils.int_to_bytes(s, 32)
 
     @classmethod
     def from_string(cls, key, key_id=None):
diff --git a/google/auth/downscoped.py b/google/auth/downscoped.py
index 96a4e65..a1d7b6e 100644
--- a/google/auth/downscoped.py
+++ b/google/auth/downscoped.py
@@ -50,6 +50,8 @@
 
 import datetime
 
+import six
+
 from google.auth import _helpers
 from google.auth import credentials
 from google.oauth2 import sts
@@ -221,7 +223,7 @@
         Raises:
             TypeError: If the value is not a string.
         """
-        if not isinstance(value, str):
+        if not isinstance(value, six.string_types):
             raise TypeError("The provided available_resource is not a string.")
         self._available_resource = value
 
@@ -247,7 +249,7 @@
             ValueError: If the value is not valid.
         """
         for available_permission in value:
-            if not isinstance(available_permission, str):
+            if not isinstance(available_permission, six.string_types):
                 raise TypeError(
                     "Provided available_permissions are not a list of strings."
                 )
@@ -350,7 +352,7 @@
         Raises:
             TypeError: If the value is not of type string.
         """
-        if not isinstance(value, str):
+        if not isinstance(value, six.string_types):
             raise TypeError("The provided expression is not a string.")
         self._expression = value
 
@@ -373,7 +375,7 @@
         Raises:
             TypeError: If the value is not of type string or None.
         """
-        if not isinstance(value, str) and value is not None:
+        if not isinstance(value, six.string_types) and value is not None:
             raise TypeError("The provided title is not a string or None.")
         self._title = value
 
@@ -396,7 +398,7 @@
         Raises:
             TypeError: If the value is not of type string or None.
         """
-        if not isinstance(value, str) and value is not None:
+        if not isinstance(value, six.string_types) and value is not None:
             raise TypeError("The provided description is not a string or None.")
         self._description = value
 
diff --git a/google/auth/external_account.py b/google/auth/external_account.py
index f588981..cbd0baf 100644
--- a/google/auth/external_account.py
+++ b/google/auth/external_account.py
@@ -33,6 +33,8 @@
 import json
 import re
 
+import six
+
 from google.auth import _helpers
 from google.auth import credentials
 from google.auth import exceptions
@@ -50,9 +52,8 @@
 _CLOUD_RESOURCE_MANAGER = "https://cloudresourcemanager.googleapis.com/v1/projects/"
 
 
-class Credentials(
-    credentials.Scoped, credentials.CredentialsWithQuotaProject, metaclass=abc.ABCMeta
-):
[email protected]_metaclass(abc.ABCMeta)
+class Credentials(credentials.Scoped, credentials.CredentialsWithQuotaProject):
     """Base class for all external account credentials.
 
     This is used to instantiate Credentials for exchanging external account
diff --git a/google/auth/iam.py b/google/auth/iam.py
index 277f4b7..5d63dc5 100644
--- a/google/auth/iam.py
+++ b/google/auth/iam.py
@@ -20,9 +20,10 @@
 """
 
 import base64
-import http.client
 import json
 
+from six.moves import http_client
+
 from google.auth import _helpers
 from google.auth import crypt
 from google.auth import exceptions
@@ -76,7 +77,7 @@
         self._credentials.before_request(self._request, method, url, headers)
         response = self._request(url=url, method=method, body=body, headers=headers)
 
-        if response.status != http.client.OK:
+        if response.status != http_client.OK:
             raise exceptions.TransportError(
                 "Error calling the IAM signBlob API: {}".format(response.data)
             )
diff --git a/google/auth/identity_pool.py b/google/auth/identity_pool.py
index 901fd62..fb33d77 100644
--- a/google/auth/identity_pool.py
+++ b/google/auth/identity_pool.py
@@ -33,7 +33,11 @@
 access tokens.
 """
 
-from collections.abc import Mapping
+try:
+    from collections.abc import Mapping
+# Python 2.7 compatibility
+except ImportError:  # pragma: NO COVER
+    from collections import Mapping
 import io
 import json
 import os
diff --git a/google/auth/impersonated_credentials.py b/google/auth/impersonated_credentials.py
index 2704bfd..b8a6c49 100644
--- a/google/auth/impersonated_credentials.py
+++ b/google/auth/impersonated_credentials.py
@@ -28,9 +28,11 @@
 import base64
 import copy
 from datetime import datetime
-import http.client
 import json
 
+import six
+from six.moves import http_client
+
 from google.auth import _helpers
 from google.auth import credentials
 from google.auth import exceptions
@@ -98,7 +100,7 @@
         else response.data
     )
 
-    if response.status != http.client.OK:
+    if response.status != http_client.OK:
         exceptions.RefreshError(_REFRESH_ERROR, response_body)
 
     try:
@@ -115,7 +117,7 @@
             ),
             response_body,
         )
-        raise new_exc from caught_exc
+        six.raise_from(new_exc, caught_exc)
 
 
 class Credentials(credentials.CredentialsWithQuotaProject, credentials.Signing):
diff --git a/google/auth/jwt.py b/google/auth/jwt.py
index bb9ffae..d565595 100644
--- a/google/auth/jwt.py
+++ b/google/auth/jwt.py
@@ -40,13 +40,18 @@
 
 """
 
-from collections.abc import Mapping
+try:
+    from collections.abc import Mapping
+# Python 2.7 compatibility
+except ImportError:  # pragma: NO COVER
+    from collections import Mapping
 import copy
 import datetime
 import json
-import urllib
 
 import cachetools
+import six
+from six.moves import urllib
 
 from google.auth import _helpers
 from google.auth import _service_account_info
@@ -118,7 +123,7 @@
         return json.loads(section_bytes.decode("utf-8"))
     except ValueError as caught_exc:
         new_exc = ValueError("Can't parse segment: {0}".format(section_bytes))
-        raise new_exc from caught_exc
+        six.raise_from(new_exc, caught_exc)
 
 
 def _unverified_decode(token):
@@ -244,16 +249,19 @@
 
     try:
         verifier_cls = _ALGORITHM_TO_VERIFIER_CLASS[key_alg]
-    except KeyError as caught_exc:
+    except KeyError as exc:
         if key_alg in _CRYPTOGRAPHY_BASED_ALGORITHMS:
-            msg = (
-                "The key algorithm {} requires the cryptography package "
-                "to be installed."
+            six.raise_from(
+                ValueError(
+                    "The key algorithm {} requires the cryptography package "
+                    "to be installed.".format(key_alg)
+                ),
+                exc,
             )
         else:
-            msg = "Unsupported signature algorithm {}"
-        new_exc = ValueError(msg.format(key_alg))
-        raise new_exc from caught_exc
+            six.raise_from(
+                ValueError("Unsupported signature algorithm {}".format(key_alg)), exc
+            )
 
     # If certs is specified as a dictionary of key IDs to certificates, then
     # use the certificate identified by the key ID in the token header.
diff --git a/google/auth/transport/__init__.py b/google/auth/transport/__init__.py
index d1b035d..374e7b4 100644
--- a/google/auth/transport/__init__.py
+++ b/google/auth/transport/__init__.py
@@ -25,9 +25,11 @@
 """
 
 import abc
-import http.client
 
-DEFAULT_REFRESH_STATUS_CODES = (http.client.UNAUTHORIZED,)
+import six
+from six.moves import http_client
+
+DEFAULT_REFRESH_STATUS_CODES = (http_client.UNAUTHORIZED,)
 """Sequence[int]:  Which HTTP status code indicate that credentials should be
 refreshed and a request should be retried.
 """
@@ -36,7 +38,8 @@
 """int: How many times to refresh the credentials and retry a request."""
 
 
-class Response(object, metaclass=abc.ABCMeta):
[email protected]_metaclass(abc.ABCMeta)
+class Response(object):
     """HTTP Response data."""
 
     @abc.abstractproperty
@@ -55,7 +58,8 @@
         raise NotImplementedError("data must be implemented.")
 
 
-class Request(object, metaclass=abc.ABCMeta):
[email protected]_metaclass(abc.ABCMeta)
+class Request(object):
     """Interface for a callable that makes HTTP requests.
 
     Specific transport implementations should provide an implementation of
diff --git a/google/auth/transport/_aiohttp_requests.py b/google/auth/transport/_aiohttp_requests.py
index ee94043..ab7dfef 100644
--- a/google/auth/transport/_aiohttp_requests.py
+++ b/google/auth/transport/_aiohttp_requests.py
@@ -24,6 +24,7 @@
 import functools
 
 import aiohttp
+import six
 import urllib3
 
 from google.auth import exceptions
@@ -190,11 +191,11 @@
 
         except aiohttp.ClientError as caught_exc:
             new_exc = exceptions.TransportError(caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
         except asyncio.TimeoutError as caught_exc:
             new_exc = exceptions.TransportError(caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
 
 class AuthorizedSession(aiohttp.ClientSession):
diff --git a/google/auth/transport/_http_client.py b/google/auth/transport/_http_client.py
index 679087f..c153763 100644
--- a/google/auth/transport/_http_client.py
+++ b/google/auth/transport/_http_client.py
@@ -14,10 +14,12 @@
 
 """Transport adapter for http.client, for internal use only."""
 
-import http.client
 import logging
 import socket
-import urllib
+
+import six
+from six.moves import http_client
+from six.moves import urllib
 
 from google.auth import exceptions
 from google.auth import transport
@@ -96,7 +98,7 @@
                 "was specified".format(parts.scheme)
             )
 
-        connection = http.client.HTTPConnection(parts.netloc, timeout=timeout)
+        connection = http_client.HTTPConnection(parts.netloc, timeout=timeout)
 
         try:
             _LOGGER.debug("Making request: %s %s", method, url)
@@ -105,9 +107,9 @@
             response = connection.getresponse()
             return Response(response)
 
-        except (http.client.HTTPException, socket.error) as caught_exc:
+        except (http_client.HTTPException, socket.error) as caught_exc:
             new_exc = exceptions.TransportError(caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
         finally:
             connection.close()
diff --git a/google/auth/transport/_mtls_helper.py b/google/auth/transport/_mtls_helper.py
index 1b9b9c2..4dccb10 100644
--- a/google/auth/transport/_mtls_helper.py
+++ b/google/auth/transport/_mtls_helper.py
@@ -20,6 +20,8 @@
 import re
 import subprocess
 
+import six
+
 from google.auth import exceptions
 
 CONTEXT_AWARE_METADATA_PATH = "~/.secureConnect/context_aware_metadata.json"
@@ -80,7 +82,7 @@
             metadata = json.load(f)
     except ValueError as caught_exc:
         new_exc = exceptions.ClientCertError(caught_exc)
-        raise new_exc from caught_exc
+        six.raise_from(new_exc, caught_exc)
 
     return metadata
 
@@ -108,7 +110,7 @@
         stdout, stderr = process.communicate()
     except OSError as caught_exc:
         new_exc = exceptions.ClientCertError(caught_exc)
-        raise new_exc from caught_exc
+        six.raise_from(new_exc, caught_exc)
 
     # Check cert provider command execution error.
     if process.returncode != 0:
diff --git a/google/auth/transport/grpc.py b/google/auth/transport/grpc.py
index 160dc94..c47cb3d 100644
--- a/google/auth/transport/grpc.py
+++ b/google/auth/transport/grpc.py
@@ -19,6 +19,8 @@
 import logging
 import os
 
+import six
+
 from google.auth import environment_vars
 from google.auth import exceptions
 from google.auth.transport import _mtls_helper
@@ -27,11 +29,13 @@
 try:
     import grpc
 except ImportError as caught_exc:  # pragma: NO COVER
-    new_exc = ImportError(
-        "gRPC is not installed, please install the grpcio package "
-        "to use the gRPC transport."
+    six.raise_from(
+        ImportError(
+            "gRPC is not installed, please install the grpcio package "
+            "to use the gRPC transport."
+        ),
+        caught_exc,
     )
-    raise new_exc from caught_exc
 
 _LOGGER = logging.getLogger(__name__)
 
@@ -84,7 +88,7 @@
             self._request, context.method_name, context.service_url, headers
         )
 
-        return list(headers.items())
+        return list(six.iteritems(headers))
 
     def __call__(self, context, callback):
         """Passes authorization metadata into the given callback.
@@ -333,7 +337,7 @@
                 )
             except exceptions.ClientCertError as caught_exc:
                 new_exc = exceptions.MutualTLSChannelError(caught_exc)
-                raise new_exc from caught_exc
+                six.raise_from(new_exc, caught_exc)
         else:
             self._ssl_credentials = grpc.ssl_channel_credentials()
 
diff --git a/google/auth/transport/mtls.py b/google/auth/transport/mtls.py
index c570761..b40bfbe 100644
--- a/google/auth/transport/mtls.py
+++ b/google/auth/transport/mtls.py
@@ -14,6 +14,8 @@
 
 """Utilites for mutual TLS."""
 
+import six
+
 from google.auth import exceptions
 from google.auth.transport import _mtls_helper
 
@@ -51,7 +53,7 @@
             _, cert_bytes, key_bytes = _mtls_helper.get_client_cert_and_key()
         except (OSError, RuntimeError, ValueError) as caught_exc:
             new_exc = exceptions.MutualTLSChannelError(caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
         return cert_bytes, key_bytes
 
@@ -96,7 +98,7 @@
                 key_file.write(key_bytes)
         except (exceptions.ClientCertError, OSError) as caught_exc:
             new_exc = exceptions.MutualTLSChannelError(caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
         return cert_path, key_path, passphrase_bytes
 
diff --git a/google/auth/transport/requests.py b/google/auth/transport/requests.py
index 2cb6942..817176b 100644
--- a/google/auth/transport/requests.py
+++ b/google/auth/transport/requests.py
@@ -25,16 +25,21 @@
 try:
     import requests
 except ImportError as caught_exc:  # pragma: NO COVER
-    new_exc = ImportError(
-        "The requests library is not installed, please install the "
-        "requests package to use the requests transport."
+    import six
+
+    six.raise_from(
+        ImportError(
+            "The requests library is not installed, please install the "
+            "requests package to use the requests transport."
+        ),
+        caught_exc,
     )
-    raise new_exc from caught_exc
 import requests.adapters  # pylint: disable=ungrouped-imports
 import requests.exceptions  # pylint: disable=ungrouped-imports
 from requests.packages.urllib3.util.ssl_ import (
     create_urllib3_context,
 )  # pylint: disable=ungrouped-imports
+import six  # pylint: disable=ungrouped-imports
 
 from google.auth import environment_vars
 from google.auth import exceptions
@@ -181,7 +186,7 @@
             return _Response(response)
         except requests.exceptions.RequestException as caught_exc:
             new_exc = exceptions.TransportError(caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
 
 class _MutualTlsAdapter(requests.adapters.HTTPAdapter):
@@ -391,7 +396,7 @@
             import OpenSSL
         except ImportError as caught_exc:
             new_exc = exceptions.MutualTLSChannelError(caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
         try:
             (
@@ -411,7 +416,7 @@
             OpenSSL.crypto.Error,
         ) as caught_exc:
             new_exc = exceptions.MutualTLSChannelError(caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
     def request(
         self,
diff --git a/google/auth/transport/urllib3.py b/google/auth/transport/urllib3.py
index aa7188c..6a2504d 100644
--- a/google/auth/transport/urllib3.py
+++ b/google/auth/transport/urllib3.py
@@ -34,11 +34,16 @@
 try:
     import urllib3
 except ImportError as caught_exc:  # pragma: NO COVER
-    new_exc = ImportError(
-        "The urllib3 library is not installed, please install the "
-        "urllib3 package to use the urllib3 transport."
+    import six
+
+    six.raise_from(
+        ImportError(
+            "The urllib3 library is not installed, please install the "
+            "urllib3 package to use the urllib3 transport."
+        ),
+        caught_exc,
     )
-    raise new_exc from caught_exc
+import six
 import urllib3.exceptions  # pylint: disable=ungrouped-imports
 
 from google.auth import environment_vars
@@ -137,7 +142,7 @@
             return _Response(response)
         except urllib3.exceptions.HTTPError as caught_exc:
             new_exc = exceptions.TransportError(caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
 
 def _make_default_http():
@@ -329,7 +334,7 @@
             import OpenSSL
         except ImportError as caught_exc:
             new_exc = exceptions.MutualTLSChannelError(caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
         try:
             found_cert_key, cert, key = transport._mtls_helper.get_client_cert_and_key(
@@ -346,7 +351,7 @@
             OpenSSL.crypto.Error,
         ) as caught_exc:
             new_exc = exceptions.MutualTLSChannelError(caught_exc)
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
         if self._has_user_provided_http:
             self._has_user_provided_http = False
diff --git a/google/oauth2/_client.py b/google/oauth2/_client.py
index f819371..2f4e847 100644
--- a/google/oauth2/_client.py
+++ b/google/oauth2/_client.py
@@ -24,9 +24,11 @@
 """
 
 import datetime
-import http.client
 import json
-import urllib
+
+import six
+from six.moves import http_client
+from six.moves import urllib
 
 from google.auth import _helpers
 from google.auth import exceptions
@@ -118,7 +120,7 @@
         )
         response_data = json.loads(response_body)
 
-        if response.status == http.client.OK:
+        if response.status == http_client.OK:
             break
         else:
             error_desc = response_data.get("error_description") or ""
@@ -129,9 +131,9 @@
             ):
                 retry += 1
                 continue
-            return response.status == http.client.OK, response_data
+            return response.status == http_client.OK, response_data
 
-    return response.status == http.client.OK, response_data
+    return response.status == http_client.OK, response_data
 
 
 def _token_endpoint_request(
@@ -194,7 +196,7 @@
         access_token = response_data["access_token"]
     except KeyError as caught_exc:
         new_exc = exceptions.RefreshError("No access token in response.", response_data)
-        raise new_exc from caught_exc
+        six.raise_from(new_exc, caught_exc)
 
     expiry = _parse_expiry(response_data)
 
@@ -234,7 +236,7 @@
         id_token = response_data["id_token"]
     except KeyError as caught_exc:
         new_exc = exceptions.RefreshError("No ID token in response.", response_data)
-        raise new_exc from caught_exc
+        six.raise_from(new_exc, caught_exc)
 
     payload = jwt.decode(id_token, verify=False)
     expiry = datetime.datetime.utcfromtimestamp(payload["exp"])
@@ -263,7 +265,7 @@
         access_token = response_data["access_token"]
     except KeyError as caught_exc:
         new_exc = exceptions.RefreshError("No access token in response.", response_data)
-        raise new_exc from caught_exc
+        six.raise_from(new_exc, caught_exc)
 
     refresh_token = response_data.get("refresh_token", refresh_token)
     expiry = _parse_expiry(response_data)
diff --git a/google/oauth2/_client_async.py b/google/oauth2/_client_async.py
index 8849023..cf51211 100644
--- a/google/oauth2/_client_async.py
+++ b/google/oauth2/_client_async.py
@@ -24,9 +24,11 @@
 """
 
 import datetime
-import http.client
 import json
-import urllib
+
+import six
+from six.moves import http_client
+from six.moves import urllib
 
 from google.auth import exceptions
 from google.auth import jwt
@@ -83,7 +85,7 @@
 
         response_data = json.loads(response_body)
 
-        if response.status == http.client.OK:
+        if response.status == http_client.OK:
             break
         else:
             error_desc = response_data.get("error_description") or ""
@@ -94,9 +96,9 @@
             ):
                 retry += 1
                 continue
-            return response.status == http.client.OK, response_data
+            return response.status == http_client.OK, response_data
 
-    return response.status == http.client.OK, response_data
+    return response.status == http_client.OK, response_data
 
 
 async def _token_endpoint_request(
@@ -159,7 +161,7 @@
         access_token = response_data["access_token"]
     except KeyError as caught_exc:
         new_exc = exceptions.RefreshError("No access token in response.", response_data)
-        raise new_exc from caught_exc
+        six.raise_from(new_exc, caught_exc)
 
     expiry = client._parse_expiry(response_data)
 
@@ -199,7 +201,7 @@
         id_token = response_data["id_token"]
     except KeyError as caught_exc:
         new_exc = exceptions.RefreshError("No ID token in response.", response_data)
-        raise new_exc from caught_exc
+        six.raise_from(new_exc, caught_exc)
 
     payload = jwt.decode(id_token, verify=False)
     expiry = datetime.datetime.utcfromtimestamp(payload["exp"])
diff --git a/google/oauth2/_id_token_async.py b/google/oauth2/_id_token_async.py
index a4a526d..31fcbc6 100644
--- a/google/oauth2/_id_token_async.py
+++ b/google/oauth2/_id_token_async.py
@@ -58,10 +58,12 @@
 .. _CacheControl: https://cachecontrol.readthedocs.io
 """
 
-import http.client
 import json
 import os
 
+import six
+from six.moves import http_client
+
 from google.auth import environment_vars
 from google.auth import exceptions
 from google.auth import jwt
@@ -86,7 +88,7 @@
     """
     response = await request(certs_url, method="GET")
 
-    if response.status != http.client.OK:
+    if response.status != http_client.OK:
         raise exceptions.TransportError(
             "Could not fetch certificates at {}".format(certs_url)
         )
@@ -241,7 +243,7 @@
                 "GOOGLE_APPLICATION_CREDENTIALS is not valid service account credentials.",
                 caught_exc,
             )
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
     # 2. Try to fetch ID token from metada server if it exists. The code works
     # for GAE and Cloud Run metadata server as well.
diff --git a/google/oauth2/_reauth_async.py b/google/oauth2/_reauth_async.py
index f74f50b..0276ddd 100644
--- a/google/oauth2/_reauth_async.py
+++ b/google/oauth2/_reauth_async.py
@@ -34,6 +34,8 @@
 
 import sys
 
+from six.moves import range
+
 from google.auth import exceptions
 from google.oauth2 import _client
 from google.oauth2 import _client_async
diff --git a/google/oauth2/challenges.py b/google/oauth2/challenges.py
index 0baff62..95e76cb 100644
--- a/google/oauth2/challenges.py
+++ b/google/oauth2/challenges.py
@@ -20,6 +20,8 @@
 import getpass
 import sys
 
+import six
+
 from google.auth import _helpers
 from google.auth import exceptions
 
@@ -45,7 +47,8 @@
     return getpass.getpass(text)
 
 
-class ReauthChallenge(object, metaclass=abc.ABCMeta):
[email protected]_metaclass(abc.ABCMeta)
+class ReauthChallenge(object):
     """Base class for reauth challenges."""
 
     @property
diff --git a/google/oauth2/credentials.py b/google/oauth2/credentials.py
index 6d34edf..9b59f8c 100644
--- a/google/oauth2/credentials.py
+++ b/google/oauth2/credentials.py
@@ -35,6 +35,8 @@
 import io
 import json
 
+import six
+
 from google.auth import _cloud_sdk
 from google.auth import _helpers
 from google.auth import credentials
@@ -262,7 +264,7 @@
         if self._refresh_token is None and self.refresh_handler:
             token, expiry = self.refresh_handler(request, scopes=scopes)
             # Validate returned data.
-            if not isinstance(token, str):
+            if not isinstance(token, six.string_types):
                 raise exceptions.RefreshError(
                     "The refresh_handler returned token is not a string."
                 )
@@ -344,7 +346,7 @@
             ValueError: If the info is not in the expected format.
         """
         keys_needed = set(("refresh_token", "client_id", "client_secret"))
-        missing = keys_needed.difference(info)
+        missing = keys_needed.difference(six.iterkeys(info))
 
         if missing:
             raise ValueError(
@@ -364,7 +366,7 @@
         # process scopes, which needs to be a seq
         if scopes is None and "scopes" in info:
             scopes = info.get("scopes")
-            if isinstance(scopes, str):
+            if isinstance(scopes, six.string_types):
                 scopes = scopes.split(" ")
 
         return cls(
diff --git a/google/oauth2/id_token.py b/google/oauth2/id_token.py
index 25492ca..8d0f85a 100644
--- a/google/oauth2/id_token.py
+++ b/google/oauth2/id_token.py
@@ -55,10 +55,12 @@
 .. _CacheControl: https://cachecontrol.readthedocs.io
 """
 
-import http.client
 import json
 import os
 
+import six
+from six.moves import http_client
+
 from google.auth import environment_vars
 from google.auth import exceptions
 from google.auth import jwt
@@ -95,7 +97,7 @@
     """
     response = request(certs_url, method="GET")
 
-    if response.status != http.client.OK:
+    if response.status != http_client.OK:
         raise exceptions.TransportError(
             "Could not fetch certificates at {}".format(certs_url)
         )
@@ -240,7 +242,7 @@
                 "GOOGLE_APPLICATION_CREDENTIALS is not valid service account credentials.",
                 caught_exc,
             )
-            raise new_exc from caught_exc
+            six.raise_from(new_exc, caught_exc)
 
     # 2. Try to fetch ID token from metada server if it exists. The code
     # works for GAE and Cloud Run metadata server as well.
diff --git a/google/oauth2/reauth.py b/google/oauth2/reauth.py
index 1e496d1..cbf1d7f 100644
--- a/google/oauth2/reauth.py
+++ b/google/oauth2/reauth.py
@@ -34,6 +34,8 @@
 
 import sys
 
+from six.moves import range
+
 from google.auth import exceptions
 from google.oauth2 import _client
 from google.oauth2 import challenges
diff --git a/google/oauth2/sts.py b/google/oauth2/sts.py
index 9f2d68a..ae3c014 100644
--- a/google/oauth2/sts.py
+++ b/google/oauth2/sts.py
@@ -31,9 +31,10 @@
 .. _rfc8693 section 2.2.1: https://tools.ietf.org/html/rfc8693#section-2.2.1
 """
 
-import http.client
 import json
-import urllib
+
+from six.moves import http_client
+from six.moves import urllib
 
 from google.oauth2 import utils
 
@@ -145,7 +146,7 @@
         )
 
         # If non-200 response received, translate to OAuthError exception.
-        if response.status != http.client.OK:
+        if response.status != http_client.OK:
             utils.handle_error_response(response_body)
 
         response_data = json.loads(response_body)
diff --git a/google/oauth2/utils.py b/google/oauth2/utils.py
index c57833d..593f032 100644
--- a/google/oauth2/utils.py
+++ b/google/oauth2/utils.py
@@ -45,6 +45,8 @@
 import enum
 import json
 
+import six
+
 from google.auth import exceptions
 
 
@@ -75,7 +77,8 @@
         self.client_secret = client_secret
 
 
-class OAuthClientAuthHandler(object, metaclass=abc.ABCMeta):
[email protected]_metaclass(abc.ABCMeta)
+class OAuthClientAuthHandler(object):
     """Abstract class for handling client authentication in OAuth-based
     operations.
     """
diff --git a/noxfile.py b/noxfile.py
index e238c97..885dbd6 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -81,6 +81,23 @@
     )
 
 
[email protected](python=["2.7"])
+def unit_prev_versions(session):
+    constraints_path = str(
+        CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt"
+    )
+    session.install("-r", "testing/requirements.txt", "-c", constraints_path)
+    session.install("-e", ".", "-c", constraints_path)
+    session.run(
+        "pytest",
+        f"--junitxml=unit_{session.python}_sponge_log.xml",
+        "--cov=google.auth",
+        "--cov=google.oauth2",
+        "--cov=tests",
+        "tests",
+    )
+
+
 @nox.session(python="3.7")
 def cover(session):
     session.install("-r", "testing/requirements.txt")
diff --git a/setup.py b/setup.py
index 343e660..301e996 100644
--- a/setup.py
+++ b/setup.py
@@ -20,16 +20,25 @@
 
 
 DEPENDENCIES = (
-    "cachetools >= 2.0.0, < 5.0",
-    "pyasn1-modules >= 0.2.1",
-    "rsa >= 3.1.4, < 5",
-    "setuptools >= 40.3.0",
+    "cachetools>=2.0.0,<5.0",
+    "pyasn1-modules>=0.2.1",
+    # rsa==4.5 is the last version to support 2.7
+    # https://github.com/sybrenstuvel/python-rsa/issues/152#issuecomment-643470233
+    'rsa<4.6; python_version < "3.6"',
+    'rsa>=3.1.4,<5; python_version >= "3.6"',
+    # install enum34 to support 2.7. enum34 only works up to python version 3.3.
+    'enum34>=1.1.10; python_version < "3.4"',
+    "setuptools>=40.3.0",
+    "six>=1.9.0",
 )
 
 extras = {
-    "aiohttp": ["aiohttp >= 3.6.2, < 4.0.0dev", "requests >= 2.20.0, < 3.0.0dev"],
-    "pyopenssl": "pyopenssl >= 20.0.0",
-    "reauth": "pyu2f >= 0.1.5",
+    "aiohttp": [
+        "aiohttp >= 3.6.2, < 4.0.0dev; python_version>='3.6'",
+        "requests >= 2.20.0, < 3.0.0dev",
+    ],
+    "pyopenssl": "pyopenssl>=20.0.0",
+    "reauth": "pyu2f>=0.1.5",
 }
 
 with io.open("README.rst", "r") as fh:
@@ -54,7 +63,7 @@
     namespace_packages=("google",),
     install_requires=DEPENDENCIES,
     extras_require=extras,
-    python_requires=">= 3.6",
+    python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*",
     license="Apache 2.0",
     keywords="google auth oauth client",
     classifiers=[
diff --git a/system_tests/noxfile.py b/system_tests/noxfile.py
index 540727e..459b71c 100644
--- a/system_tests/noxfile.py
+++ b/system_tests/noxfile.py
@@ -171,7 +171,7 @@
 TEST_DEPENDENCIES_ASYNC = ["aiohttp", "pytest-asyncio", "nest-asyncio"]
 TEST_DEPENDENCIES_SYNC = ["pytest", "requests", "mock"]
 PYTHON_VERSIONS_ASYNC = ["3.7"]
-PYTHON_VERSIONS_SYNC = ["3.7"]
+PYTHON_VERSIONS_SYNC = ["2.7", "3.7"]
 
 
 def default(session, *test_paths):
@@ -287,6 +287,50 @@
     )
 
 
[email protected](python=["2.7"])
+def app_engine(session):
+    if SKIP_GAE_TEST_ENV in os.environ:
+        session.log("Skipping App Engine tests.")
+        return
+
+    session.install(LIBRARY_DIR)
+    # Unlike the default tests above, the App Engine system test require a
+    # 'real' gcloud sdk installation that is configured to deploy to an
+    # app engine project.
+    # Grab the project ID from the cloud sdk.
+    project_id = (
+        subprocess.check_output(
+            ["gcloud", "config", "list", "project", "--format", "value(core.project)"]
+        )
+        .decode("utf-8")
+        .strip()
+    )
+
+    if not project_id:
+        session.error(
+            "The Cloud SDK must be installed and configured to deploy to App " "Engine."
+        )
+
+    application_url = GAE_APP_URL_TMPL.format(GAE_TEST_APP_SERVICE, project_id)
+
+    # Vendor in the test application's dependencies
+    session.chdir(os.path.join(HERE, "system_tests_sync/app_engine_test_app"))
+    session.install(*TEST_DEPENDENCIES_SYNC)
+    session.run(
+        "pip", "install", "--target", "lib", "-r", "requirements.txt", silent=True
+    )
+
+    # Deploy the application.
+    session.run("gcloud", "app", "deploy", "-q", "app.yaml")
+
+    # Run the tests
+    session.env["TEST_APP_URL"] = application_url
+    session.chdir(HERE)
+    default(
+        session, "system_tests_sync/test_app_engine.py",
+    )
+
+
 @nox.session(python=PYTHON_VERSIONS_SYNC)
 def grpc(session):
     session.install(LIBRARY_DIR)
@@ -339,9 +383,8 @@
 def external_accounts(session):
     session.install(
         *TEST_DEPENDENCIES_SYNC,
-        "google-auth",
+        LIBRARY_DIR,
         "google-api-python-client",
-        "enum34",
     )
     default(
         session,
@@ -354,7 +397,7 @@
 def downscoping(session):
     session.install(
         *TEST_DEPENDENCIES_SYNC,
-        "google-auth",
+        LIBRARY_DIR,
         "google-cloud-storage",
     )
     default(
diff --git a/system_tests/system_tests_sync/app_engine_test_app/.gitignore b/system_tests/system_tests_sync/app_engine_test_app/.gitignore
new file mode 100644
index 0000000..7951405
--- /dev/null
+++ b/system_tests/system_tests_sync/app_engine_test_app/.gitignore
@@ -0,0 +1 @@
+lib
\ No newline at end of file
diff --git a/system_tests/system_tests_sync/app_engine_test_app/app.yaml b/system_tests/system_tests_sync/app_engine_test_app/app.yaml
new file mode 100644
index 0000000..06f2270
--- /dev/null
+++ b/system_tests/system_tests_sync/app_engine_test_app/app.yaml
@@ -0,0 +1,12 @@
+api_version: 1
+service: google-auth-system-tests
+runtime: python27
+threadsafe: true
+
+handlers:
+- url: .*
+  script: main.app
+
+libraries:
+- name: ssl
+  version: 2.7.11
\ No newline at end of file
diff --git a/system_tests/system_tests_sync/app_engine_test_app/appengine_config.py b/system_tests/system_tests_sync/app_engine_test_app/appengine_config.py
new file mode 100644
index 0000000..1197ab5
--- /dev/null
+++ b/system_tests/system_tests_sync/app_engine_test_app/appengine_config.py
@@ -0,0 +1,30 @@
+# Copyright 2016 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.
+
+from google.appengine.ext import vendor
+
+# Add any libraries installed in the "lib" folder.
+vendor.add("lib")
+
+
+# Patch os.path.expanduser. This should be fixed in GAE
+# versions released after Nov 2016.
+import os.path
+
+
+def patched_expanduser(path):
+    return path
+
+
+os.path.expanduser = patched_expanduser
\ No newline at end of file
diff --git a/system_tests/system_tests_sync/app_engine_test_app/main.py b/system_tests/system_tests_sync/app_engine_test_app/main.py
new file mode 100644
index 0000000..f44ed4c
--- /dev/null
+++ b/system_tests/system_tests_sync/app_engine_test_app/main.py
@@ -0,0 +1,129 @@
+# Copyright 2016 Google LLC All Rights Reserved.
+#
+# 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.
+
+"""App Engine standard application that runs basic system tests for
+google.auth.app_engine.
+This application has to run tests manually instead of using pytest because
+pytest currently doesn't work on App Engine standard.
+"""
+
+import contextlib
+import json
+import sys
+from StringIO import StringIO
+import traceback
+
+from google.appengine.api import app_identity
+import google.auth
+from google.auth import _helpers
+from google.auth import app_engine
+import google.auth.transport.urllib3
+import urllib3.contrib.appengine
+import webapp2
+
+FAILED_TEST_TMPL = """
+Test {} failed: {}
+Stacktrace:
+{}
+Captured output:
+{}
+"""
+TOKEN_INFO_URL = "https://www.googleapis.com/oauth2/v3/tokeninfo"
+EMAIL_SCOPE = "https://www.googleapis.com/auth/userinfo.email"
+HTTP = urllib3.contrib.appengine.AppEngineManager()
+HTTP_REQUEST = google.auth.transport.urllib3.Request(HTTP)
+
+
+def test_credentials():
+    credentials = app_engine.Credentials()
+    scoped_credentials = credentials.with_scopes([EMAIL_SCOPE])
+
+    scoped_credentials.refresh(None)
+
+    assert scoped_credentials.valid
+    assert scoped_credentials.token is not None
+
+    # Get token info and verify scope
+    url = _helpers.update_query(
+        TOKEN_INFO_URL, {"access_token": scoped_credentials.token}
+    )
+    response = HTTP_REQUEST(url=url, method="GET")
+    token_info = json.loads(response.data.decode("utf-8"))
+
+    assert token_info["scope"] == EMAIL_SCOPE
+
+
+def test_default():
+    credentials, project_id = google.auth.default()
+
+    assert isinstance(credentials, app_engine.Credentials)
+    assert project_id == app_identity.get_application_id()
+
+
[email protected]
+def capture():
+    """Context manager that captures stderr and stdout."""
+    oldout, olderr = sys.stdout, sys.stderr
+    try:
+        out = StringIO()
+        sys.stdout, sys.stderr = out, out
+        yield out
+    finally:
+        sys.stdout, sys.stderr = oldout, olderr
+
+
+def run_test_func(func):
+    with capture() as capsys:
+        try:
+            func()
+            return True, ""
+        except Exception as exc:
+            output = FAILED_TEST_TMPL.format(
+                func.func_name, exc, traceback.format_exc(), capsys.getvalue()
+            )
+            return False, output
+
+
+def run_tests():
+    """Runs all tests.
+    Returns:
+        Tuple[bool, str]: A tuple containing True if all tests pass, False
+        otherwise, and any captured output from the tests.
+    """
+    status = True
+    output = ""
+
+    tests = (test_credentials, test_default)
+
+    for test in tests:
+        test_status, test_output = run_test_func(test)
+        status = status and test_status
+        output += test_output
+
+    return status, output
+
+
+class MainHandler(webapp2.RequestHandler):
+    def get(self):
+        self.response.headers["content-type"] = "text/plain"
+
+        status, output = run_tests()
+
+        if not status:
+            self.response.status = 500
+
+        self.response.write(output)
+
+
+app = webapp2.WSGIApplication([("/", MainHandler)], debug=True)
\ No newline at end of file
diff --git a/system_tests/system_tests_sync/app_engine_test_app/requirements.txt b/system_tests/system_tests_sync/app_engine_test_app/requirements.txt
new file mode 100644
index 0000000..cb8a382
--- /dev/null
+++ b/system_tests/system_tests_sync/app_engine_test_app/requirements.txt
@@ -0,0 +1,3 @@
+urllib3
+# Relative path to google-auth-python's source.
+../../..
\ No newline at end of file
diff --git a/system_tests/system_tests_sync/test_app_engine.py b/system_tests/system_tests_sync/test_app_engine.py
new file mode 100644
index 0000000..79776ce
--- /dev/null
+++ b/system_tests/system_tests_sync/test_app_engine.py
@@ -0,0 +1,22 @@
+# Copyright 2016 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.
+
+import os
+
+TEST_APP_URL = os.environ["TEST_APP_URL"]
+
+
+def test_live_application(http_request):
+    response = http_request(method="GET", url=TEST_APP_URL)
+    assert response.status == 200, response.data.decode("utf-8")
\ No newline at end of file
diff --git a/system_tests/system_tests_sync/test_downscoping.py b/system_tests/system_tests_sync/test_downscoping.py
index 77224ae..fdb4efa 100644
--- a/system_tests/system_tests_sync/test_downscoping.py
+++ b/system_tests/system_tests_sync/test_downscoping.py
@@ -28,7 +28,7 @@
  # The object prefix used to test access to files beginning with this prefix.
 _OBJECT_PREFIX = "customer-a"
 # The object name of the object inaccessible by the downscoped token.
-_ACCESSIBLE_OBJECT_NAME = f"{_OBJECT_PREFIX}-data.txt"
+_ACCESSIBLE_OBJECT_NAME = "{0}-data.txt".format(_OBJECT_PREFIX)
 # The content of the object accessible by the downscoped token.
 _ACCESSIBLE_CONTENT = "hello world"
 # The content of the object inaccessible by the downscoped token.
@@ -76,13 +76,13 @@
         Tuple[str, datetime.datetime]: The downscoped access token and its expiry date.
     """
     # Initialize the Credential Access Boundary rules.
-    available_resource = f"//storage.googleapis.com/projects/_/buckets/{bucket_name}"
+    available_resource = "//storage.googleapis.com/projects/_/buckets/{0}".format(bucket_name)
     # Downscoped credentials will have readonly access to the resource.
     available_permissions = ["inRole:roles/storage.objectViewer"]
     # Only objects starting with the specified prefix string in the object name
     # will be allowed read access.
     availability_expression = (
-        f"resource.name.startsWith('projects/_/buckets/{bucket_name}/objects/{object_prefix}')"
+        "resource.name.startsWith('projects/_/buckets/{0}/objects/{1}')".format(bucket_name, object_prefix)
     )
     availability_condition = downscoped.AvailabilityCondition(availability_expression)
     # Define the single access boundary rule using the above properties.
diff --git a/system_tests/system_tests_sync/test_external_accounts.py b/system_tests/system_tests_sync/test_external_accounts.py
index c2855a2..e24c7b4 100644
--- a/system_tests/system_tests_sync/test_external_accounts.py
+++ b/system_tests/system_tests_sync/test_external_accounts.py
@@ -32,21 +32,19 @@
 # original service account key.
 
 
-from http.server import BaseHTTPRequestHandler
-from http.server import HTTPServer
 import json
 import os
 import socket
-import sys
 from tempfile import NamedTemporaryFile
 import threading
 
-import pytest
-from mock import patch
-
+import sys
 import google.auth
 from googleapiclient import discovery
+from six.moves import BaseHTTPServer
 from google.oauth2 import service_account
+import pytest
+from mock import patch
 
 # Populate values from the output of scripts/setup_external_accounts.sh.
 _AUDIENCE_OIDC = "//iam.googleapis.com/projects/79992041559/locations/global/workloadIdentityPools/pool-73wslmxn/providers/oidc-73wslmxn"
@@ -177,7 +175,7 @@
 # This test makes sure that setting up an http server to provide credentials
 # works to allow access to Google resources.
 def test_url_based_external_account(dns_access, oidc_credentials, service_account_info):
-    class TestResponseHandler(BaseHTTPRequestHandler):
+    class TestResponseHandler(BaseHTTPServer.BaseHTTPRequestHandler):
         def do_GET(self):
             if self.headers["my-header"] != "expected-value":
                 self.send_response(400)
@@ -201,7 +199,7 @@
                     json.dumps({"access_token": oidc_credentials.token}).encode("utf-8")
                 )
 
-    class TestHTTPServer(HTTPServer, object):
+    class TestHTTPServer(BaseHTTPServer.HTTPServer, object):
         def __init__(self):
             self.port = self._find_open_port()
             super(TestHTTPServer, self).__init__(("", self.port), TestResponseHandler)
diff --git a/testing/constraints-2.7.txt b/testing/constraints-2.7.txt
new file mode 100644
index 0000000..dcc09f7
--- /dev/null
+++ b/testing/constraints-2.7.txt
@@ -0,0 +1 @@
+rsa==3.1.4
\ No newline at end of file
diff --git a/tests/compute_engine/test__metadata.py b/tests/compute_engine/test__metadata.py
index 0bb07b0..852822d 100644
--- a/tests/compute_engine/test__metadata.py
+++ b/tests/compute_engine/test__metadata.py
@@ -13,13 +13,13 @@
 # limitations under the License.
 
 import datetime
-import http.client
-import importlib
 import json
 import os
 
 import mock
 import pytest
+from six.moves import http_client
+from six.moves import reload_module
 
 from google.auth import _helpers
 from google.auth import environment_vars
@@ -30,7 +30,7 @@
 PATH = "instance/service-accounts/default"
 
 
-def make_request(data, status=http.client.OK, headers=None, retry=False):
+def make_request(data, status=http_client.OK, headers=None, retry=False):
     response = mock.create_autospec(transport.Response, instance=True)
     response.status = status
     response.data = _helpers.to_bytes(data)
@@ -90,13 +90,13 @@
 
     fake_ip = "1.2.3.4"
     os.environ[environment_vars.GCE_METADATA_IP] = fake_ip
-    importlib.reload(_metadata)
+    reload_module(_metadata)
 
     try:
         assert _metadata.ping(request)
     finally:
         del os.environ[environment_vars.GCE_METADATA_IP]
-        importlib.reload(_metadata)
+        reload_module(_metadata)
 
     request.assert_called_once_with(
         method="GET",
@@ -203,13 +203,13 @@
 
     fake_root = "another.metadata.service"
     os.environ[environment_vars.GCE_METADATA_HOST] = fake_root
-    importlib.reload(_metadata)
+    reload_module(_metadata)
 
     try:
         _metadata.get(request, PATH)
     finally:
         del os.environ[environment_vars.GCE_METADATA_HOST]
-        importlib.reload(_metadata)
+        reload_module(_metadata)
 
     request.assert_called_once_with(
         method="GET",
@@ -223,13 +223,13 @@
 
     fake_root = "another.metadata.service"
     os.environ[environment_vars.GCE_METADATA_ROOT] = fake_root
-    importlib.reload(_metadata)
+    reload_module(_metadata)
 
     try:
         _metadata.get(request, PATH)
     finally:
         del os.environ[environment_vars.GCE_METADATA_ROOT]
-        importlib.reload(_metadata)
+        reload_module(_metadata)
 
     request.assert_called_once_with(
         method="GET",
@@ -239,7 +239,7 @@
 
 
 def test_get_failure():
-    request = make_request("Metadata error", status=http.client.NOT_FOUND)
+    request = make_request("Metadata error", status=http_client.NOT_FOUND)
 
     with pytest.raises(exceptions.TransportError) as excinfo:
         _metadata.get(request, PATH)
diff --git a/tests/crypt/test__cryptography_rsa.py b/tests/crypt/test__cryptography_rsa.py
index 41dfc36..dbf07c7 100644
--- a/tests/crypt/test__cryptography_rsa.py
+++ b/tests/crypt/test__cryptography_rsa.py
@@ -60,7 +60,7 @@
 
 
 class TestRSAVerifier(object):
-    def test_verify_bytes_success(self):
+    def test_verify_success(self):
         to_sign = b"foo"
         signer = _cryptography_rsa.RSASigner.from_string(PRIVATE_KEY_BYTES)
         actual_signature = signer.sign(to_sign)
@@ -68,8 +68,8 @@
         verifier = _cryptography_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES)
         assert verifier.verify(to_sign, actual_signature)
 
-    def test_verify_text_success(self):
-        to_sign = "foo"
+    def test_verify_unicode_success(self):
+        to_sign = u"foo"
         signer = _cryptography_rsa.RSASigner.from_string(PRIVATE_KEY_BYTES)
         actual_signature = signer.sign(to_sign)
 
diff --git a/tests/crypt/test__python_rsa.py b/tests/crypt/test__python_rsa.py
index 9ef29ee..886ee55 100644
--- a/tests/crypt/test__python_rsa.py
+++ b/tests/crypt/test__python_rsa.py
@@ -12,7 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import io
 import json
 import os
 
@@ -20,6 +19,7 @@
 from pyasn1_modules import pem
 import pytest
 import rsa
+import six
 
 from google.auth import _helpers
 from google.auth.crypt import _python_rsa
@@ -63,7 +63,7 @@
 
 
 class TestRSAVerifier(object):
-    def test_verify_bytes_success(self):
+    def test_verify_success(self):
         to_sign = b"foo"
         signer = _python_rsa.RSASigner.from_string(PRIVATE_KEY_BYTES)
         actual_signature = signer.sign(to_sign)
@@ -71,8 +71,8 @@
         verifier = _python_rsa.RSAVerifier.from_string(PUBLIC_KEY_BYTES)
         assert verifier.verify(to_sign, actual_signature)
 
-    def test_verify_text_success(self):
-        to_sign = "foo"
+    def test_verify_unicode_success(self):
+        to_sign = u"foo"
         signer = _python_rsa.RSASigner.from_string(PRIVATE_KEY_BYTES)
         actual_signature = signer.sign(to_sign)
 
@@ -141,7 +141,7 @@
     def test_from_string_pkcs8_extra_bytes(self):
         key_bytes = PKCS8_KEY_BYTES
         _, pem_bytes = pem.readPemBlocksFromFile(
-            io.StringIO(_helpers.from_bytes(key_bytes)), _python_rsa._PKCS8_MARKER
+            six.StringIO(_helpers.from_bytes(key_bytes)), _python_rsa._PKCS8_MARKER
         )
 
         key_info, remaining = None, "extra"
diff --git a/tests/crypt/test_es256.py b/tests/crypt/test_es256.py
index 720f74c..5bb9050 100644
--- a/tests/crypt/test_es256.py
+++ b/tests/crypt/test_es256.py
@@ -50,7 +50,7 @@
 
 
 class TestES256Verifier(object):
-    def test_verify_bytes_success(self):
+    def test_verify_success(self):
         to_sign = b"foo"
         signer = es256.ES256Signer.from_string(PRIVATE_KEY_BYTES)
         actual_signature = signer.sign(to_sign)
@@ -58,8 +58,8 @@
         verifier = es256.ES256Verifier.from_string(PUBLIC_KEY_BYTES)
         assert verifier.verify(to_sign, actual_signature)
 
-    def test_verify_text_success(self):
-        to_sign = "foo"
+    def test_verify_unicode_success(self):
+        to_sign = u"foo"
         signer = es256.ES256Signer.from_string(PRIVATE_KEY_BYTES)
         actual_signature = signer.sign(to_sign)
 
diff --git a/tests/oauth2/test__client.py b/tests/oauth2/test__client.py
index 690a87b..54686df 100644
--- a/tests/oauth2/test__client.py
+++ b/tests/oauth2/test__client.py
@@ -13,13 +13,14 @@
 # limitations under the License.
 
 import datetime
-import http.client
 import json
 import os
-import urllib
 
 import mock
 import pytest
+import six
+from six.moves import http_client
+from six.moves import urllib
 
 from google.auth import _helpers
 from google.auth import crypt
@@ -74,7 +75,7 @@
     assert _client._parse_expiry({}) is None
 
 
-def make_request(response_data, status=http.client.OK):
+def make_request(response_data, status=http_client.OK):
     response = mock.create_autospec(transport.Response, instance=True)
     response.status = status
     response.data = json.dumps(response_data).encode("utf-8")
@@ -129,7 +130,7 @@
 
 
 def test__token_endpoint_request_error():
-    request = make_request({}, status=http.client.BAD_REQUEST)
+    request = make_request({}, status=http_client.BAD_REQUEST)
 
     with pytest.raises(exceptions.RefreshError):
         _client._token_endpoint_request(request, "http://example.com", {})
@@ -137,7 +138,7 @@
 
 def test__token_endpoint_request_internal_failure_error():
     request = make_request(
-        {"error_description": "internal_failure"}, status=http.client.BAD_REQUEST
+        {"error_description": "internal_failure"}, status=http_client.BAD_REQUEST
     )
 
     with pytest.raises(exceptions.RefreshError):
@@ -146,7 +147,7 @@
         )
 
     request = make_request(
-        {"error": "internal_failure"}, status=http.client.BAD_REQUEST
+        {"error": "internal_failure"}, status=http_client.BAD_REQUEST
     )
 
     with pytest.raises(exceptions.RefreshError):
@@ -159,7 +160,7 @@
     request_body = request.call_args[1]["body"].decode("utf-8")
     request_params = urllib.parse.parse_qs(request_body)
 
-    for key, value in params.items():
+    for key, value in six.iteritems(params):
         assert request_params[key][0] == value
 
 
diff --git a/tests/oauth2/test_sts.py b/tests/oauth2/test_sts.py
index b516c8a..e8e008d 100644
--- a/tests/oauth2/test_sts.py
+++ b/tests/oauth2/test_sts.py
@@ -12,12 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import http.client
 import json
-import urllib
 
 import mock
 import pytest
+from six.moves import http_client
+from six.moves import urllib
 
 from google.auth import exceptions
 from google.auth import transport
@@ -67,7 +67,7 @@
         return sts.Client(cls.TOKEN_EXCHANGE_ENDPOINT, client_auth)
 
     @classmethod
-    def make_mock_request(cls, data, status=http.client.OK):
+    def make_mock_request(cls, data, status=http_client.OK):
         response = mock.create_autospec(transport.Response, instance=True)
         response.status = status
         response.data = json.dumps(data).encode("utf-8")
@@ -110,7 +110,7 @@
             "options": urllib.parse.quote(json.dumps(self.ADDON_OPTIONS)),
         }
         request = self.make_mock_request(
-            status=http.client.OK, data=self.SUCCESS_RESPONSE
+            status=http_client.OK, data=self.SUCCESS_RESPONSE
         )
 
         response = client.exchange_token(
@@ -145,7 +145,7 @@
             "subject_token_type": self.SUBJECT_TOKEN_TYPE,
         }
         request = self.make_mock_request(
-            status=http.client.OK, data=self.SUCCESS_RESPONSE
+            status=http_client.OK, data=self.SUCCESS_RESPONSE
         )
 
         response = client.exchange_token(
@@ -165,7 +165,7 @@
         """
         client = self.make_client()
         request = self.make_mock_request(
-            status=http.client.BAD_REQUEST, data=self.ERROR_RESPONSE
+            status=http_client.BAD_REQUEST, data=self.ERROR_RESPONSE
         )
 
         with pytest.raises(exceptions.OAuthError) as excinfo:
@@ -209,7 +209,7 @@
             "options": urllib.parse.quote(json.dumps(self.ADDON_OPTIONS)),
         }
         request = self.make_mock_request(
-            status=http.client.OK, data=self.SUCCESS_RESPONSE
+            status=http_client.OK, data=self.SUCCESS_RESPONSE
         )
 
         response = client.exchange_token(
@@ -247,7 +247,7 @@
             "subject_token_type": self.SUBJECT_TOKEN_TYPE,
         }
         request = self.make_mock_request(
-            status=http.client.OK, data=self.SUCCESS_RESPONSE
+            status=http_client.OK, data=self.SUCCESS_RESPONSE
         )
 
         response = client.exchange_token(
@@ -268,7 +268,7 @@
         """
         client = self.make_client(self.CLIENT_AUTH_BASIC)
         request = self.make_mock_request(
-            status=http.client.BAD_REQUEST, data=self.ERROR_RESPONSE
+            status=http_client.BAD_REQUEST, data=self.ERROR_RESPONSE
         )
 
         with pytest.raises(exceptions.OAuthError) as excinfo:
@@ -313,7 +313,7 @@
             "client_secret": CLIENT_SECRET,
         }
         request = self.make_mock_request(
-            status=http.client.OK, data=self.SUCCESS_RESPONSE
+            status=http_client.OK, data=self.SUCCESS_RESPONSE
         )
 
         response = client.exchange_token(
@@ -350,7 +350,7 @@
             "client_secret": CLIENT_SECRET,
         }
         request = self.make_mock_request(
-            status=http.client.OK, data=self.SUCCESS_RESPONSE
+            status=http_client.OK, data=self.SUCCESS_RESPONSE
         )
 
         response = client.exchange_token(
@@ -371,7 +371,7 @@
         """
         client = self.make_client(self.CLIENT_AUTH_REQUEST_BODY)
         request = self.make_mock_request(
-            status=http.client.BAD_REQUEST, data=self.ERROR_RESPONSE
+            status=http_client.BAD_REQUEST, data=self.ERROR_RESPONSE
         )
 
         with pytest.raises(exceptions.OAuthError) as excinfo:
diff --git a/tests/test__helpers.py b/tests/test__helpers.py
index 906cf12..0c0bad2 100644
--- a/tests/test__helpers.py
+++ b/tests/test__helpers.py
@@ -13,9 +13,9 @@
 # limitations under the License.
 
 import datetime
-import urllib
 
 import pytest
+from six.moves import urllib
 
 from google.auth import _helpers
 
@@ -65,8 +65,8 @@
     assert _helpers.to_bytes(value) == value
 
 
-def test_to_bytes_with_text():
-    value = "string-val"
+def test_to_bytes_with_unicode():
+    value = u"string-val"
     encoded_value = b"string-val"
     assert _helpers.to_bytes(value) == encoded_value
 
@@ -76,14 +76,14 @@
         _helpers.to_bytes(object())
 
 
-def test_from_bytes_with_text():
-    value = "bytes-val"
+def test_from_bytes_with_unicode():
+    value = u"bytes-val"
     assert _helpers.from_bytes(value) == value
 
 
 def test_from_bytes_with_bytes():
     value = b"string-val"
-    decoded_value = "string-val"
+    decoded_value = u"string-val"
     assert _helpers.from_bytes(value) == decoded_value
 
 
diff --git a/tests/test__oauth2client.py b/tests/test__oauth2client.py
index aa06ece..6b1112b 100644
--- a/tests/test__oauth2client.py
+++ b/tests/test__oauth2client.py
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 import datetime
-import importlib
 import os
 import sys
 
@@ -22,6 +21,7 @@
 import oauth2client.contrib.gce
 import oauth2client.service_account
 import pytest
+from six.moves import reload_module
 
 from google.auth import _oauth2client
 
@@ -152,19 +152,19 @@
 @pytest.fixture
 def reset__oauth2client_module():
     """Reloads the _oauth2client module after a test."""
-    importlib.reload(_oauth2client)
+    reload_module(_oauth2client)
 
 
 def test_import_has_app_engine(
     mock_oauth2client_gae_imports, reset__oauth2client_module
 ):
-    importlib.reload(_oauth2client)
+    reload_module(_oauth2client)
     assert _oauth2client._HAS_APPENGINE
 
 
 def test_import_without_oauth2client(monkeypatch, reset__oauth2client_module):
     monkeypatch.setitem(sys.modules, "oauth2client", None)
     with pytest.raises(ImportError) as excinfo:
-        importlib.reload(_oauth2client)
+        reload_module(_oauth2client)
 
     assert excinfo.match("oauth2client")
diff --git a/tests/test__service_account_info.py b/tests/test__service_account_info.py
index fd2d8c8..13b2f85 100644
--- a/tests/test__service_account_info.py
+++ b/tests/test__service_account_info.py
@@ -16,6 +16,7 @@
 import os
 
 import pytest
+import six
 
 from google.auth import _service_account_info
 from google.auth import crypt
@@ -54,7 +55,7 @@
 def test_from_filename():
     info, signer = _service_account_info.from_filename(SERVICE_ACCOUNT_JSON_FILE)
 
-    for key, value in SERVICE_ACCOUNT_INFO.items():
+    for key, value in six.iteritems(SERVICE_ACCOUNT_INFO):
         assert info[key] == value
 
     assert isinstance(signer, crypt.RSASigner)
diff --git a/tests/test_aws.py b/tests/test_aws.py
index 8659437..9ca08d5 100644
--- a/tests/test_aws.py
+++ b/tests/test_aws.py
@@ -13,12 +13,12 @@
 # limitations under the License.
 
 import datetime
-import http.client
 import json
-import urllib
 
 import mock
 import pytest
+from six.moves import http_client
+from six.moves import urllib
 
 from google.auth import _helpers
 from google.auth import aws
@@ -952,11 +952,11 @@
             self.AWS_SIGNATURE_TIME, "%Y-%m-%dT%H:%M:%SZ"
         )
         request = self.make_mock_request(
-            region_status=http.client.OK,
+            region_status=http_client.OK,
             region_name=self.AWS_REGION,
-            role_status=http.client.OK,
+            role_status=http_client.OK,
             role_name=self.AWS_ROLE,
-            security_credentials_status=http.client.OK,
+            security_credentials_status=http_client.OK,
             security_credentials_data=self.AWS_SECURITY_CREDENTIALS_RESPONSE,
         )
         credentials = self.make_credentials(credential_source=self.CREDENTIAL_SOURCE)
@@ -987,9 +987,9 @@
 
         # Retrieve subject_token again. Region should not be queried again.
         new_request = self.make_mock_request(
-            role_status=http.client.OK,
+            role_status=http_client.OK,
             role_name=self.AWS_ROLE,
-            security_credentials_status=http.client.OK,
+            security_credentials_status=http_client.OK,
             security_credentials_data=self.AWS_SECURITY_CREDENTIALS_RESPONSE,
         )
 
@@ -1020,11 +1020,11 @@
             self.AWS_SIGNATURE_TIME, "%Y-%m-%dT%H:%M:%SZ"
         )
         request = self.make_mock_request(
-            region_status=http.client.OK,
+            region_status=http_client.OK,
             region_name=self.AWS_REGION,
-            role_status=http.client.OK,
+            role_status=http_client.OK,
             role_name=self.AWS_ROLE,
-            security_credentials_status=http.client.OK,
+            security_credentials_status=http_client.OK,
             security_credentials_data=security_creds_response,
         )
         credentials = self.make_credentials(credential_source=self.CREDENTIAL_SOURCE)
@@ -1136,7 +1136,7 @@
         )
         # Region will be queried since it is not found in envvars.
         request = self.make_mock_request(
-            region_status=http.client.OK, region_name=self.AWS_REGION
+            region_status=http_client.OK, region_name=self.AWS_REGION
         )
         credentials = self.make_credentials(credential_source=self.CREDENTIAL_SOURCE)
 
@@ -1152,7 +1152,7 @@
 
     def test_retrieve_subject_token_error_determining_aws_region(self):
         # Simulate error in retrieving the AWS region.
-        request = self.make_mock_request(region_status=http.client.BAD_REQUEST)
+        request = self.make_mock_request(region_status=http_client.BAD_REQUEST)
         credentials = self.make_credentials(credential_source=self.CREDENTIAL_SOURCE)
 
         with pytest.raises(exceptions.RefreshError) as excinfo:
@@ -1163,9 +1163,9 @@
     def test_retrieve_subject_token_error_determining_aws_role(self):
         # Simulate error in retrieving the AWS role name.
         request = self.make_mock_request(
-            region_status=http.client.OK,
+            region_status=http_client.OK,
             region_name=self.AWS_REGION,
-            role_status=http.client.BAD_REQUEST,
+            role_status=http_client.BAD_REQUEST,
         )
         credentials = self.make_credentials(credential_source=self.CREDENTIAL_SOURCE)
 
@@ -1180,7 +1180,7 @@
         credential_source = self.CREDENTIAL_SOURCE.copy()
         credential_source.pop("url")
         request = self.make_mock_request(
-            region_status=http.client.OK, region_name=self.AWS_REGION
+            region_status=http_client.OK, region_name=self.AWS_REGION
         )
         credentials = self.make_credentials(credential_source=credential_source)
 
@@ -1194,11 +1194,11 @@
     def test_retrieve_subject_token_error_determining_aws_security_creds(self):
         # Simulate error in retrieving the AWS security credentials.
         request = self.make_mock_request(
-            region_status=http.client.OK,
+            region_status=http_client.OK,
             region_name=self.AWS_REGION,
-            role_status=http.client.OK,
+            role_status=http_client.OK,
             role_name=self.AWS_ROLE,
-            security_credentials_status=http.client.BAD_REQUEST,
+            security_credentials_status=http_client.BAD_REQUEST,
         )
         credentials = self.make_credentials(credential_source=self.CREDENTIAL_SOURCE)
 
@@ -1232,13 +1232,13 @@
             "subject_token_type": SUBJECT_TOKEN_TYPE,
         }
         request = self.make_mock_request(
-            region_status=http.client.OK,
+            region_status=http_client.OK,
             region_name=self.AWS_REGION,
-            role_status=http.client.OK,
+            role_status=http_client.OK,
             role_name=self.AWS_ROLE,
-            security_credentials_status=http.client.OK,
+            security_credentials_status=http_client.OK,
             security_credentials_data=self.AWS_SECURITY_CREDENTIALS_RESPONSE,
-            token_status=http.client.OK,
+            token_status=http_client.OK,
             token_data=self.SUCCESS_RESPONSE,
         )
         credentials = self.make_credentials(
@@ -1288,13 +1288,13 @@
             "subject_token_type": SUBJECT_TOKEN_TYPE,
         }
         request = self.make_mock_request(
-            region_status=http.client.OK,
+            region_status=http_client.OK,
             region_name=self.AWS_REGION,
-            role_status=http.client.OK,
+            role_status=http_client.OK,
             role_name=self.AWS_ROLE,
-            security_credentials_status=http.client.OK,
+            security_credentials_status=http_client.OK,
             security_credentials_data=self.AWS_SECURITY_CREDENTIALS_RESPONSE,
-            token_status=http.client.OK,
+            token_status=http_client.OK,
             token_data=self.SUCCESS_RESPONSE,
         )
         credentials = self.make_credentials(
@@ -1362,15 +1362,15 @@
             "lifetime": "3600s",
         }
         request = self.make_mock_request(
-            region_status=http.client.OK,
+            region_status=http_client.OK,
             region_name=self.AWS_REGION,
-            role_status=http.client.OK,
+            role_status=http_client.OK,
             role_name=self.AWS_ROLE,
-            security_credentials_status=http.client.OK,
+            security_credentials_status=http_client.OK,
             security_credentials_data=self.AWS_SECURITY_CREDENTIALS_RESPONSE,
-            token_status=http.client.OK,
+            token_status=http_client.OK,
             token_data=self.SUCCESS_RESPONSE,
-            impersonation_status=http.client.OK,
+            impersonation_status=http_client.OK,
             impersonation_data=impersonation_response,
         )
         credentials = self.make_credentials(
@@ -1446,15 +1446,15 @@
             "lifetime": "3600s",
         }
         request = self.make_mock_request(
-            region_status=http.client.OK,
+            region_status=http_client.OK,
             region_name=self.AWS_REGION,
-            role_status=http.client.OK,
+            role_status=http_client.OK,
             role_name=self.AWS_ROLE,
-            security_credentials_status=http.client.OK,
+            security_credentials_status=http_client.OK,
             security_credentials_data=self.AWS_SECURITY_CREDENTIALS_RESPONSE,
-            token_status=http.client.OK,
+            token_status=http_client.OK,
             token_data=self.SUCCESS_RESPONSE,
-            impersonation_status=http.client.OK,
+            impersonation_status=http_client.OK,
             impersonation_data=impersonation_response,
         )
         credentials = self.make_credentials(
@@ -1488,7 +1488,7 @@
         assert credentials.default_scopes == SCOPES
 
     def test_refresh_with_retrieve_subject_token_error(self):
-        request = self.make_mock_request(region_status=http.client.BAD_REQUEST)
+        request = self.make_mock_request(region_status=http_client.BAD_REQUEST)
         credentials = self.make_credentials(credential_source=self.CREDENTIAL_SOURCE)
 
         with pytest.raises(exceptions.RefreshError) as excinfo:
diff --git a/tests/test_downscoped.py b/tests/test_downscoped.py
index a686391..9ca95f5 100644
--- a/tests/test_downscoped.py
+++ b/tests/test_downscoped.py
@@ -13,12 +13,12 @@
 # limitations under the License.
 
 import datetime
-import http.client
 import json
-import urllib
 
 import mock
 import pytest
+from six.moves import http_client
+from six.moves import urllib
 
 from google.auth import _helpers
 from google.auth import credentials
@@ -461,7 +461,7 @@
         )
 
     @staticmethod
-    def make_mock_request(data, status=http.client.OK):
+    def make_mock_request(data, status=http_client.OK):
         response = mock.create_autospec(transport.Response, instance=True)
         response.status = status
         response.data = json.dumps(data).encode("utf-8")
@@ -521,7 +521,7 @@
             "requested_token_type": REQUESTED_TOKEN_TYPE,
             "options": urllib.parse.quote(json.dumps(CREDENTIAL_ACCESS_BOUNDARY_JSON)),
         }
-        request = self.make_mock_request(status=http.client.OK, data=response)
+        request = self.make_mock_request(status=http_client.OK, data=response)
         source_credentials = SourceCredentials()
         credentials = self.make_credentials(source_credentials=source_credentials)
 
@@ -563,7 +563,7 @@
             "requested_token_type": REQUESTED_TOKEN_TYPE,
             "options": urllib.parse.quote(json.dumps(CREDENTIAL_ACCESS_BOUNDARY_JSON)),
         }
-        request = self.make_mock_request(status=http.client.OK, data=response)
+        request = self.make_mock_request(status=http_client.OK, data=response)
         credentials = self.make_credentials(source_credentials=source_credentials)
 
         # Spy on calls to source credentials refresh to confirm the expected request
@@ -583,7 +583,7 @@
 
     def test_refresh_token_exchange_error(self):
         request = self.make_mock_request(
-            status=http.client.BAD_REQUEST, data=ERROR_RESPONSE
+            status=http_client.BAD_REQUEST, data=ERROR_RESPONSE
         )
         credentials = self.make_credentials()
 
@@ -612,7 +612,7 @@
 
     def test_apply_without_quota_project_id(self):
         headers = {}
-        request = self.make_mock_request(status=http.client.OK, data=SUCCESS_RESPONSE)
+        request = self.make_mock_request(status=http_client.OK, data=SUCCESS_RESPONSE)
         credentials = self.make_credentials()
 
         credentials.refresh(request)
@@ -624,7 +624,7 @@
 
     def test_apply_with_quota_project_id(self):
         headers = {"other": "header-value"}
-        request = self.make_mock_request(status=http.client.OK, data=SUCCESS_RESPONSE)
+        request = self.make_mock_request(status=http_client.OK, data=SUCCESS_RESPONSE)
         credentials = self.make_credentials(quota_project_id=QUOTA_PROJECT_ID)
 
         credentials.refresh(request)
@@ -638,7 +638,7 @@
 
     def test_before_request(self):
         headers = {"other": "header-value"}
-        request = self.make_mock_request(status=http.client.OK, data=SUCCESS_RESPONSE)
+        request = self.make_mock_request(status=http_client.OK, data=SUCCESS_RESPONSE)
         credentials = self.make_credentials()
 
         # First call should call refresh, setting the token.
@@ -662,7 +662,7 @@
     @mock.patch("google.auth._helpers.utcnow")
     def test_before_request_expired(self, utcnow):
         headers = {}
-        request = self.make_mock_request(status=http.client.OK, data=SUCCESS_RESPONSE)
+        request = self.make_mock_request(status=http_client.OK, data=SUCCESS_RESPONSE)
         credentials = self.make_credentials()
         credentials.token = "token"
         utcnow.return_value = datetime.datetime.min
diff --git a/tests/test_external_account.py b/tests/test_external_account.py
index 97f1564..3c34f99 100644
--- a/tests/test_external_account.py
+++ b/tests/test_external_account.py
@@ -13,12 +13,12 @@
 # limitations under the License.
 
 import datetime
-import http.client
 import json
-import urllib
 
 import mock
 import pytest
+from six.moves import http_client
+from six.moves import urllib
 
 from google.auth import _helpers
 from google.auth import exceptions
@@ -208,7 +208,7 @@
     @classmethod
     def make_mock_request(
         cls,
-        status=http.client.OK,
+        status=http_client.OK,
         data=None,
         impersonation_status=None,
         impersonation_data=None,
@@ -605,7 +605,7 @@
             "subject_token": "subject_token_0",
             "subject_token_type": self.SUBJECT_TOKEN_TYPE,
         }
-        request = self.make_mock_request(status=http.client.OK, data=response)
+        request = self.make_mock_request(status=http_client.OK, data=response)
         credentials = self.make_credentials()
 
         credentials.refresh(request)
@@ -635,7 +635,7 @@
                 json.dumps({"userProject": self.WORKFORCE_POOL_USER_PROJECT})
             ),
         }
-        request = self.make_mock_request(status=http.client.OK, data=response)
+        request = self.make_mock_request(status=http_client.OK, data=response)
         credentials = self.make_workforce_pool_credentials(
             workforce_pool_user_project=self.WORKFORCE_POOL_USER_PROJECT
         )
@@ -667,7 +667,7 @@
             "subject_token": "subject_token_0",
             "subject_token_type": self.WORKFORCE_SUBJECT_TOKEN_TYPE,
         }
-        request = self.make_mock_request(status=http.client.OK, data=response)
+        request = self.make_mock_request(status=http_client.OK, data=response)
         # Client Auth will have higher priority over workforce_pool_user_project.
         credentials = self.make_workforce_pool_credentials(
             client_id=CLIENT_ID,
@@ -704,7 +704,7 @@
             "subject_token": "subject_token_0",
             "subject_token_type": self.WORKFORCE_SUBJECT_TOKEN_TYPE,
         }
-        request = self.make_mock_request(status=http.client.OK, data=response)
+        request = self.make_mock_request(status=http_client.OK, data=response)
         # Client Auth will be sufficient for user project determination.
         credentials = self.make_workforce_pool_credentials(
             client_id=CLIENT_ID,
@@ -754,9 +754,9 @@
         # Initialize mock request to handle token exchange and service account
         # impersonation request.
         request = self.make_mock_request(
-            status=http.client.OK,
+            status=http_client.OK,
             data=token_response,
-            impersonation_status=http.client.OK,
+            impersonation_status=http_client.OK,
             impersonation_data=impersonation_response,
         )
         # Initialize credentials with service account impersonation.
@@ -821,9 +821,9 @@
         # Initialize mock request to handle token exchange and service account
         # impersonation request.
         request = self.make_mock_request(
-            status=http.client.OK,
+            status=http_client.OK,
             data=token_response,
-            impersonation_status=http.client.OK,
+            impersonation_status=http_client.OK,
             impersonation_data=impersonation_response,
         )
         # Initialize credentials with service account impersonation.
@@ -865,7 +865,7 @@
             "subject_token_type": self.SUBJECT_TOKEN_TYPE,
         }
         request = self.make_mock_request(
-            status=http.client.OK, data=self.SUCCESS_RESPONSE
+            status=http_client.OK, data=self.SUCCESS_RESPONSE
         )
         credentials = self.make_credentials(
             scopes=["scope1", "scope2"],
@@ -893,7 +893,7 @@
             "subject_token_type": self.SUBJECT_TOKEN_TYPE,
         }
         request = self.make_mock_request(
-            status=http.client.OK, data=self.SUCCESS_RESPONSE
+            status=http_client.OK, data=self.SUCCESS_RESPONSE
         )
         credentials = self.make_credentials(
             scopes=None,
@@ -911,7 +911,7 @@
 
     def test_refresh_without_client_auth_error(self):
         request = self.make_mock_request(
-            status=http.client.BAD_REQUEST, data=self.ERROR_RESPONSE
+            status=http_client.BAD_REQUEST, data=self.ERROR_RESPONSE
         )
         credentials = self.make_credentials()
 
@@ -926,9 +926,9 @@
 
     def test_refresh_impersonation_without_client_auth_error(self):
         request = self.make_mock_request(
-            status=http.client.OK,
+            status=http_client.OK,
             data=self.SUCCESS_RESPONSE,
-            impersonation_status=http.client.BAD_REQUEST,
+            impersonation_status=http_client.BAD_REQUEST,
             impersonation_data=self.IMPERSONATION_ERROR_RESPONSE,
         )
         credentials = self.make_credentials(
@@ -956,7 +956,7 @@
             "subject_token_type": self.SUBJECT_TOKEN_TYPE,
         }
         request = self.make_mock_request(
-            status=http.client.OK, data=self.SUCCESS_RESPONSE
+            status=http_client.OK, data=self.SUCCESS_RESPONSE
         )
         credentials = self.make_credentials(
             client_id=CLIENT_ID, client_secret=CLIENT_SECRET
@@ -1006,9 +1006,9 @@
         # Initialize mock request to handle token exchange and service account
         # impersonation request.
         request = self.make_mock_request(
-            status=http.client.OK,
+            status=http_client.OK,
             data=token_response,
-            impersonation_status=http.client.OK,
+            impersonation_status=http_client.OK,
             impersonation_data=impersonation_response,
         )
         # Initialize credentials with service account impersonation and basic auth.
@@ -1077,9 +1077,9 @@
         # Initialize mock request to handle token exchange and service account
         # impersonation request.
         request = self.make_mock_request(
-            status=http.client.OK,
+            status=http_client.OK,
             data=token_response,
-            impersonation_status=http.client.OK,
+            impersonation_status=http_client.OK,
             impersonation_data=impersonation_response,
         )
         # Initialize credentials with service account impersonation and basic auth.
@@ -1114,7 +1114,7 @@
     def test_apply_without_quota_project_id(self):
         headers = {}
         request = self.make_mock_request(
-            status=http.client.OK, data=self.SUCCESS_RESPONSE
+            status=http_client.OK, data=self.SUCCESS_RESPONSE
         )
         credentials = self.make_credentials()
 
@@ -1128,7 +1128,7 @@
     def test_apply_workforce_without_quota_project_id(self):
         headers = {}
         request = self.make_mock_request(
-            status=http.client.OK, data=self.SUCCESS_RESPONSE
+            status=http_client.OK, data=self.SUCCESS_RESPONSE
         )
         credentials = self.make_workforce_pool_credentials(
             workforce_pool_user_project=self.WORKFORCE_POOL_USER_PROJECT
@@ -1153,9 +1153,9 @@
         # Initialize mock request to handle token exchange and service account
         # impersonation request.
         request = self.make_mock_request(
-            status=http.client.OK,
+            status=http_client.OK,
             data=self.SUCCESS_RESPONSE.copy(),
-            impersonation_status=http.client.OK,
+            impersonation_status=http_client.OK,
             impersonation_data=impersonation_response,
         )
         # Initialize credentials with service account impersonation.
@@ -1175,7 +1175,7 @@
     def test_apply_with_quota_project_id(self):
         headers = {"other": "header-value"}
         request = self.make_mock_request(
-            status=http.client.OK, data=self.SUCCESS_RESPONSE
+            status=http_client.OK, data=self.SUCCESS_RESPONSE
         )
         credentials = self.make_credentials(quota_project_id=self.QUOTA_PROJECT_ID)
 
@@ -1200,9 +1200,9 @@
         # Initialize mock request to handle token exchange and service account
         # impersonation request.
         request = self.make_mock_request(
-            status=http.client.OK,
+            status=http_client.OK,
             data=self.SUCCESS_RESPONSE.copy(),
-            impersonation_status=http.client.OK,
+            impersonation_status=http_client.OK,
             impersonation_data=impersonation_response,
         )
         # Initialize credentials with service account impersonation.
@@ -1225,7 +1225,7 @@
     def test_before_request(self):
         headers = {"other": "header-value"}
         request = self.make_mock_request(
-            status=http.client.OK, data=self.SUCCESS_RESPONSE
+            status=http_client.OK, data=self.SUCCESS_RESPONSE
         )
         credentials = self.make_credentials()
 
@@ -1248,7 +1248,7 @@
     def test_before_request_workforce(self):
         headers = {"other": "header-value"}
         request = self.make_mock_request(
-            status=http.client.OK, data=self.SUCCESS_RESPONSE
+            status=http_client.OK, data=self.SUCCESS_RESPONSE
         )
         credentials = self.make_workforce_pool_credentials(
             workforce_pool_user_project=self.WORKFORCE_POOL_USER_PROJECT
@@ -1282,9 +1282,9 @@
         # Initialize mock request to handle token exchange and service account
         # impersonation request.
         request = self.make_mock_request(
-            status=http.client.OK,
+            status=http_client.OK,
             data=self.SUCCESS_RESPONSE.copy(),
-            impersonation_status=http.client.OK,
+            impersonation_status=http_client.OK,
             impersonation_data=impersonation_response,
         )
         headers = {"other": "header-value"}
@@ -1312,7 +1312,7 @@
     def test_before_request_expired(self, utcnow):
         headers = {}
         request = self.make_mock_request(
-            status=http.client.OK, data=self.SUCCESS_RESPONSE
+            status=http_client.OK, data=self.SUCCESS_RESPONSE
         )
         credentials = self.make_credentials()
         credentials.token = "token"
@@ -1360,9 +1360,9 @@
         # Initialize mock request to handle token exchange and service account
         # impersonation request.
         request = self.make_mock_request(
-            status=http.client.OK,
+            status=http_client.OK,
             data=self.SUCCESS_RESPONSE.copy(),
-            impersonation_status=http.client.OK,
+            impersonation_status=http_client.OK,
             impersonation_data=impersonation_response,
         )
         credentials = self.make_credentials(
@@ -1491,11 +1491,11 @@
         # Initialize mock request to handle token exchange, service account
         # impersonation and cloud resource manager request.
         request = self.make_mock_request(
-            status=http.client.OK,
+            status=http_client.OK,
             data=self.SUCCESS_RESPONSE.copy(),
-            impersonation_status=http.client.OK,
+            impersonation_status=http_client.OK,
             impersonation_data=impersonation_response,
-            cloud_resource_manager_status=http.client.OK,
+            cloud_resource_manager_status=http_client.OK,
             cloud_resource_manager_data=self.CLOUD_RESOURCE_MANAGER_SUCCESS_RESPONSE,
         )
         credentials = self.make_credentials(
@@ -1562,9 +1562,9 @@
         # Initialize mock request to handle token exchange and cloud resource
         # manager request.
         request = self.make_mock_request(
-            status=http.client.OK,
+            status=http_client.OK,
             data=self.SUCCESS_RESPONSE.copy(),
-            cloud_resource_manager_status=http.client.OK,
+            cloud_resource_manager_status=http_client.OK,
             cloud_resource_manager_data=self.CLOUD_RESOURCE_MANAGER_SUCCESS_RESPONSE,
         )
         credentials = self.make_workforce_pool_credentials(
@@ -1611,9 +1611,9 @@
         # Simulate resource doesn't have sufficient permissions to access
         # cloud resource manager.
         request = self.make_mock_request(
-            status=http.client.OK,
+            status=http_client.OK,
             data=self.SUCCESS_RESPONSE.copy(),
-            cloud_resource_manager_status=http.client.UNAUTHORIZED,
+            cloud_resource_manager_status=http_client.UNAUTHORIZED,
         )
         credentials = self.make_credentials(scopes=self.SCOPES)
 
diff --git a/tests/test_iam.py b/tests/test_iam.py
index e9eca58..bc71225 100644
--- a/tests/test_iam.py
+++ b/tests/test_iam.py
@@ -14,11 +14,11 @@
 
 import base64
 import datetime
-import http.client
 import json
 
 import mock
 import pytest
+from six.moves import http_client
 
 from google.auth import _helpers
 from google.auth import exceptions
@@ -81,7 +81,7 @@
     def test_sign_bytes(self):
         signature = b"DEADBEEF"
         encoded_signature = base64.b64encode(signature).decode("utf-8")
-        request = make_request(http.client.OK, data={"signedBlob": encoded_signature})
+        request = make_request(http_client.OK, data={"signedBlob": encoded_signature})
         credentials = make_credentials()
 
         signer = iam.Signer(request, credentials, mock.sentinel.service_account_email)
@@ -93,7 +93,7 @@
         assert kwargs["headers"]["Content-Type"] == "application/json"
 
     def test_sign_bytes_failure(self):
-        request = make_request(http.client.UNAUTHORIZED)
+        request = make_request(http_client.UNAUTHORIZED)
         credentials = make_credentials()
 
         signer = iam.Signer(request, credentials, mock.sentinel.service_account_email)
diff --git a/tests/test_identity_pool.py b/tests/test_identity_pool.py
index e90e288..87e343b 100644
--- a/tests/test_identity_pool.py
+++ b/tests/test_identity_pool.py
@@ -13,13 +13,13 @@
 # limitations under the License.
 
 import datetime
-import http.client
 import json
 import os
-import urllib
 
 import mock
 import pytest
+from six.moves import http_client
+from six.moves import urllib
 
 from google.auth import _helpers
 from google.auth import exceptions
@@ -92,7 +92,7 @@
 
     @classmethod
     def make_mock_request(
-        cls, token_status=http.client.OK, token_data=None, *extra_requests
+        cls, token_status=http_client.OK, token_data=None, *extra_requests
     ):
         responses = []
         responses.append(cls.make_mock_response(token_status, token_data))
@@ -218,14 +218,14 @@
         # service account impersonation request.
         requests = []
         if credential_data:
-            requests.append((http.client.OK, credential_data))
+            requests.append((http_client.OK, credential_data))
 
         token_request_index = len(requests)
-        requests.append((http.client.OK, token_response))
+        requests.append((http_client.OK, token_response))
 
         if service_account_impersonation_url:
             impersonation_request_index = len(requests)
-            requests.append((http.client.OK, impersonation_response))
+            requests.append((http_client.OK, impersonation_response))
 
         request = cls.make_mock_request(*[el for req in requests for el in req])
 
diff --git a/tests/test_impersonated_credentials.py b/tests/test_impersonated_credentials.py
index 3dbb6ca..bceaeba 100644
--- a/tests/test_impersonated_credentials.py
+++ b/tests/test_impersonated_credentials.py
@@ -13,12 +13,12 @@
 # limitations under the License.
 
 import datetime
-import http.client
 import json
 import os
 
 import mock
 import pytest
+from six.moves import http_client
 
 from google.auth import _helpers
 from google.auth import crypt
@@ -79,7 +79,7 @@
         "google.auth.transport.requests.AuthorizedSession.request", autospec=True
     ) as auth_session:
         data = {"keyId": "1", "signedBlob": "c2lnbmF0dXJl"}
-        auth_session.return_value = MockResponse(data, http.client.OK)
+        auth_session.return_value = MockResponse(data, http_client.OK)
         yield auth_session
 
 
@@ -89,7 +89,7 @@
         "google.auth.transport.requests.AuthorizedSession.request", autospec=True
     ) as auth_session:
         data = {"token": ID_TOKEN_DATA}
-        auth_session.return_value = MockResponse(data, http.client.OK)
+        auth_session.return_value = MockResponse(data, http_client.OK)
         yield auth_session
 
 
@@ -141,7 +141,7 @@
     def make_request(
         self,
         data,
-        status=http.client.OK,
+        status=http_client.OK,
         headers=None,
         side_effect=None,
         use_data_bytes=True,
@@ -169,7 +169,7 @@
 
         request = self.make_request(
             data=json.dumps(response_body),
-            status=http.client.OK,
+            status=http_client.OK,
             use_data_bytes=use_data_bytes,
         )
 
@@ -194,7 +194,7 @@
 
         request = self.make_request(
             data=json.dumps(response_body),
-            status=http.client.OK,
+            status=http_client.OK,
             use_data_bytes=use_data_bytes,
         )
 
@@ -229,7 +229,7 @@
             ).isoformat("T") + "Z"
             response_body = {"accessToken": "token", "expireTime": expire_time}
             request = self.make_request(
-                data=json.dumps(response_body), status=http.client.OK
+                data=json.dumps(response_body), status=http_client.OK
             )
 
             credentials.refresh(request)
@@ -254,7 +254,7 @@
         response_body = {"accessToken": token, "expireTime": expire_time}
 
         request = self.make_request(
-            data=json.dumps(response_body), status=http.client.OK
+            data=json.dumps(response_body), status=http_client.OK
         )
 
         with pytest.raises(exceptions.RefreshError) as excinfo:
@@ -277,7 +277,7 @@
         }
 
         request = self.make_request(
-            data=json.dumps(response_body), status=http.client.UNAUTHORIZED
+            data=json.dumps(response_body), status=http_client.UNAUTHORIZED
         )
 
         with pytest.raises(exceptions.RefreshError) as excinfo:
@@ -294,7 +294,7 @@
         response_body = {}
 
         request = self.make_request(
-            data=json.dumps(response_body), status=http.client.HTTPException
+            data=json.dumps(response_body), status=http_client.HTTPException
         )
 
         with pytest.raises(exceptions.RefreshError) as excinfo:
@@ -331,7 +331,7 @@
         token_response_body = {"accessToken": token, "expireTime": expire_time}
 
         response = mock.create_autospec(transport.Response, instance=False)
-        response.status = http.client.OK
+        response.status = http_client.OK
         response.data = _helpers.to_bytes(json.dumps(token_response_body))
 
         request = mock.create_autospec(transport.Request, instance=False)
@@ -369,7 +369,7 @@
 
         request = self.make_request(
             data=json.dumps(response_body),
-            status=http.client.OK,
+            status=http_client.OK,
             use_data_bytes=use_data_bytes,
         )
 
@@ -394,7 +394,7 @@
         response_body = {"accessToken": token, "expireTime": expire_time}
 
         request = self.make_request(
-            data=json.dumps(response_body), status=http.client.OK
+            data=json.dumps(response_body), status=http_client.OK
         )
 
         credentials.refresh(request)
@@ -423,7 +423,7 @@
         response_body = {"accessToken": token, "expireTime": expire_time}
 
         request = self.make_request(
-            data=json.dumps(response_body), status=http.client.OK
+            data=json.dumps(response_body), status=http_client.OK
         )
 
         credentials.refresh(request)
@@ -453,7 +453,7 @@
         response_body = {"accessToken": token, "expireTime": expire_time}
 
         request = self.make_request(
-            data=json.dumps(response_body), status=http.client.OK
+            data=json.dumps(response_body), status=http_client.OK
         )
 
         credentials.refresh(request)
@@ -494,7 +494,7 @@
         response_body = {"accessToken": token, "expireTime": expire_time}
 
         request = self.make_request(
-            data=json.dumps(response_body), status=http.client.OK
+            data=json.dumps(response_body), status=http_client.OK
         )
 
         credentials.refresh(request)
@@ -523,7 +523,7 @@
         response_body = {"accessToken": token, "expireTime": expire_time}
 
         request = self.make_request(
-            data=json.dumps(response_body), status=http.client.OK
+            data=json.dumps(response_body), status=http_client.OK
         )
 
         credentials.refresh(request)
diff --git a/tests/test_jwt.py b/tests/test_jwt.py
index ba7277c..c0e1184 100644
--- a/tests/test_jwt.py
+++ b/tests/test_jwt.py
@@ -288,9 +288,9 @@
 
 
 def test_decode_unknown_alg():
-    headers = json.dumps({"kid": "1", "alg": "fakealg"})
+    headers = json.dumps({u"kid": u"1", u"alg": u"fakealg"})
     token = b".".join(
-        map(lambda seg: base64.b64encode(seg.encode("utf-8")), [headers, "{}", "sig"])
+        map(lambda seg: base64.b64encode(seg.encode("utf-8")), [headers, u"{}", u"sig"])
     )
 
     with pytest.raises(ValueError) as excinfo:
@@ -300,9 +300,9 @@
 
 def test_decode_missing_crytography_alg(monkeypatch):
     monkeypatch.delitem(jwt._ALGORITHM_TO_VERIFIER_CLASS, "ES256")
-    headers = json.dumps({"kid": "1", "alg": "ES256"})
+    headers = json.dumps({u"kid": u"1", u"alg": u"ES256"})
     token = b".".join(
-        map(lambda seg: base64.b64encode(seg.encode("utf-8")), [headers, "{}", "sig"])
+        map(lambda seg: base64.b64encode(seg.encode("utf-8")), [headers, u"{}", u"sig"])
     )
 
     with pytest.raises(ValueError) as excinfo:
diff --git a/tests/transport/compliance.py b/tests/transport/compliance.py
index a5cb678..e093d76 100644
--- a/tests/transport/compliance.py
+++ b/tests/transport/compliance.py
@@ -12,12 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import http.client
 import time
 
 import flask
 import pytest
 from pytest_localserver.http import WSGIServer
+from six.moves import http_client
 
 from google.auth import exceptions
 
@@ -43,11 +43,11 @@
         def index():
             header_value = flask.request.headers.get("x-test-header", "value")
             headers = {"X-Test-Header": header_value}
-            return "Basic Content", http.client.OK, headers
+            return "Basic Content", http_client.OK, headers
 
         @app.route("/server_error")
         def server_error():
-            return "Error", http.client.INTERNAL_SERVER_ERROR
+            return "Error", http_client.INTERNAL_SERVER_ERROR
 
         @app.route("/wait")
         def wait():
@@ -65,7 +65,7 @@
         request = self.make_request()
         response = request(url=server.url + "/basic", method="GET")
 
-        assert response.status == http.client.OK
+        assert response.status == http_client.OK
         assert response.headers["x-test-header"] == "value"
         assert response.data == b"Basic Content"
 
@@ -73,7 +73,7 @@
         request = self.make_request()
         response = request(url=server.url + "/basic", method="GET", timeout=2)
 
-        assert response.status == http.client.OK
+        assert response.status == http_client.OK
         assert response.headers["x-test-header"] == "value"
         assert response.data == b"Basic Content"
 
@@ -91,7 +91,7 @@
             headers={"x-test-header": "hello world"},
         )
 
-        assert response.status == http.client.OK
+        assert response.status == http_client.OK
         assert response.headers["x-test-header"] == "hello world"
         assert response.data == b"Basic Content"
 
@@ -99,7 +99,7 @@
         request = self.make_request()
         response = request(url=server.url + "/server_error", method="GET")
 
-        assert response.status == http.client.INTERNAL_SERVER_ERROR
+        assert response.status == http_client.INTERNAL_SERVER_ERROR
         assert response.data == b"Error"
 
     def test_connection_error(self):
diff --git a/tests/transport/test_requests.py b/tests/transport/test_requests.py
index 8b57e0b..ed9300d 100644
--- a/tests/transport/test_requests.py
+++ b/tests/transport/test_requests.py
@@ -14,7 +14,6 @@
 
 import datetime
 import functools
-import http.client
 import os
 import sys
 
@@ -24,6 +23,7 @@
 import pytest
 import requests
 import requests.adapters
+from six.moves import http_client
 
 from google.auth import environment_vars
 from google.auth import exceptions
@@ -188,7 +188,7 @@
             )
 
 
-def make_response(status=http.client.OK, data=None):
+def make_response(status=http_client.OK, data=None):
     response = requests.Response()
     response.status_code = status
     response._content = data
@@ -249,10 +249,10 @@
 
     def test_request_refresh(self):
         credentials = mock.Mock(wraps=CredentialsStub())
-        final_response = make_response(status=http.client.OK)
+        final_response = make_response(status=http_client.OK)
         # First request will 401, second request will succeed.
         adapter = AdapterStub(
-            [make_response(status=http.client.UNAUTHORIZED), final_response]
+            [make_response(status=http_client.UNAUTHORIZED), final_response]
         )
 
         authed_session = google.auth.transport.requests.AuthorizedSession(
@@ -282,7 +282,7 @@
             wraps=TimeTickCredentialsStub(time_tick=tick_one_second)
         )
         adapter = TimeTickAdapterStub(
-            time_tick=tick_one_second, responses=[make_response(status=http.client.OK)]
+            time_tick=tick_one_second, responses=[make_response(status=http_client.OK)]
         )
 
         authed_session = google.auth.transport.requests.AuthorizedSession(credentials)
@@ -304,8 +304,8 @@
         adapter = TimeTickAdapterStub(
             time_tick=tick_one_second,
             responses=[
-                make_response(status=http.client.UNAUTHORIZED),
-                make_response(status=http.client.OK),
+                make_response(status=http_client.UNAUTHORIZED),
+                make_response(status=http_client.OK),
             ],
         )
 
@@ -328,8 +328,8 @@
         adapter = TimeTickAdapterStub(
             time_tick=tick_one_second,
             responses=[
-                make_response(status=http.client.UNAUTHORIZED),
-                make_response(status=http.client.OK),
+                make_response(status=http_client.UNAUTHORIZED),
+                make_response(status=http_client.OK),
             ],
         )
 
@@ -355,8 +355,8 @@
         adapter = TimeTickAdapterStub(
             time_tick=tick_one_second,
             responses=[
-                make_response(status=http.client.UNAUTHORIZED),
-                make_response(status=http.client.OK),
+                make_response(status=http_client.UNAUTHORIZED),
+                make_response(status=http_client.OK),
             ],
         )
 
diff --git a/tests/transport/test_urllib3.py b/tests/transport/test_urllib3.py
index 995d3dc..e3848c1 100644
--- a/tests/transport/test_urllib3.py
+++ b/tests/transport/test_urllib3.py
@@ -12,13 +12,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import http.client
 import os
 import sys
 
 import mock
 import OpenSSL
 import pytest
+from six.moves import http_client
 import urllib3
 
 from google.auth import environment_vars
@@ -84,7 +84,7 @@
 
 
 class ResponseStub(object):
-    def __init__(self, status=http.client.OK, data=None):
+    def __init__(self, status=http_client.OK, data=None):
         self.status = status
         self.data = data
 
@@ -141,12 +141,12 @@
 
     def test_urlopen_refresh(self):
         credentials = mock.Mock(wraps=CredentialsStub())
-        final_response = ResponseStub(status=http.client.OK)
+        final_response = ResponseStub(status=http_client.OK)
         # First request will 401, second request will succeed.
-        stub = HttpStub([ResponseStub(status=http.client.UNAUTHORIZED), final_response])
+        http = HttpStub([ResponseStub(status=http_client.UNAUTHORIZED), final_response])
 
         authed_http = google.auth.transport.urllib3.AuthorizedHttp(
-            credentials, http=stub
+            credentials, http=http
         )
 
         authed_http = authed_http.urlopen("GET", "http://example.com")
@@ -154,7 +154,7 @@
         assert authed_http == final_response
         assert credentials.before_request.call_count == 2
         assert credentials.refresh.called
-        assert stub.requests == [
+        assert http.requests == [
             ("GET", self.TEST_URL, None, {"authorization": "token"}, {}),
             ("GET", self.TEST_URL, None, {"authorization": "token1"}, {}),
         ]
diff --git a/tests_async/oauth2/test__client_async.py b/tests_async/oauth2/test__client_async.py
index 66338d5..6e48c45 100644
--- a/tests_async/oauth2/test__client_async.py
+++ b/tests_async/oauth2/test__client_async.py
@@ -13,12 +13,13 @@
 # limitations under the License.
 
 import datetime
-import http.client
 import json
-import urllib
 
 import mock
 import pytest
+import six
+from six.moves import http_client
+from six.moves import urllib
 
 from google.auth import _helpers
 from google.auth import _jwt_async as jwt
@@ -28,7 +29,7 @@
 from tests.oauth2 import test__client as test_client
 
 
-def make_request(response_data, status=http.client.OK):
+def make_request(response_data, status=http_client.OK):
     response = mock.AsyncMock(spec=["transport.Response"])
     response.status = status
     data = json.dumps(response_data).encode("utf-8")
@@ -92,7 +93,7 @@
 
 @pytest.mark.asyncio
 async def test__token_endpoint_request_error():
-    request = make_request({}, status=http.client.BAD_REQUEST)
+    request = make_request({}, status=http_client.BAD_REQUEST)
 
     with pytest.raises(exceptions.RefreshError):
         await _client._token_endpoint_request(request, "http://example.com", {})
@@ -101,7 +102,7 @@
 @pytest.mark.asyncio
 async def test__token_endpoint_request_internal_failure_error():
     request = make_request(
-        {"error_description": "internal_failure"}, status=http.client.BAD_REQUEST
+        {"error_description": "internal_failure"}, status=http_client.BAD_REQUEST
     )
 
     with pytest.raises(exceptions.RefreshError):
@@ -110,7 +111,7 @@
         )
 
     request = make_request(
-        {"error": "internal_failure"}, status=http.client.BAD_REQUEST
+        {"error": "internal_failure"}, status=http_client.BAD_REQUEST
     )
 
     with pytest.raises(exceptions.RefreshError):
@@ -123,7 +124,7 @@
     request_body = request.call_args[1]["body"].decode("utf-8")
     request_params = urllib.parse.parse_qs(request_body)
 
-    for key, value in params.items():
+    for key, value in six.iteritems(params):
         assert request_params[key][0] == value
 
 
diff --git a/tests_async/transport/async_compliance.py b/tests_async/transport/async_compliance.py
index 385a923..9c4b173 100644
--- a/tests_async/transport/async_compliance.py
+++ b/tests_async/transport/async_compliance.py
@@ -12,12 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import http.client
 import time
 
 import flask
 import pytest
 from pytest_localserver.http import WSGIServer
+from six.moves import http_client
 
 from google.auth import exceptions
 from tests.transport import compliance
@@ -41,11 +41,11 @@
         def index():
             header_value = flask.request.headers.get("x-test-header", "value")
             headers = {"X-Test-Header": header_value}
-            return "Basic Content", http.client.OK, headers
+            return "Basic Content", http_client.OK, headers
 
         @app.route("/server_error")
         def server_error():
-            return "Error", http.client.INTERNAL_SERVER_ERROR
+            return "Error", http_client.INTERNAL_SERVER_ERROR
 
         @app.route("/wait")
         def wait():
@@ -63,7 +63,7 @@
     async def test_request_basic(self, server):
         request = self.make_request()
         response = await request(url=server.url + "/basic", method="GET")
-        assert response.status == http.client.OK
+        assert response.status == http_client.OK
         assert response.headers["x-test-header"] == "value"
 
         # Use 13 as this is the length of the data written into the stream.
@@ -75,7 +75,7 @@
     async def test_request_basic_with_http(self, server):
         request = self.make_with_parameter_request()
         response = await request(url=server.url + "/basic", method="GET")
-        assert response.status == http.client.OK
+        assert response.status == http_client.OK
         assert response.headers["x-test-header"] == "value"
 
         # Use 13 as this is the length of the data written into the stream.
@@ -88,7 +88,7 @@
         request = self.make_request()
         response = await request(url=server.url + "/basic", method="GET", timeout=2)
 
-        assert response.status == http.client.OK
+        assert response.status == http_client.OK
         assert response.headers["x-test-header"] == "value"
 
         data = await response.data.read(13)
@@ -110,7 +110,7 @@
             headers={"x-test-header": "hello world"},
         )
 
-        assert response.status == http.client.OK
+        assert response.status == http_client.OK
         assert response.headers["x-test-header"] == "hello world"
 
         data = await response.data.read(13)
@@ -121,7 +121,7 @@
         request = self.make_request()
 
         response = await request(url=server.url + "/server_error", method="GET")
-        assert response.status == http.client.INTERNAL_SERVER_ERROR
+        assert response.status == http_client.INTERNAL_SERVER_ERROR
         data = await response.data.read(5)
         assert data == b"Error"