fix: disable warning if quota project id provided to auth.default() (#856)

* fix: disable warning if quota project id provided to auth.default()

* add more tests
diff --git a/google/auth/_default.py b/google/auth/_default.py
index 7da77a2..d4ccbc6 100644
--- a/google/auth/_default.py
+++ b/google/auth/_default.py
@@ -172,7 +172,7 @@
         )
 
 
-def _get_gcloud_sdk_credentials():
+def _get_gcloud_sdk_credentials(quota_project_id=None):
     """Gets the credentials and project ID from the Cloud SDK."""
     from google.auth import _cloud_sdk
 
@@ -185,7 +185,9 @@
         _LOGGER.debug("Cloud SDK credentials not found on disk; not using them")
         return None, None
 
-    credentials, project_id = load_credentials_from_file(credentials_filename)
+    credentials, project_id = load_credentials_from_file(
+        credentials_filename, quota_project_id=quota_project_id
+    )
 
     if not project_id:
         project_id = _cloud_sdk.get_project_id()
@@ -193,7 +195,7 @@
     return credentials, project_id
 
 
-def _get_explicit_environ_credentials():
+def _get_explicit_environ_credentials(quota_project_id=None):
     """Gets credentials from the GOOGLE_APPLICATION_CREDENTIALS environment
     variable."""
     from google.auth import _cloud_sdk
@@ -213,11 +215,11 @@
             "Explicit credentials path %s is the same as Cloud SDK credentials path, fall back to Cloud SDK credentials flow...",
             explicit_file,
         )
-        return _get_gcloud_sdk_credentials()
+        return _get_gcloud_sdk_credentials(quota_project_id=quota_project_id)
 
     if explicit_file is not None:
         credentials, project_id = load_credentials_from_file(
-            os.environ[environment_vars.CREDENTIALS]
+            os.environ[environment_vars.CREDENTIALS], quota_project_id=quota_project_id
         )
 
         return credentials, project_id
@@ -447,8 +449,8 @@
         # with_scopes_if_required() below will ensure scopes/default scopes are
         # safely set on the returned credentials since requires_scopes will
         # guard against setting scopes on user credentials.
-        _get_explicit_environ_credentials,
-        _get_gcloud_sdk_credentials,
+        lambda: _get_explicit_environ_credentials(quota_project_id=quota_project_id),
+        lambda: _get_gcloud_sdk_credentials(quota_project_id=quota_project_id),
         _get_gae_credentials,
         lambda: _get_gce_credentials(request),
     )
diff --git a/google/auth/_default_async.py b/google/auth/_default_async.py
index 82e6c43..3fa125b 100644
--- a/google/auth/_default_async.py
+++ b/google/auth/_default_async.py
@@ -73,11 +73,13 @@
         try:
             credentials = credentials.Credentials.from_authorized_user_info(
                 info, scopes=scopes
-            ).with_quota_project(quota_project_id)
+            )
         except ValueError as caught_exc:
             msg = "Failed to load authorized user credentials from {}".format(filename)
             new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
             raise new_exc from caught_exc
+        if quota_project_id:
+            credentials = credentials.with_quota_project(quota_project_id)
         if not credentials.quota_project_id:
             _default._warn_about_problematic_credentials(credentials)
         return credentials, None
@@ -104,7 +106,7 @@
         )
 
 
-def _get_gcloud_sdk_credentials():
+def _get_gcloud_sdk_credentials(quota_project_id=None):
     """Gets the credentials and project ID from the Cloud SDK."""
     from google.auth import _cloud_sdk
 
@@ -114,7 +116,9 @@
     if not os.path.isfile(credentials_filename):
         return None, None
 
-    credentials, project_id = load_credentials_from_file(credentials_filename)
+    credentials, project_id = load_credentials_from_file(
+        credentials_filename, quota_project_id=quota_project_id
+    )
 
     if not project_id:
         project_id = _cloud_sdk.get_project_id()
@@ -122,7 +126,7 @@
     return credentials, project_id
 
 
-def _get_explicit_environ_credentials():
+def _get_explicit_environ_credentials(quota_project_id=None):
     """Gets credentials from the GOOGLE_APPLICATION_CREDENTIALS environment
     variable."""
     from google.auth import _cloud_sdk
@@ -134,11 +138,11 @@
         # Cloud sdk flow calls gcloud to fetch project id, so if the explicit
         # file path is cloud sdk credentials path, then we should fall back
         # to cloud sdk flow, otherwise project id cannot be obtained.
-        return _get_gcloud_sdk_credentials()
+        return _get_gcloud_sdk_credentials(quota_project_id=quota_project_id)
 
     if explicit_file is not None:
         credentials, project_id = load_credentials_from_file(
-            os.environ[environment_vars.CREDENTIALS]
+            os.environ[environment_vars.CREDENTIALS], quota_project_id=quota_project_id
         )
 
         return credentials, project_id
@@ -250,8 +254,8 @@
     )
 
     checkers = (
-        _get_explicit_environ_credentials,
-        _get_gcloud_sdk_credentials,
+        lambda: _get_explicit_environ_credentials(quota_project_id=quota_project_id),
+        lambda: _get_gcloud_sdk_credentials(quota_project_id=quota_project_id),
         _get_gae_credentials,
         lambda: _get_gce_credentials(request),
     )
diff --git a/tests/test__default.py b/tests/test__default.py
index a515f38..c70ceaa 100644
--- a/tests/test__default.py
+++ b/tests/test__default.py
@@ -328,15 +328,18 @@
     assert _default._get_explicit_environ_credentials() == (None, None)
 
 
[email protected]("quota_project_id", [None, "project-foo"])
 @LOAD_FILE_PATCH
-def test__get_explicit_environ_credentials(load, monkeypatch):
+def test__get_explicit_environ_credentials(load, quota_project_id, monkeypatch):
     monkeypatch.setenv(environment_vars.CREDENTIALS, "filename")
 
-    credentials, project_id = _default._get_explicit_environ_credentials()
+    credentials, project_id = _default._get_explicit_environ_credentials(
+        quota_project_id=quota_project_id
+    )
 
     assert credentials is MOCK_CREDENTIALS
     assert project_id is mock.sentinel.project_id
-    load.assert_called_with("filename")
+    load.assert_called_with("filename", quota_project_id=quota_project_id)
 
 
 @LOAD_FILE_PATCH
@@ -350,36 +353,40 @@
     assert project_id is None
 
 
[email protected]("quota_project_id", [None, "project-foo"])
 @mock.patch(
     "google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
 )
 @mock.patch("google.auth._default._get_gcloud_sdk_credentials", autospec=True)
 def test__get_explicit_environ_credentials_fallback_to_gcloud(
-    get_gcloud_creds, get_adc_path, monkeypatch
+    get_gcloud_creds, get_adc_path, quota_project_id, monkeypatch
 ):
     # Set explicit credentials path to cloud sdk credentials path.
     get_adc_path.return_value = "filename"
     monkeypatch.setenv(environment_vars.CREDENTIALS, "filename")
 
-    _default._get_explicit_environ_credentials()
+    _default._get_explicit_environ_credentials(quota_project_id=quota_project_id)
 
     # Check we fall back to cloud sdk flow since explicit credentials path is
     # cloud sdk credentials path
-    get_gcloud_creds.assert_called_once()
+    get_gcloud_creds.assert_called_with(quota_project_id=quota_project_id)
 
 
[email protected]("quota_project_id", [None, "project-foo"])
 @LOAD_FILE_PATCH
 @mock.patch(
     "google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
 )
-def test__get_gcloud_sdk_credentials(get_adc_path, load):
+def test__get_gcloud_sdk_credentials(get_adc_path, load, quota_project_id):
     get_adc_path.return_value = SERVICE_ACCOUNT_FILE
 
-    credentials, project_id = _default._get_gcloud_sdk_credentials()
+    credentials, project_id = _default._get_gcloud_sdk_credentials(
+        quota_project_id=quota_project_id
+    )
 
     assert credentials is MOCK_CREDENTIALS
     assert project_id is mock.sentinel.project_id
-    load.assert_called_with(SERVICE_ACCOUNT_FILE)
+    load.assert_called_with(SERVICE_ACCOUNT_FILE, quota_project_id=quota_project_id)
 
 
 @mock.patch(
@@ -779,3 +786,22 @@
     assert excinfo.match(
         "Failed to load external account credentials from {}".format(str(filename))
     )
+
+
[email protected](
+    "google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
+)
+def test_default_warning_without_quota_project_id_for_user_creds(get_adc_path):
+    get_adc_path.return_value = AUTHORIZED_USER_CLOUD_SDK_FILE
+
+    with pytest.warns(UserWarning, match="Cloud SDK"):
+        credentials, project_id = _default.default(quota_project_id=None)
+
+
[email protected](
+    "google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
+)
+def test_default_no_warning_with_quota_project_id_for_user_creds(get_adc_path):
+    get_adc_path.return_value = AUTHORIZED_USER_CLOUD_SDK_FILE
+
+    credentials, project_id = _default.default(quota_project_id="project-foo")
diff --git a/tests_async/test__default_async.py b/tests_async/test__default_async.py
index b672303..69a50d6 100644
--- a/tests_async/test__default_async.py
+++ b/tests_async/test__default_async.py
@@ -165,15 +165,18 @@
     assert _default._get_explicit_environ_credentials() == (None, None)
 
 
[email protected]("quota_project_id", [None, "project-foo"])
 @LOAD_FILE_PATCH
-def test__get_explicit_environ_credentials(load, monkeypatch):
+def test__get_explicit_environ_credentials(load, quota_project_id, monkeypatch):
     monkeypatch.setenv(environment_vars.CREDENTIALS, "filename")
 
-    credentials, project_id = _default._get_explicit_environ_credentials()
+    credentials, project_id = _default._get_explicit_environ_credentials(
+        quota_project_id=quota_project_id
+    )
 
     assert credentials is MOCK_CREDENTIALS
     assert project_id is mock.sentinel.project_id
-    load.assert_called_with("filename")
+    load.assert_called_with("filename", quota_project_id=quota_project_id)
 
 
 @LOAD_FILE_PATCH
@@ -187,36 +190,42 @@
     assert project_id is None
 
 
[email protected]("quota_project_id", [None, "project-foo"])
 @mock.patch(
     "google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
 )
 @mock.patch("google.auth._default_async._get_gcloud_sdk_credentials", autospec=True)
 def test__get_explicit_environ_credentials_fallback_to_gcloud(
-    get_gcloud_creds, get_adc_path, monkeypatch
+    get_gcloud_creds, get_adc_path, quota_project_id, monkeypatch
 ):
     # Set explicit credentials path to cloud sdk credentials path.
     get_adc_path.return_value = "filename"
     monkeypatch.setenv(environment_vars.CREDENTIALS, "filename")
 
-    _default._get_explicit_environ_credentials()
+    _default._get_explicit_environ_credentials(quota_project_id=quota_project_id)
 
     # Check we fall back to cloud sdk flow since explicit credentials path is
     # cloud sdk credentials path
-    get_gcloud_creds.assert_called_once()
+    get_gcloud_creds.assert_called_with(quota_project_id=quota_project_id)
 
 
[email protected]("quota_project_id", [None, "project-foo"])
 @LOAD_FILE_PATCH
 @mock.patch(
     "google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
 )
-def test__get_gcloud_sdk_credentials(get_adc_path, load):
+def test__get_gcloud_sdk_credentials(get_adc_path, load, quota_project_id):
     get_adc_path.return_value = test_default.SERVICE_ACCOUNT_FILE
 
-    credentials, project_id = _default._get_gcloud_sdk_credentials()
+    credentials, project_id = _default._get_gcloud_sdk_credentials(
+        quota_project_id=quota_project_id
+    )
 
     assert credentials is MOCK_CREDENTIALS
     assert project_id is mock.sentinel.project_id
-    load.assert_called_with(test_default.SERVICE_ACCOUNT_FILE)
+    load.assert_called_with(
+        test_default.SERVICE_ACCOUNT_FILE, quota_project_id=quota_project_id
+    )
 
 
 @mock.patch(
@@ -533,3 +542,22 @@
         sys.modules["google.auth.compute_engine"] = None
         sys.modules["google.auth.app_engine"] = None
         assert _default.default_async() == (MOCK_CREDENTIALS, mock.sentinel.project_id)
+
+
[email protected](
+    "google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
+)
+def test_default_warning_without_quota_project_id_for_user_creds(get_adc_path):
+    get_adc_path.return_value = test_default.AUTHORIZED_USER_CLOUD_SDK_FILE
+
+    with pytest.warns(UserWarning, match="Cloud SDK"):
+        credentials, project_id = _default.default_async(quota_project_id=None)
+
+
[email protected](
+    "google.auth._cloud_sdk.get_application_default_credentials_path", autospec=True
+)
+def test_default_no_warning_with_quota_project_id_for_user_creds(get_adc_path):
+    get_adc_path.return_value = test_default.AUTHORIZED_USER_CLOUD_SDK_FILE
+
+    credentials, project_id = _default.default_async(quota_project_id="project-foo")