Repository: googleapis/google-auth-library-python

Issue:
Errors in sign_bytes are not clear
 **Is your feature request related to a problem? Please describe.**
When there is an error in the request made in the function [sign_bytes](https://github.com/googleapis/google-auth-library-python/blob/main/google/auth/impersonated_credentials.py#L274) is not clear what the real error is. It is  just throwing  a `KeyError: 'signedBlob'`:

```
File "/usr/local/lib/python3.9/site-packages/storages/backends/gcloud.py", line 255, in url
return blob.generate_signed_url(self.expiration)
partner-marketing_web_1 |   File "/usr/local/lib/python3.9/site-packages/google/cloud/storage/blob.py", line 622, in generate_signed_url
return helper(
partner-marketing_web_1 |   File "/usr/local/lib/python3.9/site-packages/google/cloud/storage/_signing.py", line 395, in generate_signed_url_v2
signed_query_params = get_signed_query_params_v2(
partner-marketing_web_1 |   File "/usr/local/lib/python3.9/site-packages/google/cloud/storage/_signing.py", line 80, in get_signed_query_params_v2
signature_bytes = credentials.sign_bytes(string_to_sign.encode("ascii"))
partner-marketing_web_1 |   File "/usr/local/lib/python3.9/site-packages/google/auth/impersonated_credentials.py", line 293, in sign_bytes
return base64.b64decode(response.json()["signedBlob"])
KeyError: 'signedBlob'
```
 **Describe the solution you'd like**
It would be great to have a more clear understanding of the real error. In my case the response was returning 
```
{'error': {'code': 403, 'message': 'The caller does not have permission', 'status': 'PERMISSION_DENIED'}}
```

One option is to validate the response (`response.json()`), before returning the `signedBlob`. Similar to https://github.com/googleapis/google-auth-library-python/blob/main/google/auth/iam.py#L79

```
def sign_bytes(self, message):
    ...
    response = authed_session.post(
            url=iam_sign_endpoint, headers=headers, json=body
        )
    body = response.json()
    
    if "error" in body:
        # raise a more appropriate error
        
    return base64.b64decode(body["signedBlob"])

```


Discussion:

Resolving patch:
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
@@ -290,6 +290,11 @@ def sign_bytes(self, message):
             url=iam_sign_endpoint, headers=headers, json=body
         )
 
+        if response.status_code != http_client.OK:
+            raise exceptions.TransportError(
+                "Error calling sign_bytes: {}".format(response.json())
+            )
+
         return base64.b64decode(response.json()["signedBlob"])
 
     @property


Test patch:
diff --git a/tests/test_impersonated_credentials.py b/tests/test_impersonated_credentials.py
--- a/tests/test_impersonated_credentials.py
+++ b/tests/test_impersonated_credentials.py
@@ -345,6 +345,19 @@ def test_sign_bytes(self, mock_donor_credentials, mock_authorizedsession_sign):
         signature = credentials.sign_bytes(b"signed bytes")
         assert signature == b"signature"
 
+    def test_sign_bytes_failure(self):
+        credentials = self.make_credentials(lifetime=None)
+
+        with mock.patch(
+            "google.auth.transport.requests.AuthorizedSession.request", autospec=True
+        ) as auth_session:
+            data = {"error": {"code": 403, "message": "unauthorized"}}
+            auth_session.return_value = MockResponse(data, http_client.FORBIDDEN)
+
+            with pytest.raises(exceptions.TransportError) as excinfo:
+                credentials.sign_bytes(b"foo")
+            assert excinfo.match("'code': 403")
+
     def test_with_quota_project(self):
         credentials = self.make_credentials()