Repository: googleapis/google-auth-library-python

Issue:
Implement service_account.Credentials.to_jwt_credentials()
(Context: #29)



Discussion:

Resolving patch:
diff --git a/google/oauth2/service_account.py b/google/oauth2/service_account.py
--- a/google/oauth2/service_account.py
+++ b/google/oauth2/service_account.py
@@ -204,6 +204,33 @@ def from_service_account_file(cls, filename, **kwargs):
             filename, require=['client_email', 'token_uri'])
         return cls._from_signer_and_info(signer, info, **kwargs)
 
+    def to_jwt_credentials(self):
+        """Creates a :cls:`google.auth.jwt.Credentials` instance from this
+        instance.
+
+        The new instance will use the same private key as this instance and
+        will use this instance's service account email as the issuer and
+        subject.
+
+        This is the same as calling
+        :meth:`jwt.Credentials.from_service_account_file` with the same
+        file used to create these credentials::
+
+            svc_creds = service_account.Credentials.from_service_account_file(
+                'service_account.json')
+            jwt_from_svc = svc_credentials.to_jwt_credentials()
+            # is the same as:
+            jwt_creds = jwt.Credentials.from_service_account_file(
+                'service_account.json')
+
+        Returns:
+            google.auth.jwt.Credentials: A new Credentials instance.
+        """
+        return jwt.Credentials(
+            self._signer,
+            issuer=self._service_account_email,
+            subject=self._service_account_email)
+
     @property
     def requires_scopes(self):
         """Checks if the credentials requires scopes.


Test patch:
diff --git a/tests/oauth2/test_service_account.py b/tests/oauth2/test_service_account.py
--- a/tests/oauth2/test_service_account.py
+++ b/tests/oauth2/test_service_account.py
@@ -58,15 +58,14 @@ def credentials_fixture(self, signer):
             signer, self.SERVICE_ACCOUNT_EMAIL, self.TOKEN_URI)
 
     def test_from_service_account_info(self):
-        with open(SERVICE_ACCOUNT_JSON_FILE, 'r') as fh:
-            info = json.load(fh)
-
         credentials = service_account.Credentials.from_service_account_info(
-            info)
+            SERVICE_ACCOUNT_INFO)
 
-        assert credentials._signer.key_id == info['private_key_id']
-        assert credentials._service_account_email == info['client_email']
-        assert credentials._token_uri == info['token_uri']
+        assert (credentials._signer.key_id ==
+                SERVICE_ACCOUNT_INFO['private_key_id'])
+        assert (credentials._service_account_email ==
+                SERVICE_ACCOUNT_INFO['client_email'])
+        assert credentials._token_uri == SERVICE_ACCOUNT_INFO['token_uri']
 
     def test_from_service_account_info_args(self):
         info = SERVICE_ACCOUNT_INFO.copy()
@@ -112,6 +111,17 @@ def test_from_service_account_file_args(self):
         assert credentials._subject == subject
         assert credentials._additional_claims == additional_claims
 
+    def test_to_jwt_credentials(self):
+        jwt_from_svc = self.credentials.to_jwt_credentials()
+        jwt_from_info = jwt.Credentials.from_service_account_info(
+            SERVICE_ACCOUNT_INFO)
+
+        assert isinstance(jwt_from_svc, jwt.Credentials)
+        assert jwt_from_svc._signer.key_id == jwt_from_info._signer.key_id
+        assert jwt_from_svc._issuer == jwt_from_info._issuer
+        assert jwt_from_svc._subject == jwt_from_info._subject
+        assert jwt_from_svc._audience == jwt_from_info._audience
+
     def test_default_state(self):
         assert not self.credentials.valid
         # Expiration hasn't been set yet