Skip to content
12 changes: 6 additions & 6 deletions google/auth/_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,31 +330,31 @@ def _get_explicit_environ_credentials(quota_project_id=None):
from google.auth import _cloud_sdk

cloud_sdk_adc_path = _cloud_sdk.get_application_default_credentials_path()
explicit_file = os.environ.get(environment_vars.CREDENTIALS)
explicit_file = os.environ.get(environment_vars.CREDENTIALS, "")
Copy link
Collaborator

@daniel-sanche daniel-sanche Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if I like using "" to represent the empty state. That seems like it could lead to confusing situations down the line. Instead, I'd recommend:

  1. changing this line to os.environ.get(environment_vars.CREDENTIALS) or None, which will replace empty strings with None

or

  1. use if explicit_file: in place of the checks, which will respond to None and empty strings the same way, since both are falsy

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Daniel:

Thanks for this comment. I considered similar ideas when evaluating this, and this is the conclusion I came to:

The variable explicit_file is a local variable within _get_explicit_environ_credentials(), so no external API is changed or affected and the way the code is currently written, it must be a str. Why? Because of the default behavior of the .get() method on a dict and because of the return object from .get_application_default_credentials_path() which is a str.

dict .get()

Environmental variables are stored as strings AND if the CREDENTIALS variable is not found, we return an empty string as the default value. There is no reason to return a None, it serves no additional purpose in the limited uses that we have for the variable explicit_file.

explicit_file = os.environ.get(environment_vars.CREDENTIALS, "")

_cloud_sdk.get_application_default_credentials_path()

This function returns a string.

def get_application_default_credentials_path():
    """Gets the path to the application default credentials file.

    The path may or may not exist.

    Returns:
        str: The full path to application default credentials.
    """

In the comparision, we are checking to see IF explicit_file was unset AND if not, whether the path matches the path referenced by cloud_sdk_adc_path. There is no need OR reason to prefer None over the empty string.

if explicit_file != "" and explicit_file == cloud_sdk_adc_path:

So why use the empty string?

Old Behavior: Defaulted to None if unset.
New Behavior: Defaults to "" if unset (via os.environ.get(..., "")).
Result: The logic now treats "unset" and "set to empty string" identically (as ""). The check explicit_file != "" correctly handles both cases (unset OR empty string) by skipping the file load, which prevents a crash. cloud_sdk_adc_path always returns a string path, so the comparison remains safe.

Before I had fully worked through the above logic, I had considered something such as:

if explicit_file not in {None, ""} and feel that while if explicit_file not in {None, ""} OR similar patterns (such as you recommend) where we look for both is a valid defensive pattern, the current PR's approach is slightly cleaner because it enforces type consistency. If we initialize explicit_file with a default of "", the variable is always a str, avoiding mixed types (Optional[str]). The current implementation explicit_file != "" is correct given the initialization change.


_LOGGER.debug(
"Checking %s for explicit credentials as part of auth process...", explicit_file
"Checking '%s' for explicit credentials as part of auth process...",
explicit_file,
)

if explicit_file is not None and explicit_file == cloud_sdk_adc_path:
if explicit_file != "" and explicit_file == cloud_sdk_adc_path:
# 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.
_LOGGER.debug(
"Explicit credentials path %s is the same as Cloud SDK credentials path, fall back to Cloud SDK credentials flow...",
"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(quota_project_id=quota_project_id)

if explicit_file is not None:
if explicit_file != "":
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
credentials, project_id = load_credentials_from_file(
os.environ[environment_vars.CREDENTIALS],
quota_project_id=quota_project_id,
)
credentials._cred_file_path = f"{explicit_file} file via the GOOGLE_APPLICATION_CREDENTIALS environment variable"

return credentials, project_id

else:
Expand Down
Loading