Repository: googleapis/google-auth-library-python

Issue:
Invalid type for expiry when using GCE metadata server
#### Environment details

  - OS: Linux
  - Python version: 3.8.2
  - pip version: 20.0.2
  - `google-auth` version: 1.12.0

#### Steps to reproduce

  1. Create an ID token using the GCE metadata server:
      ```python
      from google.auth import compute_engine
      import google.auth.transport.requests
      request = google.auth.transport.requests.Request()
      target_audience = 'https://example.com'
      creds = compute_engine.IDTokenCredentials(request, target_audience, use_metadata_identity_endpoint=True)
      ```
  2. Try to use the credentials to authorize a request:
      ```python
      from google.auth.transport.requests import AuthorizedSession
      session = AuthorizedSession(credentials)
      response = session.get('https://example.com')
      ```

This results in an error:
```
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.8/site-packages/google/auth/credentials.py", line 66, in expired
    skewed_expiry = self.expiry - _helpers.CLOCK_SKEW
TypeError: unsupported operand type(s) for -: 'int' and 'datetime.timedelta'
```

I think the issue is on [this line](https://github.com/googleapis/google-auth-library-python/blob/master/google/auth/compute_engine/credentials.py#L294). `payload["exp"]` should (apparently) be getting parsed into a `datetime.timedelta`


Discussion:

Resolving patch:
diff --git a/google/auth/compute_engine/credentials.py b/google/auth/compute_engine/credentials.py
--- a/google/auth/compute_engine/credentials.py
+++ b/google/auth/compute_engine/credentials.py
@@ -274,6 +274,9 @@ def _call_metadata_identity_endpoint(self, request):
             request (google.auth.transport.Request): The object used to make
                 HTTP requests.
 
+        Returns:
+            Tuple[str, datetime.datetime]: The ID token and the expiry of the ID token.
+
         Raises:
             google.auth.exceptions.RefreshError: If the Compute Engine metadata
                 service can't be reached or if the instance has no credentials.
@@ -291,7 +294,7 @@ def _call_metadata_identity_endpoint(self, request):
             six.raise_from(new_exc, caught_exc)
 
         _, payload, _, _ = jwt._unverified_decode(id_token)
-        return id_token, payload["exp"]
+        return id_token, datetime.datetime.fromtimestamp(payload["exp"])
 
     def refresh(self, request):
         """Refreshes the ID token.


Test patch:
diff --git a/system_tests/test_compute_engine.py b/system_tests/test_compute_engine.py
--- a/system_tests/test_compute_engine.py
+++ b/system_tests/test_compute_engine.py
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from datetime import datetime
+
 import pytest
 
 import google.auth
@@ -61,8 +63,9 @@ def test_id_token_from_metadata(http_request):
     credentials.refresh(http_request)
 
     _, payload, _, _ = jwt._unverified_decode(credentials.token)
+    assert credentials.valid
     assert payload["aud"] == AUDIENCE
-    assert payload["exp"] == credentials.expiry
+    assert datetime.fromtimestamp(payload["exp"]) == credentials.expiry
 
 
 def test_fetch_id_token(http_request):
diff --git a/tests/compute_engine/test_credentials.py b/tests/compute_engine/test_credentials.py
--- a/tests/compute_engine/test_credentials.py
+++ b/tests/compute_engine/test_credentials.py
@@ -522,7 +522,7 @@ def test_get_id_token_from_metadata(self, get, get_service_account_info):
         cred.refresh(request=mock.Mock())
 
         assert cred.token == SAMPLE_ID_TOKEN
-        assert cred.expiry == SAMPLE_ID_TOKEN_EXP
+        assert cred.expiry == datetime.datetime.fromtimestamp(SAMPLE_ID_TOKEN_EXP)
         assert cred._use_metadata_identity_endpoint
         assert cred._signer is None
         assert cred._token_uri is None