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:
@Nikratio Thank you for reporting this issue.  I apologize for the late reply.  I clicked on the link https://google-auth.readthedocs.io/en/latest/reference/google.auth.html#google.auth.default, and I don't see a discussion of a body parameter or bytes.

Is this issue still reproducible today?  Is there another doc that describes the conflict between the docs and the code?
The link may have changed since the issue was posted:

`google.auth.transport.Request` documentation says that the body should have type bytes

https://google-auth.readthedocs.io/en/latest/reference/google.auth.transport.html#google.auth.transport.Request
![image](https://user-images.githubusercontent.com/8822365/67342933-7be97c00-f4e8-11e9-8e7b-399533126e20.png)

---

Investigation (in progress):
`_token_endpoint_request` is a private method that makes a request using parameter `body`
https://github.com/googleapis/google-auth-library-python/blob/137b43ba002d422c68e7bfacdafa54986f38500b/google/oauth2/_client.py#L83-L91

All the public methods that call it specify parameter type `str` for the values that are used to make the  body.

https://github.com/googleapis/google-auth-library-python/blob/137b43ba002d422c68e7bfacdafa54986f38500b/google/oauth2/_client.py#L118-L138



google.auth.transport.Request calls [`requests.Sesion.request`](http://requests.kennethreitz.org/en/master/api/#requests.Session.request). 

https://github.com/googleapis/google-auth-library-python/blob/137b43ba002d422c68e7bfacdafa54986f38500b/google/auth/transport/requests.py#L94-L124

```
data – (optional) Dictionary, list of tuples, bytes, or file-like object to send in the body of the Request.
```


Resolving patch:
diff --git a/google/auth/iam.py b/google/auth/iam.py
--- a/google/auth/iam.py
+++ b/google/auth/iam.py
@@ -70,7 +70,9 @@ def _make_signing_request(self, message):
         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
--- 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
--- 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
--- 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):