Repository: googleapis/google-auth-library-python

Issue:
google.auth.transport.Request is called with type(body) == str
According to eg https://google-auth.readthedocs.io/en/latest/reference/google.auth.html#google.auth.default, classes implementing google.auth.transport.Request will be called with a *body* parameter of type bytes. This makes sense, because the request agent is in no position to decide how a str body should be encoded for transport.

Unfortunately, in some cases the googl.auth module does pass str value. One such call is made in https://github.com/googleapis/google-auth-library-python/blob/137b43ba002d422c68e7bfacdafa54986f38500b/google/oauth2/_client.py#L100, but there may be more.

Discussion:

Patch:
diff --git a/google/auth/iam.py b/google/auth/iam.py
index a438726..0ab5b55 100644
--- a/google/auth/iam.py
+++ b/google/auth/iam.py
@@ -70,7 +70,9 @@ class Signer(crypt.Signer):
         method = "POST"
         url = _SIGN_BLOB_URI.format(self._service_account_email)
         headers = {}
-        body = json.dumps({"bytesToSign": base64.b64encode(message).decode("utf-8")})
+        body = json.dumps(
+            {"bytesToSign": base64.b64encode(message).decode("utf-8")}
+        ).encode("utf-8")
 
         self._credentials.before_request(self._request, method, url, headers)
         response = self._request(url=url, method=method, body=body, headers=headers)
diff --git a/google/auth/impersonated_credentials.py b/google/auth/impersonated_credentials.py
index 70fa5dc..bc7031e 100644
--- a/google/auth/impersonated_credentials.py
+++ b/google/auth/impersonated_credentials.py
@@ -84,7 +84,7 @@ def _make_iam_token_request(request, principal, headers, body):
     """
     iam_endpoint = _IAM_ENDPOINT.format(principal)
 
-    body = json.dumps(body)
+    body = json.dumps(body).encode("utf-8")
 
     response = request(url=iam_endpoint, method="POST", headers=headers, body=body)
 
diff --git a/google/oauth2/_client.py b/google/oauth2/_client.py
index 4cf7a7f..4ba31a8 100644
--- a/google/oauth2/_client.py
+++ b/google/oauth2/_client.py
@@ -95,7 +95,7 @@ def _token_endpoint_request(request, token_uri, body):
         google.auth.exceptions.RefreshError: If the token endpoint returned
             an error.
     """
-    body = urllib.parse.urlencode(body)
+    body = urllib.parse.urlencode(body).encode("utf-8")
     headers = {"content-type": _URLENCODED_CONTENT_TYPE}
 
     retry = 0


Test patch:
diff --git a/tests/oauth2/test__client.py b/tests/oauth2/test__client.py
index 9cf59eb..052390a 100644
--- a/tests/oauth2/test__client.py
+++ b/tests/oauth2/test__client.py
@@ -96,7 +96,7 @@ def test__token_endpoint_request():
         method="POST",
         url="http://example.com",
         headers={"content-type": "application/x-www-form-urlencoded"},
-        body="test=params",
+        body="test=params".encode("utf-8"),
     )
 
     # Check result
@@ -131,7 +131,7 @@ def test__token_endpoint_request_internal_failure_error():
 
 
 def verify_request_params(request, params):
-    request_body = request.call_args[1]["body"]
+    request_body = request.call_args[1]["body"].decode("utf-8")
     request_params = urllib.parse.parse_qs(request_body)
 
     for key, value in six.iteritems(params):