feat: add quota project to base credentials class (#546)

diff --git a/google/oauth2/credentials.py b/google/oauth2/credentials.py
index 7572196..6f96275 100644
--- a/google/oauth2/credentials.py
+++ b/google/oauth2/credentials.py
@@ -156,26 +156,14 @@
         return self._client_secret
 
     @property
-    def quota_project_id(self):
-        """Optional[str]: The project to use for quota and billing purposes."""
-        return self._quota_project_id
-
-    @property
     def requires_scopes(self):
         """False: OAuth 2.0 credentials have their scopes set when
         the initial token is requested and can not be changed."""
         return False
 
+    @_helpers.copy_docstring(credentials.Credentials)
     def with_quota_project(self, quota_project_id):
-        """Returns a copy of these credentials with a modified quota project
 
-        Args:
-            quota_project_id (str): The project to use for quota and
-            billing purposes
-
-        Returns:
-            google.oauth2.credentials.Credentials: A new credentials instance.
-        """
         return self.__class__(
             self.token,
             refresh_token=self.refresh_token,
@@ -227,12 +215,6 @@
                     )
                 )
 
-    @_helpers.copy_docstring(credentials.Credentials)
-    def apply(self, headers, token=None):
-        super(Credentials, self).apply(headers, token=token)
-        if self.quota_project_id is not None:
-            headers["x-goog-user-project"] = self.quota_project_id
-
     @classmethod
     def from_authorized_user_info(cls, info, scopes=None):
         """Creates a Credentials instance from parsed authorized user info.
@@ -332,11 +314,15 @@
     Args:
         account (Optional[str]): Account to get the access token for. If not
             specified, the current active account will be used.
+        quota_project_id (Optional[str]): The project ID used for quota
+            and billing.
+
     """
 
-    def __init__(self, account=None):
+    def __init__(self, account=None, quota_project_id=None):
         super(UserAccessTokenCredentials, self).__init__()
         self._account = account
+        self._quota_project_id = quota_project_id
 
     def with_account(self, account):
         """Create a new instance with the given account.
@@ -348,7 +334,11 @@
             google.oauth2.credentials.UserAccessTokenCredentials: The created
                 credentials with the given account.
         """
-        return self.__class__(account=account)
+        return self.__class__(account=account, quota_project_id=self._quota_project_id)
+
+    @_helpers.copy_docstring(credentials.Credentials)
+    def with_quota_project(self, quota_project_id):
+        return self.__class__(account=self._account, quota_project_id=quota_project_id)
 
     def refresh(self, request):
         """Refreshes the access token.
diff --git a/google/oauth2/service_account.py b/google/oauth2/service_account.py
index 54630d3..2240631 100644
--- a/google/oauth2/service_account.py
+++ b/google/oauth2/service_account.py
@@ -238,11 +238,6 @@
         return self._project_id
 
     @property
-    def quota_project_id(self):
-        """Project ID to use for quota and billing purposes."""
-        return self._quota_project_id
-
-    @property
     def requires_scopes(self):
         """Checks if the credentials requires scopes.
 
@@ -311,17 +306,9 @@
             additional_claims=new_additional_claims,
         )
 
+    @_helpers.copy_docstring(credentials.Credentials)
     def with_quota_project(self, quota_project_id):
-        """Returns a copy of these credentials with a modified quota project.
 
-        Args:
-            quota_project_id (str): The project to use for quota and
-            billing purposes
-
-        Returns:
-            google.auth.service_account.Credentials: A new credentials
-                instance.
-        """
         return self.__class__(
             self._signer,
             service_account_email=self._service_account_email,
@@ -373,12 +360,6 @@
         self.token = access_token
         self.expiry = expiry
 
-    @_helpers.copy_docstring(credentials.Credentials)
-    def apply(self, headers, token=None):
-        super(Credentials, self).apply(headers, token=token)
-        if self.quota_project_id is not None:
-            headers["x-goog-user-project"] = self.quota_project_id
-
     @_helpers.copy_docstring(credentials.Signing)
     def sign_bytes(self, message):
         return self._signer.sign(message)
@@ -443,6 +424,7 @@
         token_uri,
         target_audience,
         additional_claims=None,
+        quota_project_id=None,
     ):
         """
         Args:
@@ -454,7 +436,7 @@
                 will be set to this string.
             additional_claims (Mapping[str, str]): Any additional claims for
                 the JWT assertion used in the authorization grant.
-
+            quota_project_id (Optional[str]): The project ID used for quota and billing.
         .. note:: Typically one of the helper constructors
             :meth:`from_service_account_file` or
             :meth:`from_service_account_info` are used instead of calling the
@@ -465,6 +447,7 @@
         self._service_account_email = service_account_email
         self._token_uri = token_uri
         self._target_audience = target_audience
+        self._quota_project_id = quota_project_id
 
         if additional_claims is not None:
             self._additional_claims = additional_claims
@@ -547,6 +530,18 @@
             token_uri=self._token_uri,
             target_audience=target_audience,
             additional_claims=self._additional_claims.copy(),
+            quota_project_id=self.quota_project_id,
+        )
+
+    @_helpers.copy_docstring(credentials.Credentials)
+    def with_quota_project(self, quota_project_id):
+        return self.__class__(
+            self._signer,
+            service_account_email=self._service_account_email,
+            token_uri=self._token_uri,
+            target_audience=self._target_audience,
+            additional_claims=self._additional_claims.copy(),
+            quota_project_id=quota_project_id,
         )
 
     def _make_authorization_grant_assertion(self):