about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/google/auth
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/google/auth
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/google/auth')
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/__init__.py53
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/_cloud_sdk.py153
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/_credentials_async.py171
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/_credentials_base.py75
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/_default.py719
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/_default_async.py282
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/_exponential_backoff.py164
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/_helpers.py273
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/_jwt_async.py164
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/_oauth2client.py167
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/_refresh_worker.py109
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/_service_account_info.py80
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/aio/__init__.py25
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/aio/credentials.py143
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/aio/transport/__init__.py144
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/aio/transport/aiohttp.py184
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/aio/transport/sessions.py268
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/api_key.py76
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/app_engine.py180
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/aws.py861
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/compute_engine/__init__.py22
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/compute_engine/_metadata.py375
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/compute_engine/credentials.py496
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/credentials.py522
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/crypt/__init__.py98
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/crypt/_cryptography_rsa.py151
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/crypt/_helpers.py0
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/crypt/_python_rsa.py175
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/crypt/base.py127
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/crypt/es256.py175
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/crypt/rsa.py30
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/downscoped.py512
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/environment_vars.py84
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/exceptions.py108
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/external_account.py628
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/external_account_authorized_user.py380
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/iam.py136
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/identity_pool.py439
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/impersonated_credentials.py579
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/jwt.py878
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/metrics.py154
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/pluggable.py429
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/py.typed2
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/transport/__init__.py103
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/transport/_aiohttp_requests.py391
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/transport/_custom_tls_signer.py283
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/transport/_http_client.py113
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/transport/_mtls_helper.py407
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/transport/_requests_base.py53
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/transport/grpc.py343
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/transport/mtls.py112
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/transport/requests.py599
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/transport/urllib3.py444
-rw-r--r--.venv/lib/python3.12/site-packages/google/auth/version.py15
54 files changed, 13654 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/google/auth/__init__.py b/.venv/lib/python3.12/site-packages/google/auth/__init__.py
new file mode 100644
index 00000000..765bbd70
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/__init__.py
@@ -0,0 +1,53 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Google Auth Library for Python."""
+
+import logging
+import sys
+import warnings
+
+from google.auth import version as google_auth_version
+from google.auth._default import (
+    default,
+    load_credentials_from_dict,
+    load_credentials_from_file,
+)
+
+
+__version__ = google_auth_version.__version__
+
+
+__all__ = ["default", "load_credentials_from_file", "load_credentials_from_dict"]
+
+
+class Python37DeprecationWarning(DeprecationWarning):  # pragma: NO COVER
+    """
+    Deprecation warning raised when Python 3.7 runtime is detected.
+    Python 3.7 support will be dropped after January 1, 2024.
+    """
+
+    pass
+
+
+# Checks if the current runtime is Python 3.7.
+if sys.version_info.major == 3 and sys.version_info.minor == 7:  # pragma: NO COVER
+    message = (
+        "After January 1, 2024, new releases of this library will drop support "
+        "for Python 3.7."
+    )
+    warnings.warn(message, Python37DeprecationWarning)
+
+# Set default logging handler to avoid "No handler found" warnings.
+logging.getLogger(__name__).addHandler(logging.NullHandler())
diff --git a/.venv/lib/python3.12/site-packages/google/auth/_cloud_sdk.py b/.venv/lib/python3.12/site-packages/google/auth/_cloud_sdk.py
new file mode 100644
index 00000000..a9441194
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/_cloud_sdk.py
@@ -0,0 +1,153 @@
+# Copyright 2015 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Helpers for reading the Google Cloud SDK's configuration."""
+
+import os
+import subprocess
+
+from google.auth import _helpers
+from google.auth import environment_vars
+from google.auth import exceptions
+
+
+# The ~/.config subdirectory containing gcloud credentials.
+_CONFIG_DIRECTORY = "gcloud"
+# Windows systems store config at %APPDATA%\gcloud
+_WINDOWS_CONFIG_ROOT_ENV_VAR = "APPDATA"
+# The name of the file in the Cloud SDK config that contains default
+# credentials.
+_CREDENTIALS_FILENAME = "application_default_credentials.json"
+# The name of the Cloud SDK shell script
+_CLOUD_SDK_POSIX_COMMAND = "gcloud"
+_CLOUD_SDK_WINDOWS_COMMAND = "gcloud.cmd"
+# The command to get the Cloud SDK configuration
+_CLOUD_SDK_CONFIG_GET_PROJECT_COMMAND = ("config", "get", "project")
+# The command to get google user access token
+_CLOUD_SDK_USER_ACCESS_TOKEN_COMMAND = ("auth", "print-access-token")
+# Cloud SDK's application-default client ID
+CLOUD_SDK_CLIENT_ID = (
+    "764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com"
+)
+
+
+def get_config_path():
+    """Returns the absolute path the the Cloud SDK's configuration directory.
+
+    Returns:
+        str: The Cloud SDK config path.
+    """
+    # If the path is explicitly set, return that.
+    try:
+        return os.environ[environment_vars.CLOUD_SDK_CONFIG_DIR]
+    except KeyError:
+        pass
+
+    # Non-windows systems store this at ~/.config/gcloud
+    if os.name != "nt":
+        return os.path.join(os.path.expanduser("~"), ".config", _CONFIG_DIRECTORY)
+    # Windows systems store config at %APPDATA%\gcloud
+    else:
+        try:
+            return os.path.join(
+                os.environ[_WINDOWS_CONFIG_ROOT_ENV_VAR], _CONFIG_DIRECTORY
+            )
+        except KeyError:
+            # This should never happen unless someone is really
+            # messing with things, but we'll cover the case anyway.
+            drive = os.environ.get("SystemDrive", "C:")
+            return os.path.join(drive, "\\", _CONFIG_DIRECTORY)
+
+
+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.
+    """
+    config_path = get_config_path()
+    return os.path.join(config_path, _CREDENTIALS_FILENAME)
+
+
+def _run_subprocess_ignore_stderr(command):
+    """ Return subprocess.check_output with the given command and ignores stderr."""
+    with open(os.devnull, "w") as devnull:
+        output = subprocess.check_output(command, stderr=devnull)
+    return output
+
+
+def get_project_id():
+    """Gets the project ID from the Cloud SDK.
+
+    Returns:
+        Optional[str]: The project ID.
+    """
+    if os.name == "nt":
+        command = _CLOUD_SDK_WINDOWS_COMMAND
+    else:
+        command = _CLOUD_SDK_POSIX_COMMAND
+
+    try:
+        # Ignore the stderr coming from gcloud, so it won't be mixed into the output.
+        # https://github.com/googleapis/google-auth-library-python/issues/673
+        project = _run_subprocess_ignore_stderr(
+            (command,) + _CLOUD_SDK_CONFIG_GET_PROJECT_COMMAND
+        )
+
+        # Turn bytes into a string and remove "\n"
+        project = _helpers.from_bytes(project).strip()
+        return project if project else None
+    except (subprocess.CalledProcessError, OSError, IOError):
+        return None
+
+
+def get_auth_access_token(account=None):
+    """Load user access token with the ``gcloud auth print-access-token`` command.
+
+    Args:
+        account (Optional[str]): Account to get the access token for. If not
+            specified, the current active account will be used.
+
+    Returns:
+        str: The user access token.
+
+    Raises:
+        google.auth.exceptions.UserAccessTokenError: if failed to get access
+            token from gcloud.
+    """
+    if os.name == "nt":
+        command = _CLOUD_SDK_WINDOWS_COMMAND
+    else:
+        command = _CLOUD_SDK_POSIX_COMMAND
+
+    try:
+        if account:
+            command = (
+                (command,)
+                + _CLOUD_SDK_USER_ACCESS_TOKEN_COMMAND
+                + ("--account=" + account,)
+            )
+        else:
+            command = (command,) + _CLOUD_SDK_USER_ACCESS_TOKEN_COMMAND
+
+        access_token = subprocess.check_output(command, stderr=subprocess.STDOUT)
+        # remove the trailing "\n"
+        return access_token.decode("utf-8").strip()
+    except (subprocess.CalledProcessError, OSError, IOError) as caught_exc:
+        new_exc = exceptions.UserAccessTokenError(
+            "Failed to obtain access token", caught_exc
+        )
+        raise new_exc from caught_exc
diff --git a/.venv/lib/python3.12/site-packages/google/auth/_credentials_async.py b/.venv/lib/python3.12/site-packages/google/auth/_credentials_async.py
new file mode 100644
index 00000000..760758d8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/_credentials_async.py
@@ -0,0 +1,171 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""Interfaces for credentials."""
+
+import abc
+import inspect
+
+from google.auth import credentials
+
+
+class Credentials(credentials.Credentials, metaclass=abc.ABCMeta):
+    """Async inherited credentials class from google.auth.credentials.
+    The added functionality is the before_request call which requires
+    async/await syntax.
+    All credentials have a :attr:`token` that is used for authentication and
+    may also optionally set an :attr:`expiry` to indicate when the token will
+    no longer be valid.
+
+    Most credentials will be :attr:`invalid` until :meth:`refresh` is called.
+    Credentials can do this automatically before the first HTTP request in
+    :meth:`before_request`.
+
+    Although the token and expiration will change as the credentials are
+    :meth:`refreshed <refresh>` and used, credentials should be considered
+    immutable. Various credentials will accept configuration such as private
+    keys, scopes, and other options. These options are not changeable after
+    construction. Some classes will provide mechanisms to copy the credentials
+    with modifications such as :meth:`ScopedCredentials.with_scopes`.
+    """
+
+    async def before_request(self, request, method, url, headers):
+        """Performs credential-specific before request logic.
+
+        Refreshes the credentials if necessary, then calls :meth:`apply` to
+        apply the token to the authentication header.
+
+        Args:
+            request (google.auth.transport.Request): The object used to make
+                HTTP requests.
+            method (str): The request's HTTP method or the RPC method being
+                invoked.
+            url (str): The request's URI or the RPC service's URI.
+            headers (Mapping): The request's headers.
+        """
+        # pylint: disable=unused-argument
+        # (Subclasses may use these arguments to ascertain information about
+        # the http request.)
+
+        if not self.valid:
+            if inspect.iscoroutinefunction(self.refresh):
+                await self.refresh(request)
+            else:
+                self.refresh(request)
+        self.apply(headers)
+
+
+class CredentialsWithQuotaProject(credentials.CredentialsWithQuotaProject):
+    """Abstract base for credentials supporting ``with_quota_project`` factory"""
+
+
+class AnonymousCredentials(credentials.AnonymousCredentials, Credentials):
+    """Credentials that do not provide any authentication information.
+
+    These are useful in the case of services that support anonymous access or
+    local service emulators that do not use credentials. This class inherits
+    from the sync anonymous credentials file, but is kept if async credentials
+    is initialized and we would like anonymous credentials.
+    """
+
+
+class ReadOnlyScoped(credentials.ReadOnlyScoped, metaclass=abc.ABCMeta):
+    """Interface for credentials whose scopes can be queried.
+
+    OAuth 2.0-based credentials allow limiting access using scopes as described
+    in `RFC6749 Section 3.3`_.
+    If a credential class implements this interface then the credentials either
+    use scopes in their implementation.
+
+    Some credentials require scopes in order to obtain a token. You can check
+    if scoping is necessary with :attr:`requires_scopes`::
+
+        if credentials.requires_scopes:
+            # Scoping is required.
+            credentials = _credentials_async.with_scopes(scopes=['one', 'two'])
+
+    Credentials that require scopes must either be constructed with scopes::
+
+        credentials = SomeScopedCredentials(scopes=['one', 'two'])
+
+    Or must copy an existing instance using :meth:`with_scopes`::
+
+        scoped_credentials = _credentials_async.with_scopes(scopes=['one', 'two'])
+
+    Some credentials have scopes but do not allow or require scopes to be set,
+    these credentials can be used as-is.
+
+    .. _RFC6749 Section 3.3: https://tools.ietf.org/html/rfc6749#section-3.3
+    """
+
+
+class Scoped(credentials.Scoped):
+    """Interface for credentials whose scopes can be replaced while copying.
+
+    OAuth 2.0-based credentials allow limiting access using scopes as described
+    in `RFC6749 Section 3.3`_.
+    If a credential class implements this interface then the credentials either
+    use scopes in their implementation.
+
+    Some credentials require scopes in order to obtain a token. You can check
+    if scoping is necessary with :attr:`requires_scopes`::
+
+        if credentials.requires_scopes:
+            # Scoping is required.
+            credentials = _credentials_async.create_scoped(['one', 'two'])
+
+    Credentials that require scopes must either be constructed with scopes::
+
+        credentials = SomeScopedCredentials(scopes=['one', 'two'])
+
+    Or must copy an existing instance using :meth:`with_scopes`::
+
+        scoped_credentials = credentials.with_scopes(scopes=['one', 'two'])
+
+    Some credentials have scopes but do not allow or require scopes to be set,
+    these credentials can be used as-is.
+
+    .. _RFC6749 Section 3.3: https://tools.ietf.org/html/rfc6749#section-3.3
+    """
+
+
+def with_scopes_if_required(credentials, scopes):
+    """Creates a copy of the credentials with scopes if scoping is required.
+
+    This helper function is useful when you do not know (or care to know) the
+    specific type of credentials you are using (such as when you use
+    :func:`google.auth.default`). This function will call
+    :meth:`Scoped.with_scopes` if the credentials are scoped credentials and if
+    the credentials require scoping. Otherwise, it will return the credentials
+    as-is.
+
+    Args:
+        credentials (google.auth.credentials.Credentials): The credentials to
+            scope if necessary.
+        scopes (Sequence[str]): The list of scopes to use.
+
+    Returns:
+        google.auth._credentials_async.Credentials: Either a new set of scoped
+            credentials, or the passed in credentials instance if no scoping
+            was required.
+    """
+    if isinstance(credentials, Scoped) and credentials.requires_scopes:
+        return credentials.with_scopes(scopes)
+    else:
+        return credentials
+
+
+class Signing(credentials.Signing, metaclass=abc.ABCMeta):
+    """Interface for credentials that can cryptographically sign messages."""
diff --git a/.venv/lib/python3.12/site-packages/google/auth/_credentials_base.py b/.venv/lib/python3.12/site-packages/google/auth/_credentials_base.py
new file mode 100644
index 00000000..64d5ce34
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/_credentials_base.py
@@ -0,0 +1,75 @@
+# Copyright 2024 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""Interface for base credentials."""
+
+import abc
+
+from google.auth import _helpers
+
+
+class _BaseCredentials(metaclass=abc.ABCMeta):
+    """Base class for all credentials.
+
+    All credentials have a :attr:`token` that is used for authentication and
+    may also optionally set an :attr:`expiry` to indicate when the token will
+    no longer be valid.
+
+    Most credentials will be :attr:`invalid` until :meth:`refresh` is called.
+    Credentials can do this automatically before the first HTTP request in
+    :meth:`before_request`.
+
+    Although the token and expiration will change as the credentials are
+    :meth:`refreshed <refresh>` and used, credentials should be considered
+    immutable. Various credentials will accept configuration such as private
+    keys, scopes, and other options. These options are not changeable after
+    construction. Some classes will provide mechanisms to copy the credentials
+    with modifications such as :meth:`ScopedCredentials.with_scopes`.
+
+    Attributes:
+        token (Optional[str]): The bearer token that can be used in HTTP headers to make
+            authenticated requests.
+    """
+
+    def __init__(self):
+        self.token = None
+
+    @abc.abstractmethod
+    def refresh(self, request):
+        """Refreshes the access token.
+
+        Args:
+            request (google.auth.transport.Request): The object used to make
+                HTTP requests.
+
+        Raises:
+            google.auth.exceptions.RefreshError: If the credentials could
+                not be refreshed.
+        """
+        # pylint: disable=missing-raises-doc
+        # (pylint doesn't recognize that this is abstract)
+        raise NotImplementedError("Refresh must be implemented")
+
+    def _apply(self, headers, token=None):
+        """Apply the token to the authentication header.
+
+        Args:
+            headers (Mapping): The HTTP request headers.
+            token (Optional[str]): If specified, overrides the current access
+                token.
+        """
+        headers["authorization"] = "Bearer {}".format(
+            _helpers.from_bytes(token or self.token)
+        )
diff --git a/.venv/lib/python3.12/site-packages/google/auth/_default.py b/.venv/lib/python3.12/site-packages/google/auth/_default.py
new file mode 100644
index 00000000..1234fb25
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/_default.py
@@ -0,0 +1,719 @@
+# Copyright 2015 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Application default credentials.
+
+Implements application default credentials and project ID detection.
+"""
+
+import io
+import json
+import logging
+import os
+import warnings
+
+from google.auth import environment_vars
+from google.auth import exceptions
+import google.auth.transport._http_client
+
+_LOGGER = logging.getLogger(__name__)
+
+# Valid types accepted for file-based credentials.
+_AUTHORIZED_USER_TYPE = "authorized_user"
+_SERVICE_ACCOUNT_TYPE = "service_account"
+_EXTERNAL_ACCOUNT_TYPE = "external_account"
+_EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE = "external_account_authorized_user"
+_IMPERSONATED_SERVICE_ACCOUNT_TYPE = "impersonated_service_account"
+_GDCH_SERVICE_ACCOUNT_TYPE = "gdch_service_account"
+_VALID_TYPES = (
+    _AUTHORIZED_USER_TYPE,
+    _SERVICE_ACCOUNT_TYPE,
+    _EXTERNAL_ACCOUNT_TYPE,
+    _EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE,
+    _IMPERSONATED_SERVICE_ACCOUNT_TYPE,
+    _GDCH_SERVICE_ACCOUNT_TYPE,
+)
+
+# Help message when no credentials can be found.
+_CLOUD_SDK_MISSING_CREDENTIALS = """\
+Your default credentials were not found. To set up Application Default Credentials, \
+see https://cloud.google.com/docs/authentication/external/set-up-adc for more information.\
+"""
+
+# Warning when using Cloud SDK user credentials
+_CLOUD_SDK_CREDENTIALS_WARNING = """\
+Your application has authenticated using end user credentials from Google \
+Cloud SDK without a quota project. You might receive a "quota exceeded" \
+or "API not enabled" error. See the following page for troubleshooting: \
+https://cloud.google.com/docs/authentication/adc-troubleshooting/user-creds. \
+"""
+
+# The subject token type used for AWS external_account credentials.
+_AWS_SUBJECT_TOKEN_TYPE = "urn:ietf:params:aws:token-type:aws4_request"
+
+
+def _warn_about_problematic_credentials(credentials):
+    """Determines if the credentials are problematic.
+
+    Credentials from the Cloud SDK that are associated with Cloud SDK's project
+    are problematic because they may not have APIs enabled and have limited
+    quota. If this is the case, warn about it.
+    """
+    from google.auth import _cloud_sdk
+
+    if credentials.client_id == _cloud_sdk.CLOUD_SDK_CLIENT_ID:
+        warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING)
+
+
+def load_credentials_from_file(
+    filename, scopes=None, default_scopes=None, quota_project_id=None, request=None
+):
+    """Loads Google credentials from a file.
+
+    The credentials file must be a service account key, stored authorized
+    user credentials, external account credentials, or impersonated service
+    account credentials.
+
+    .. warning::
+        Important: If you accept a credential configuration (credential JSON/File/Stream)
+        from an external source for authentication to Google Cloud Platform, you must
+        validate it before providing it to any Google API or client library. Providing an
+        unvalidated credential configuration to Google APIs or libraries can compromise
+        the security of your systems and data. For more information, refer to
+        `Validate credential configurations from external sources`_.
+
+        .. _Validate credential configurations from external sources:
+            https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
+
+    Args:
+        filename (str): The full path to the credentials file.
+        scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If
+            specified, the credentials will automatically be scoped if
+            necessary
+        default_scopes (Optional[Sequence[str]]): Default scopes passed by a
+            Google client library. Use 'scopes' for user-defined scopes.
+        quota_project_id (Optional[str]):  The project ID used for
+            quota and billing.
+        request (Optional[google.auth.transport.Request]): An object used to make
+            HTTP requests. This is used to determine the associated project ID
+            for a workload identity pool resource (external account credentials).
+            If not specified, then it will use a
+            google.auth.transport.requests.Request client to make requests.
+
+    Returns:
+        Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded
+            credentials and the project ID. Authorized user credentials do not
+            have the project ID information. External account credentials project
+            IDs may not always be determined.
+
+    Raises:
+        google.auth.exceptions.DefaultCredentialsError: if the file is in the
+            wrong format or is missing.
+    """
+    if not os.path.exists(filename):
+        raise exceptions.DefaultCredentialsError(
+            "File {} was not found.".format(filename)
+        )
+
+    with io.open(filename, "r") as file_obj:
+        try:
+            info = json.load(file_obj)
+        except ValueError as caught_exc:
+            new_exc = exceptions.DefaultCredentialsError(
+                "File {} is not a valid json file.".format(filename), caught_exc
+            )
+            raise new_exc from caught_exc
+    return _load_credentials_from_info(
+        filename, info, scopes, default_scopes, quota_project_id, request
+    )
+
+
+def load_credentials_from_dict(
+    info, scopes=None, default_scopes=None, quota_project_id=None, request=None
+):
+    """Loads Google credentials from a dict.
+
+    The credentials file must be a service account key, stored authorized
+    user credentials, external account credentials, or impersonated service
+    account credentials.
+
+    .. warning::
+        Important: If you accept a credential configuration (credential JSON/File/Stream)
+        from an external source for authentication to Google Cloud Platform, you must
+        validate it before providing it to any Google API or client library. Providing an
+        unvalidated credential configuration to Google APIs or libraries can compromise
+        the security of your systems and data. For more information, refer to
+        `Validate credential configurations from external sources`_.
+
+    .. _Validate credential configurations from external sources:
+        https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
+
+    Args:
+        info (Dict[str, Any]): A dict object containing the credentials
+        scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If
+            specified, the credentials will automatically be scoped if
+            necessary
+        default_scopes (Optional[Sequence[str]]): Default scopes passed by a
+            Google client library. Use 'scopes' for user-defined scopes.
+        quota_project_id (Optional[str]):  The project ID used for
+            quota and billing.
+        request (Optional[google.auth.transport.Request]): An object used to make
+            HTTP requests. This is used to determine the associated project ID
+            for a workload identity pool resource (external account credentials).
+            If not specified, then it will use a
+            google.auth.transport.requests.Request client to make requests.
+
+    Returns:
+        Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded
+            credentials and the project ID. Authorized user credentials do not
+            have the project ID information. External account credentials project
+            IDs may not always be determined.
+
+    Raises:
+        google.auth.exceptions.DefaultCredentialsError: if the file is in the
+            wrong format or is missing.
+    """
+    if not isinstance(info, dict):
+        raise exceptions.DefaultCredentialsError(
+            "info object was of type {} but dict type was expected.".format(type(info))
+        )
+
+    return _load_credentials_from_info(
+        "dict object", info, scopes, default_scopes, quota_project_id, request
+    )
+
+
+def _load_credentials_from_info(
+    filename, info, scopes, default_scopes, quota_project_id, request
+):
+    from google.auth.credentials import CredentialsWithQuotaProject
+
+    credential_type = info.get("type")
+
+    if credential_type == _AUTHORIZED_USER_TYPE:
+        credentials, project_id = _get_authorized_user_credentials(
+            filename, info, scopes
+        )
+
+    elif credential_type == _SERVICE_ACCOUNT_TYPE:
+        credentials, project_id = _get_service_account_credentials(
+            filename, info, scopes, default_scopes
+        )
+
+    elif credential_type == _EXTERNAL_ACCOUNT_TYPE:
+        credentials, project_id = _get_external_account_credentials(
+            info,
+            filename,
+            scopes=scopes,
+            default_scopes=default_scopes,
+            request=request,
+        )
+
+    elif credential_type == _EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE:
+        credentials, project_id = _get_external_account_authorized_user_credentials(
+            filename, info, request
+        )
+
+    elif credential_type == _IMPERSONATED_SERVICE_ACCOUNT_TYPE:
+        credentials, project_id = _get_impersonated_service_account_credentials(
+            filename, info, scopes
+        )
+    elif credential_type == _GDCH_SERVICE_ACCOUNT_TYPE:
+        credentials, project_id = _get_gdch_service_account_credentials(filename, info)
+    else:
+        raise exceptions.DefaultCredentialsError(
+            "The file {file} does not have a valid type. "
+            "Type is {type}, expected one of {valid_types}.".format(
+                file=filename, type=credential_type, valid_types=_VALID_TYPES
+            )
+        )
+    if isinstance(credentials, CredentialsWithQuotaProject):
+        credentials = _apply_quota_project_id(credentials, quota_project_id)
+    return credentials, project_id
+
+
+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
+
+    _LOGGER.debug("Checking Cloud SDK credentials as part of auth process...")
+
+    # Check if application default credentials exist.
+    credentials_filename = _cloud_sdk.get_application_default_credentials_path()
+
+    if not os.path.isfile(credentials_filename):
+        _LOGGER.debug("Cloud SDK credentials not found on disk; not using them")
+        return None, None
+
+    credentials, project_id = load_credentials_from_file(
+        credentials_filename, quota_project_id=quota_project_id
+    )
+    credentials._cred_file_path = credentials_filename
+
+    if not project_id:
+        project_id = _cloud_sdk.get_project_id()
+
+    return credentials, project_id
+
+
+def _get_explicit_environ_credentials(quota_project_id=None):
+    """Gets credentials from the GOOGLE_APPLICATION_CREDENTIALS environment
+    variable."""
+    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)
+
+    _LOGGER.debug(
+        "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:
+        # 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_file,
+        )
+        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], 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:
+        return None, None
+
+
+def _get_gae_credentials():
+    """Gets Google App Engine App Identity credentials and project ID."""
+    # If not GAE gen1, prefer the metadata service even if the GAE APIs are
+    # available as per https://google.aip.dev/auth/4115.
+    if os.environ.get(environment_vars.LEGACY_APPENGINE_RUNTIME) != "python27":
+        return None, None
+
+    # While this library is normally bundled with app_engine, there are
+    # some cases where it's not available, so we tolerate ImportError.
+    try:
+        _LOGGER.debug("Checking for App Engine runtime as part of auth process...")
+        import google.auth.app_engine as app_engine
+    except ImportError:
+        _LOGGER.warning("Import of App Engine auth library failed.")
+        return None, None
+
+    try:
+        credentials = app_engine.Credentials()
+        project_id = app_engine.get_project_id()
+        return credentials, project_id
+    except EnvironmentError:
+        _LOGGER.debug(
+            "No App Engine library was found so cannot authentication via App Engine Identity Credentials."
+        )
+        return None, None
+
+
+def _get_gce_credentials(request=None, quota_project_id=None):
+    """Gets credentials and project ID from the GCE Metadata Service."""
+    # Ping requires a transport, but we want application default credentials
+    # to require no arguments. So, we'll use the _http_client transport which
+    # uses http.client. This is only acceptable because the metadata server
+    # doesn't do SSL and never requires proxies.
+
+    # While this library is normally bundled with compute_engine, there are
+    # some cases where it's not available, so we tolerate ImportError.
+    try:
+        from google.auth import compute_engine
+        from google.auth.compute_engine import _metadata
+    except ImportError:
+        _LOGGER.warning("Import of Compute Engine auth library failed.")
+        return None, None
+
+    if request is None:
+        request = google.auth.transport._http_client.Request()
+
+    if _metadata.is_on_gce(request=request):
+        # Get the project ID.
+        try:
+            project_id = _metadata.get_project_id(request=request)
+        except exceptions.TransportError:
+            project_id = None
+
+        cred = compute_engine.Credentials()
+        cred = _apply_quota_project_id(cred, quota_project_id)
+
+        return cred, project_id
+    else:
+        _LOGGER.warning(
+            "Authentication failed using Compute Engine authentication due to unavailable metadata server."
+        )
+        return None, None
+
+
+def _get_external_account_credentials(
+    info, filename, scopes=None, default_scopes=None, request=None
+):
+    """Loads external account Credentials from the parsed external account info.
+
+    The credentials information must correspond to a supported external account
+    credentials.
+
+    Args:
+        info (Mapping[str, str]): The external account info in Google format.
+        filename (str): The full path to the credentials file.
+        scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If
+            specified, the credentials will automatically be scoped if
+            necessary.
+        default_scopes (Optional[Sequence[str]]): Default scopes passed by a
+            Google client library. Use 'scopes' for user-defined scopes.
+        request (Optional[google.auth.transport.Request]): An object used to make
+            HTTP requests. This is used to determine the associated project ID
+            for a workload identity pool resource (external account credentials).
+            If not specified, then it will use a
+            google.auth.transport.requests.Request client to make requests.
+
+    Returns:
+        Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded
+            credentials and the project ID. External account credentials project
+            IDs may not always be determined.
+
+    Raises:
+        google.auth.exceptions.DefaultCredentialsError: if the info dictionary
+            is in the wrong format or is missing required information.
+    """
+    # There are currently 3 types of external_account credentials.
+    if info.get("subject_token_type") == _AWS_SUBJECT_TOKEN_TYPE:
+        # Check if configuration corresponds to an AWS credentials.
+        from google.auth import aws
+
+        credentials = aws.Credentials.from_info(
+            info, scopes=scopes, default_scopes=default_scopes
+        )
+    elif (
+        info.get("credential_source") is not None
+        and info.get("credential_source").get("executable") is not None
+    ):
+        from google.auth import pluggable
+
+        credentials = pluggable.Credentials.from_info(
+            info, scopes=scopes, default_scopes=default_scopes
+        )
+    else:
+        try:
+            # Check if configuration corresponds to an Identity Pool credentials.
+            from google.auth import identity_pool
+
+            credentials = identity_pool.Credentials.from_info(
+                info, scopes=scopes, default_scopes=default_scopes
+            )
+        except ValueError:
+            # If the configuration is invalid or does not correspond to any
+            # supported external_account credentials, raise an error.
+            raise exceptions.DefaultCredentialsError(
+                "Failed to load external account credentials from {}".format(filename)
+            )
+    if request is None:
+        import google.auth.transport.requests
+
+        request = google.auth.transport.requests.Request()
+
+    return credentials, credentials.get_project_id(request=request)
+
+
+def _get_external_account_authorized_user_credentials(
+    filename, info, scopes=None, default_scopes=None, request=None
+):
+    try:
+        from google.auth import external_account_authorized_user
+
+        credentials = external_account_authorized_user.Credentials.from_info(info)
+    except ValueError:
+        raise exceptions.DefaultCredentialsError(
+            "Failed to load external account authorized user credentials from {}".format(
+                filename
+            )
+        )
+
+    return credentials, None
+
+
+def _get_authorized_user_credentials(filename, info, scopes=None):
+    from google.oauth2 import credentials
+
+    try:
+        credentials = credentials.Credentials.from_authorized_user_info(
+            info, scopes=scopes
+        )
+    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
+    return credentials, None
+
+
+def _get_service_account_credentials(filename, info, scopes=None, default_scopes=None):
+    from google.oauth2 import service_account
+
+    try:
+        credentials = service_account.Credentials.from_service_account_info(
+            info, scopes=scopes, default_scopes=default_scopes
+        )
+    except ValueError as caught_exc:
+        msg = "Failed to load service account credentials from {}".format(filename)
+        new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
+        raise new_exc from caught_exc
+    return credentials, info.get("project_id")
+
+
+def _get_impersonated_service_account_credentials(filename, info, scopes):
+    from google.auth import impersonated_credentials
+
+    try:
+        source_credentials_info = info.get("source_credentials")
+        source_credentials_type = source_credentials_info.get("type")
+        if source_credentials_type == _AUTHORIZED_USER_TYPE:
+            source_credentials, _ = _get_authorized_user_credentials(
+                filename, source_credentials_info
+            )
+        elif source_credentials_type == _SERVICE_ACCOUNT_TYPE:
+            source_credentials, _ = _get_service_account_credentials(
+                filename, source_credentials_info
+            )
+        elif source_credentials_type == _EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE:
+            source_credentials, _ = _get_external_account_authorized_user_credentials(
+                filename, source_credentials_info
+            )
+        else:
+            raise exceptions.InvalidType(
+                "source credential of type {} is not supported.".format(
+                    source_credentials_type
+                )
+            )
+        impersonation_url = info.get("service_account_impersonation_url")
+        start_index = impersonation_url.rfind("/")
+        end_index = impersonation_url.find(":generateAccessToken")
+        if start_index == -1 or end_index == -1 or start_index > end_index:
+            raise exceptions.InvalidValue(
+                "Cannot extract target principal from {}".format(impersonation_url)
+            )
+        target_principal = impersonation_url[start_index + 1 : end_index]
+        delegates = info.get("delegates")
+        quota_project_id = info.get("quota_project_id")
+        credentials = impersonated_credentials.Credentials(
+            source_credentials,
+            target_principal,
+            scopes,
+            delegates,
+            quota_project_id=quota_project_id,
+        )
+    except ValueError as caught_exc:
+        msg = "Failed to load impersonated service account credentials from {}".format(
+            filename
+        )
+        new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
+        raise new_exc from caught_exc
+    return credentials, None
+
+
+def _get_gdch_service_account_credentials(filename, info):
+    from google.oauth2 import gdch_credentials
+
+    try:
+        credentials = gdch_credentials.ServiceAccountCredentials.from_service_account_info(
+            info
+        )
+    except ValueError as caught_exc:
+        msg = "Failed to load GDCH service account credentials from {}".format(filename)
+        new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
+        raise new_exc from caught_exc
+    return credentials, info.get("project")
+
+
+def get_api_key_credentials(key):
+    """Return credentials with the given API key."""
+    from google.auth import api_key
+
+    return api_key.Credentials(key)
+
+
+def _apply_quota_project_id(credentials, quota_project_id):
+    if quota_project_id:
+        credentials = credentials.with_quota_project(quota_project_id)
+    else:
+        credentials = credentials.with_quota_project_from_environment()
+
+    from google.oauth2 import credentials as authorized_user_credentials
+
+    if isinstance(credentials, authorized_user_credentials.Credentials) and (
+        not credentials.quota_project_id
+    ):
+        _warn_about_problematic_credentials(credentials)
+    return credentials
+
+
+def default(scopes=None, request=None, quota_project_id=None, default_scopes=None):
+    """Gets the default credentials for the current environment.
+
+    `Application Default Credentials`_ provides an easy way to obtain
+    credentials to call Google APIs for server-to-server or local applications.
+    This function acquires credentials from the environment in the following
+    order:
+
+    1. If the environment variable ``GOOGLE_APPLICATION_CREDENTIALS`` is set
+       to the path of a valid service account JSON private key file, then it is
+       loaded and returned. The project ID returned is the project ID defined
+       in the service account file if available (some older files do not
+       contain project ID information).
+
+       If the environment variable is set to the path of a valid external
+       account JSON configuration file (workload identity federation), then the
+       configuration file is used to determine and retrieve the external
+       credentials from the current environment (AWS, Azure, etc).
+       These will then be exchanged for Google access tokens via the Google STS
+       endpoint.
+       The project ID returned in this case is the one corresponding to the
+       underlying workload identity pool resource if determinable.
+
+       If the environment variable is set to the path of a valid GDCH service
+       account JSON file (`Google Distributed Cloud Hosted`_), then a GDCH
+       credential will be returned. The project ID returned is the project
+       specified in the JSON file.
+    2. If the `Google Cloud SDK`_ is installed and has application default
+       credentials set they are loaded and returned.
+
+       To enable application default credentials with the Cloud SDK run::
+
+            gcloud auth application-default login
+
+       If the Cloud SDK has an active project, the project ID is returned. The
+       active project can be set using::
+
+            gcloud config set project
+
+    3. If the application is running in the `App Engine standard environment`_
+       (first generation) then the credentials and project ID from the
+       `App Identity Service`_ are used.
+    4. If the application is running in `Compute Engine`_ or `Cloud Run`_ or
+       the `App Engine flexible environment`_ or the `App Engine standard
+       environment`_ (second generation) then the credentials and project ID
+       are obtained from the `Metadata Service`_.
+    5. If no credentials are found,
+       :class:`~google.auth.exceptions.DefaultCredentialsError` will be raised.
+
+    .. _Application Default Credentials: https://developers.google.com\
+            /identity/protocols/application-default-credentials
+    .. _Google Cloud SDK: https://cloud.google.com/sdk
+    .. _App Engine standard environment: https://cloud.google.com/appengine
+    .. _App Identity Service: https://cloud.google.com/appengine/docs/python\
+            /appidentity/
+    .. _Compute Engine: https://cloud.google.com/compute
+    .. _App Engine flexible environment: https://cloud.google.com\
+            /appengine/flexible
+    .. _Metadata Service: https://cloud.google.com/compute/docs\
+            /storing-retrieving-metadata
+    .. _Cloud Run: https://cloud.google.com/run
+    .. _Google Distributed Cloud Hosted: https://cloud.google.com/blog/topics\
+            /hybrid-cloud/announcing-google-distributed-cloud-edge-and-hosted
+
+    Example::
+
+        import google.auth
+
+        credentials, project_id = google.auth.default()
+
+    Args:
+        scopes (Sequence[str]): The list of scopes for the credentials. If
+            specified, the credentials will automatically be scoped if
+            necessary.
+        request (Optional[google.auth.transport.Request]): An object used to make
+            HTTP requests. This is used to either detect whether the application
+            is running on Compute Engine or to determine the associated project
+            ID for a workload identity pool resource (external account
+            credentials). If not specified, then it will either use the standard
+            library http client to make requests for Compute Engine credentials
+            or a google.auth.transport.requests.Request client for external
+            account credentials.
+        quota_project_id (Optional[str]): The project ID used for
+            quota and billing.
+        default_scopes (Optional[Sequence[str]]): Default scopes passed by a
+            Google client library. Use 'scopes' for user-defined scopes.
+    Returns:
+        Tuple[~google.auth.credentials.Credentials, Optional[str]]:
+            the current environment's credentials and project ID. Project ID
+            may be None, which indicates that the Project ID could not be
+            ascertained from the environment.
+
+    Raises:
+        ~google.auth.exceptions.DefaultCredentialsError:
+            If no credentials were found, or if the credentials found were
+            invalid.
+    """
+    from google.auth.credentials import with_scopes_if_required
+    from google.auth.credentials import CredentialsWithQuotaProject
+
+    explicit_project_id = os.environ.get(
+        environment_vars.PROJECT, os.environ.get(environment_vars.LEGACY_PROJECT)
+    )
+
+    checkers = (
+        # Avoid passing scopes here to prevent passing scopes to user credentials.
+        # 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.
+        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, quota_project_id=quota_project_id),
+    )
+
+    for checker in checkers:
+        credentials, project_id = checker()
+        if credentials is not None:
+            credentials = with_scopes_if_required(
+                credentials, scopes, default_scopes=default_scopes
+            )
+
+            effective_project_id = explicit_project_id or project_id
+
+            # For external account credentials, scopes are required to determine
+            # the project ID. Try to get the project ID again if not yet
+            # determined.
+            if not effective_project_id and callable(
+                getattr(credentials, "get_project_id", None)
+            ):
+                if request is None:
+                    import google.auth.transport.requests
+
+                    request = google.auth.transport.requests.Request()
+                effective_project_id = credentials.get_project_id(request=request)
+
+            if quota_project_id and isinstance(
+                credentials, CredentialsWithQuotaProject
+            ):
+                credentials = credentials.with_quota_project(quota_project_id)
+
+            if not effective_project_id:
+                _LOGGER.warning(
+                    "No project ID could be determined. Consider running "
+                    "`gcloud config set project` or setting the %s "
+                    "environment variable",
+                    environment_vars.PROJECT,
+                )
+            return credentials, effective_project_id
+
+    raise exceptions.DefaultCredentialsError(_CLOUD_SDK_MISSING_CREDENTIALS)
diff --git a/.venv/lib/python3.12/site-packages/google/auth/_default_async.py b/.venv/lib/python3.12/site-packages/google/auth/_default_async.py
new file mode 100644
index 00000000..2e53e208
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/_default_async.py
@@ -0,0 +1,282 @@
+# Copyright 2020 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Application default credentials.
+
+Implements application default credentials and project ID detection.
+"""
+
+import io
+import json
+import os
+
+from google.auth import _default
+from google.auth import environment_vars
+from google.auth import exceptions
+
+
+def load_credentials_from_file(filename, scopes=None, quota_project_id=None):
+    """Loads Google credentials from a file.
+
+    The credentials file must be a service account key or stored authorized
+    user credentials.
+
+    Args:
+        filename (str): The full path to the credentials file.
+        scopes (Optional[Sequence[str]]): The list of scopes for the credentials. If
+            specified, the credentials will automatically be scoped if
+            necessary
+        quota_project_id (Optional[str]):  The project ID used for
+                quota and billing.
+
+    Returns:
+        Tuple[google.auth.credentials.Credentials, Optional[str]]: Loaded
+            credentials and the project ID. Authorized user credentials do not
+            have the project ID information.
+
+    Raises:
+        google.auth.exceptions.DefaultCredentialsError: if the file is in the
+            wrong format or is missing.
+    """
+    if not os.path.exists(filename):
+        raise exceptions.DefaultCredentialsError(
+            "File {} was not found.".format(filename)
+        )
+
+    with io.open(filename, "r") as file_obj:
+        try:
+            info = json.load(file_obj)
+        except ValueError as caught_exc:
+            new_exc = exceptions.DefaultCredentialsError(
+                "File {} is not a valid json file.".format(filename), caught_exc
+            )
+            raise new_exc from caught_exc
+
+    # The type key should indicate that the file is either a service account
+    # credentials file or an authorized user credentials file.
+    credential_type = info.get("type")
+
+    if credential_type == _default._AUTHORIZED_USER_TYPE:
+        from google.oauth2 import _credentials_async as credentials
+
+        try:
+            credentials = credentials.Credentials.from_authorized_user_info(
+                info, scopes=scopes
+            )
+        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
+
+    elif credential_type == _default._SERVICE_ACCOUNT_TYPE:
+        from google.oauth2 import _service_account_async as service_account
+
+        try:
+            credentials = service_account.Credentials.from_service_account_info(
+                info, scopes=scopes
+            ).with_quota_project(quota_project_id)
+        except ValueError as caught_exc:
+            msg = "Failed to load service account credentials from {}".format(filename)
+            new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
+            raise new_exc from caught_exc
+        return credentials, info.get("project_id")
+
+    else:
+        raise exceptions.DefaultCredentialsError(
+            "The file {file} does not have a valid type. "
+            "Type is {type}, expected one of {valid_types}.".format(
+                file=filename, type=credential_type, valid_types=_default._VALID_TYPES
+            )
+        )
+
+
+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
+
+    # Check if application default credentials exist.
+    credentials_filename = _cloud_sdk.get_application_default_credentials_path()
+
+    if not os.path.isfile(credentials_filename):
+        return None, None
+
+    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()
+
+    return credentials, project_id
+
+
+def _get_explicit_environ_credentials(quota_project_id=None):
+    """Gets credentials from the GOOGLE_APPLICATION_CREDENTIALS environment
+    variable."""
+    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)
+
+    if explicit_file is not None 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.
+        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], quota_project_id=quota_project_id
+        )
+
+        return credentials, project_id
+
+    else:
+        return None, None
+
+
+def _get_gae_credentials():
+    """Gets Google App Engine App Identity credentials and project ID."""
+    # While this library is normally bundled with app_engine, there are
+    # some cases where it's not available, so we tolerate ImportError.
+
+    return _default._get_gae_credentials()
+
+
+def _get_gce_credentials(request=None):
+    """Gets credentials and project ID from the GCE Metadata Service."""
+    # Ping requires a transport, but we want application default credentials
+    # to require no arguments. So, we'll use the _http_client transport which
+    # uses http.client. This is only acceptable because the metadata server
+    # doesn't do SSL and never requires proxies.
+
+    # While this library is normally bundled with compute_engine, there are
+    # some cases where it's not available, so we tolerate ImportError.
+
+    return _default._get_gce_credentials(request)
+
+
+def default_async(scopes=None, request=None, quota_project_id=None):
+    """Gets the default credentials for the current environment.
+
+    `Application Default Credentials`_ provides an easy way to obtain
+    credentials to call Google APIs for server-to-server or local applications.
+    This function acquires credentials from the environment in the following
+    order:
+
+    1. If the environment variable ``GOOGLE_APPLICATION_CREDENTIALS`` is set
+       to the path of a valid service account JSON private key file, then it is
+       loaded and returned. The project ID returned is the project ID defined
+       in the service account file if available (some older files do not
+       contain project ID information).
+    2. If the `Google Cloud SDK`_ is installed and has application default
+       credentials set they are loaded and returned.
+
+       To enable application default credentials with the Cloud SDK run::
+
+            gcloud auth application-default login
+
+       If the Cloud SDK has an active project, the project ID is returned. The
+       active project can be set using::
+
+            gcloud config set project
+
+    3. If the application is running in the `App Engine standard environment`_
+       (first generation) then the credentials and project ID from the
+       `App Identity Service`_ are used.
+    4. If the application is running in `Compute Engine`_ or `Cloud Run`_ or
+       the `App Engine flexible environment`_ or the `App Engine standard
+       environment`_ (second generation) then the credentials and project ID
+       are obtained from the `Metadata Service`_.
+    5. If no credentials are found,
+       :class:`~google.auth.exceptions.DefaultCredentialsError` will be raised.
+
+    .. _Application Default Credentials: https://developers.google.com\
+            /identity/protocols/application-default-credentials
+    .. _Google Cloud SDK: https://cloud.google.com/sdk
+    .. _App Engine standard environment: https://cloud.google.com/appengine
+    .. _App Identity Service: https://cloud.google.com/appengine/docs/python\
+            /appidentity/
+    .. _Compute Engine: https://cloud.google.com/compute
+    .. _App Engine flexible environment: https://cloud.google.com\
+            /appengine/flexible
+    .. _Metadata Service: https://cloud.google.com/compute/docs\
+            /storing-retrieving-metadata
+    .. _Cloud Run: https://cloud.google.com/run
+
+    Example::
+
+        import google.auth
+
+        credentials, project_id = google.auth.default()
+
+    Args:
+        scopes (Sequence[str]): The list of scopes for the credentials. If
+            specified, the credentials will automatically be scoped if
+            necessary.
+        request (google.auth.transport.Request): An object used to make
+            HTTP requests. This is used to detect whether the application
+            is running on Compute Engine. If not specified, then it will
+            use the standard library http client to make requests.
+        quota_project_id (Optional[str]):  The project ID used for
+            quota and billing.
+    Returns:
+        Tuple[~google.auth.credentials.Credentials, Optional[str]]:
+            the current environment's credentials and project ID. Project ID
+            may be None, which indicates that the Project ID could not be
+            ascertained from the environment.
+
+    Raises:
+        ~google.auth.exceptions.DefaultCredentialsError:
+            If no credentials were found, or if the credentials found were
+            invalid.
+    """
+    from google.auth._credentials_async import with_scopes_if_required
+    from google.auth.credentials import CredentialsWithQuotaProject
+
+    explicit_project_id = os.environ.get(
+        environment_vars.PROJECT, os.environ.get(environment_vars.LEGACY_PROJECT)
+    )
+
+    checkers = (
+        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),
+    )
+
+    for checker in checkers:
+        credentials, project_id = checker()
+        if credentials is not None:
+            credentials = with_scopes_if_required(credentials, scopes)
+            if quota_project_id and isinstance(
+                credentials, CredentialsWithQuotaProject
+            ):
+                credentials = credentials.with_quota_project(quota_project_id)
+            effective_project_id = explicit_project_id or project_id
+            if not effective_project_id:
+                _default._LOGGER.warning(
+                    "No project ID could be determined. Consider running "
+                    "`gcloud config set project` or setting the %s "
+                    "environment variable",
+                    environment_vars.PROJECT,
+                )
+            return credentials, effective_project_id
+
+    raise exceptions.DefaultCredentialsError(_default._CLOUD_SDK_MISSING_CREDENTIALS)
diff --git a/.venv/lib/python3.12/site-packages/google/auth/_exponential_backoff.py b/.venv/lib/python3.12/site-packages/google/auth/_exponential_backoff.py
new file mode 100644
index 00000000..89853448
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/_exponential_backoff.py
@@ -0,0 +1,164 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import asyncio
+import random
+import time
+
+from google.auth import exceptions
+
+# The default amount of retry attempts
+_DEFAULT_RETRY_TOTAL_ATTEMPTS = 3
+
+# The default initial backoff period (1.0 second).
+_DEFAULT_INITIAL_INTERVAL_SECONDS = 1.0
+
+# The default randomization factor (0.1 which results in a random period ranging
+# between 10% below and 10% above the retry interval).
+_DEFAULT_RANDOMIZATION_FACTOR = 0.1
+
+# The default multiplier value (2 which is 100% increase per back off).
+_DEFAULT_MULTIPLIER = 2.0
+
+"""Exponential Backoff Utility
+
+This is a private module that implements the exponential back off algorithm.
+It can be used as a utility for code that needs to retry on failure, for example
+an HTTP request.
+"""
+
+
+class _BaseExponentialBackoff:
+    """An exponential backoff iterator base class.
+
+    Args:
+        total_attempts Optional[int]:
+            The maximum amount of retries that should happen.
+            The default value is 3 attempts.
+        initial_wait_seconds Optional[int]:
+            The amount of time to sleep in the first backoff. This parameter
+            should be in seconds.
+            The default value is 1 second.
+        randomization_factor Optional[float]:
+            The amount of jitter that should be in each backoff. For example,
+            a value of 0.1 will introduce a jitter range of 10% to the
+            current backoff period.
+            The default value is 0.1.
+        multiplier Optional[float]:
+            The backoff multipler. This adjusts how much each backoff will
+            increase. For example a value of 2.0 leads to a 200% backoff
+            on each attempt. If the initial_wait is 1.0 it would look like
+            this sequence [1.0, 2.0, 4.0, 8.0].
+            The default value is 2.0.
+    """
+
+    def __init__(
+        self,
+        total_attempts=_DEFAULT_RETRY_TOTAL_ATTEMPTS,
+        initial_wait_seconds=_DEFAULT_INITIAL_INTERVAL_SECONDS,
+        randomization_factor=_DEFAULT_RANDOMIZATION_FACTOR,
+        multiplier=_DEFAULT_MULTIPLIER,
+    ):
+        if total_attempts < 1:
+            raise exceptions.InvalidValue(
+                f"total_attempts must be greater than or equal to 1 but was {total_attempts}"
+            )
+
+        self._total_attempts = total_attempts
+        self._initial_wait_seconds = initial_wait_seconds
+
+        self._current_wait_in_seconds = self._initial_wait_seconds
+
+        self._randomization_factor = randomization_factor
+        self._multiplier = multiplier
+        self._backoff_count = 0
+
+    @property
+    def total_attempts(self):
+        """The total amount of backoff attempts that will be made."""
+        return self._total_attempts
+
+    @property
+    def backoff_count(self):
+        """The current amount of backoff attempts that have been made."""
+        return self._backoff_count
+
+    def _reset(self):
+        self._backoff_count = 0
+        self._current_wait_in_seconds = self._initial_wait_seconds
+
+    def _calculate_jitter(self):
+        jitter_variance = self._current_wait_in_seconds * self._randomization_factor
+        jitter = random.uniform(
+            self._current_wait_in_seconds - jitter_variance,
+            self._current_wait_in_seconds + jitter_variance,
+        )
+
+        return jitter
+
+
+class ExponentialBackoff(_BaseExponentialBackoff):
+    """An exponential backoff iterator. This can be used in a for loop to
+    perform requests with exponential backoff.
+    """
+
+    def __init__(self, *args, **kwargs):
+        super(ExponentialBackoff, self).__init__(*args, **kwargs)
+
+    def __iter__(self):
+        self._reset()
+        return self
+
+    def __next__(self):
+        if self._backoff_count >= self._total_attempts:
+            raise StopIteration
+        self._backoff_count += 1
+
+        if self._backoff_count <= 1:
+            return self._backoff_count
+
+        jitter = self._calculate_jitter()
+
+        time.sleep(jitter)
+
+        self._current_wait_in_seconds *= self._multiplier
+        return self._backoff_count
+
+
+class AsyncExponentialBackoff(_BaseExponentialBackoff):
+    """An async exponential backoff iterator. This can be used in a for loop to
+    perform async requests with exponential backoff.
+    """
+
+    def __init__(self, *args, **kwargs):
+        super(AsyncExponentialBackoff, self).__init__(*args, **kwargs)
+
+    def __aiter__(self):
+        self._reset()
+        return self
+
+    async def __anext__(self):
+        if self._backoff_count >= self._total_attempts:
+            raise StopAsyncIteration
+        self._backoff_count += 1
+
+        if self._backoff_count <= 1:
+            return self._backoff_count
+
+        jitter = self._calculate_jitter()
+
+        await asyncio.sleep(jitter)
+
+        self._current_wait_in_seconds *= self._multiplier
+        return self._backoff_count
diff --git a/.venv/lib/python3.12/site-packages/google/auth/_helpers.py b/.venv/lib/python3.12/site-packages/google/auth/_helpers.py
new file mode 100644
index 00000000..a6c07f7d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/_helpers.py
@@ -0,0 +1,273 @@
+# Copyright 2015 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Helper functions for commonly used utilities."""
+
+import base64
+import calendar
+import datetime
+from email.message import Message
+import sys
+import urllib
+
+from google.auth import exceptions
+
+# The smallest MDS cache used by this library stores tokens until 4 minutes from
+# expiry.
+REFRESH_THRESHOLD = datetime.timedelta(minutes=3, seconds=45)
+
+
+def copy_docstring(source_class):
+    """Decorator that copies a method's docstring from another class.
+
+    Args:
+        source_class (type): The class that has the documented method.
+
+    Returns:
+        Callable: A decorator that will copy the docstring of the same
+            named method in the source class to the decorated method.
+    """
+
+    def decorator(method):
+        """Decorator implementation.
+
+        Args:
+            method (Callable): The method to copy the docstring to.
+
+        Returns:
+            Callable: the same method passed in with an updated docstring.
+
+        Raises:
+            google.auth.exceptions.InvalidOperation: if the method already has a docstring.
+        """
+        if method.__doc__:
+            raise exceptions.InvalidOperation("Method already has a docstring.")
+
+        source_method = getattr(source_class, method.__name__)
+        method.__doc__ = source_method.__doc__
+
+        return method
+
+    return decorator
+
+
+def parse_content_type(header_value):
+    """Parse a 'content-type' header value to get just the plain media-type (without parameters).
+
+    This is done using the class Message from email.message as suggested in PEP 594
+        (because the cgi is now deprecated and will be removed in python 3.13,
+        see https://peps.python.org/pep-0594/#cgi).
+
+    Args:
+        header_value (str): The value of a 'content-type' header as a string.
+
+    Returns:
+        str: A string with just the lowercase media-type from the parsed 'content-type' header.
+            If the provided content-type is not parsable, returns 'text/plain',
+            the default value for textual files.
+    """
+    m = Message()
+    m["content-type"] = header_value
+    return (
+        m.get_content_type()
+    )  # Despite the name, actually returns just the media-type
+
+
+def utcnow():
+    """Returns the current UTC datetime.
+
+    Returns:
+        datetime: The current time in UTC.
+    """
+    # We used datetime.utcnow() before, since it's deprecated from python 3.12,
+    # we are using datetime.now(timezone.utc) now. "utcnow()" is offset-native
+    # (no timezone info), but "now()" is offset-aware (with timezone info).
+    # This will cause datetime comparison problem. For backward compatibility,
+    # we need to remove the timezone info.
+    now = datetime.datetime.now(datetime.timezone.utc)
+    now = now.replace(tzinfo=None)
+    return now
+
+
+def datetime_to_secs(value):
+    """Convert a datetime object to the number of seconds since the UNIX epoch.
+
+    Args:
+        value (datetime): The datetime to convert.
+
+    Returns:
+        int: The number of seconds since the UNIX epoch.
+    """
+    return calendar.timegm(value.utctimetuple())
+
+
+def to_bytes(value, encoding="utf-8"):
+    """Converts a string value to bytes, if necessary.
+
+    Args:
+        value (Union[str, bytes]): The value to be converted.
+        encoding (str): The encoding to use to convert unicode to bytes.
+            Defaults to "utf-8".
+
+    Returns:
+        bytes: The original value converted to bytes (if unicode) or as
+            passed in if it started out as bytes.
+
+    Raises:
+        google.auth.exceptions.InvalidValue: If the value could not be converted to bytes.
+    """
+    result = value.encode(encoding) if isinstance(value, str) else value
+    if isinstance(result, bytes):
+        return result
+    else:
+        raise exceptions.InvalidValue(
+            "{0!r} could not be converted to bytes".format(value)
+        )
+
+
+def from_bytes(value):
+    """Converts bytes to a string value, if necessary.
+
+    Args:
+        value (Union[str, bytes]): The value to be converted.
+
+    Returns:
+        str: The original value converted to unicode (if bytes) or as passed in
+            if it started out as unicode.
+
+    Raises:
+        google.auth.exceptions.InvalidValue: If the value could not be converted to unicode.
+    """
+    result = value.decode("utf-8") if isinstance(value, bytes) else value
+    if isinstance(result, str):
+        return result
+    else:
+        raise exceptions.InvalidValue(
+            "{0!r} could not be converted to unicode".format(value)
+        )
+
+
+def update_query(url, params, remove=None):
+    """Updates a URL's query parameters.
+
+    Replaces any current values if they are already present in the URL.
+
+    Args:
+        url (str): The URL to update.
+        params (Mapping[str, str]): A mapping of query parameter
+            keys to values.
+        remove (Sequence[str]): Parameters to remove from the query string.
+
+    Returns:
+        str: The URL with updated query parameters.
+
+    Examples:
+
+        >>> url = 'http://example.com?a=1'
+        >>> update_query(url, {'a': '2'})
+        http://example.com?a=2
+        >>> update_query(url, {'b': '3'})
+        http://example.com?a=1&b=3
+        >> update_query(url, {'b': '3'}, remove=['a'])
+        http://example.com?b=3
+
+    """
+    if remove is None:
+        remove = []
+
+    # Split the URL into parts.
+    parts = urllib.parse.urlparse(url)
+    # Parse the query string.
+    query_params = urllib.parse.parse_qs(parts.query)
+    # Update the query parameters with the new parameters.
+    query_params.update(params)
+    # Remove any values specified in remove.
+    query_params = {
+        key: value for key, value in query_params.items() if key not in remove
+    }
+    # Re-encoded the query string.
+    new_query = urllib.parse.urlencode(query_params, doseq=True)
+    # Unsplit the url.
+    new_parts = parts._replace(query=new_query)
+    return urllib.parse.urlunparse(new_parts)
+
+
+def scopes_to_string(scopes):
+    """Converts scope value to a string suitable for sending to OAuth 2.0
+    authorization servers.
+
+    Args:
+        scopes (Sequence[str]): The sequence of scopes to convert.
+
+    Returns:
+        str: The scopes formatted as a single string.
+    """
+    return " ".join(scopes)
+
+
+def string_to_scopes(scopes):
+    """Converts stringifed scopes value to a list.
+
+    Args:
+        scopes (Union[Sequence, str]): The string of space-separated scopes
+            to convert.
+    Returns:
+        Sequence(str): The separated scopes.
+    """
+    if not scopes:
+        return []
+
+    return scopes.split(" ")
+
+
+def padded_urlsafe_b64decode(value):
+    """Decodes base64 strings lacking padding characters.
+
+    Google infrastructure tends to omit the base64 padding characters.
+
+    Args:
+        value (Union[str, bytes]): The encoded value.
+
+    Returns:
+        bytes: The decoded value
+    """
+    b64string = to_bytes(value)
+    padded = b64string + b"=" * (-len(b64string) % 4)
+    return base64.urlsafe_b64decode(padded)
+
+
+def unpadded_urlsafe_b64encode(value):
+    """Encodes base64 strings removing any padding characters.
+
+    `rfc 7515`_ defines Base64url to NOT include any padding
+    characters, but the stdlib doesn't do that by default.
+
+    _rfc7515: https://tools.ietf.org/html/rfc7515#page-6
+
+    Args:
+        value (Union[str|bytes]): The bytes-like value to encode
+
+    Returns:
+        Union[str|bytes]: The encoded value
+    """
+    return base64.urlsafe_b64encode(value).rstrip(b"=")
+
+
+def is_python_3():
+    """Check if the Python interpreter is Python 2 or 3.
+
+    Returns:
+        bool: True if the Python interpreter is Python 3 and False otherwise.
+    """
+    return sys.version_info > (3, 0)
diff --git a/.venv/lib/python3.12/site-packages/google/auth/_jwt_async.py b/.venv/lib/python3.12/site-packages/google/auth/_jwt_async.py
new file mode 100644
index 00000000..3a1abc5b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/_jwt_async.py
@@ -0,0 +1,164 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""JSON Web Tokens
+
+Provides support for creating (encoding) and verifying (decoding) JWTs,
+especially JWTs generated and consumed by Google infrastructure.
+
+See `rfc7519`_ for more details on JWTs.
+
+To encode a JWT use :func:`encode`::
+
+    from google.auth import crypt
+    from google.auth import jwt_async
+
+    signer = crypt.Signer(private_key)
+    payload = {'some': 'payload'}
+    encoded = jwt_async.encode(signer, payload)
+
+To decode a JWT and verify claims use :func:`decode`::
+
+    claims = jwt_async.decode(encoded, certs=public_certs)
+
+You can also skip verification::
+
+    claims = jwt_async.decode(encoded, verify=False)
+
+.. _rfc7519: https://tools.ietf.org/html/rfc7519
+
+
+NOTE: This async support is experimental and marked internal. This surface may
+change in minor releases.
+"""
+
+from google.auth import _credentials_async
+from google.auth import jwt
+
+
+def encode(signer, payload, header=None, key_id=None):
+    """Make a signed JWT.
+
+    Args:
+        signer (google.auth.crypt.Signer): The signer used to sign the JWT.
+        payload (Mapping[str, str]): The JWT payload.
+        header (Mapping[str, str]): Additional JWT header payload.
+        key_id (str): The key id to add to the JWT header. If the
+            signer has a key id it will be used as the default. If this is
+            specified it will override the signer's key id.
+
+    Returns:
+        bytes: The encoded JWT.
+    """
+    return jwt.encode(signer, payload, header, key_id)
+
+
+def decode(token, certs=None, verify=True, audience=None):
+    """Decode and verify a JWT.
+
+    Args:
+        token (str): The encoded JWT.
+        certs (Union[str, bytes, Mapping[str, Union[str, bytes]]]): The
+            certificate used to validate the JWT signature. If bytes or string,
+            it must the the public key certificate in PEM format. If a mapping,
+            it must be a mapping of key IDs to public key certificates in PEM
+            format. The mapping must contain the same key ID that's specified
+            in the token's header.
+        verify (bool): Whether to perform signature and claim validation.
+            Verification is done by default.
+        audience (str): The audience claim, 'aud', that this JWT should
+            contain. If None then the JWT's 'aud' parameter is not verified.
+
+    Returns:
+        Mapping[str, str]: The deserialized JSON payload in the JWT.
+
+    Raises:
+        ValueError: if any verification checks failed.
+    """
+
+    return jwt.decode(token, certs, verify, audience)
+
+
+class Credentials(
+    jwt.Credentials, _credentials_async.Signing, _credentials_async.Credentials
+):
+    """Credentials that use a JWT as the bearer token.
+
+    These credentials require an "audience" claim. This claim identifies the
+    intended recipient of the bearer token.
+
+    The constructor arguments determine the claims for the JWT that is
+    sent with requests. Usually, you'll construct these credentials with
+    one of the helper constructors as shown in the next section.
+
+    To create JWT credentials using a Google service account private key
+    JSON file::
+
+        audience = 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher'
+        credentials = jwt_async.Credentials.from_service_account_file(
+            'service-account.json',
+            audience=audience)
+
+    If you already have the service account file loaded and parsed::
+
+        service_account_info = json.load(open('service_account.json'))
+        credentials = jwt_async.Credentials.from_service_account_info(
+            service_account_info,
+            audience=audience)
+
+    Both helper methods pass on arguments to the constructor, so you can
+    specify the JWT claims::
+
+        credentials = jwt_async.Credentials.from_service_account_file(
+            'service-account.json',
+            audience=audience,
+            additional_claims={'meta': 'data'})
+
+    You can also construct the credentials directly if you have a
+    :class:`~google.auth.crypt.Signer` instance::
+
+        credentials = jwt_async.Credentials(
+            signer,
+            issuer='your-issuer',
+            subject='your-subject',
+            audience=audience)
+
+    The claims are considered immutable. If you want to modify the claims,
+    you can easily create another instance using :meth:`with_claims`::
+
+        new_audience = (
+            'https://pubsub.googleapis.com/google.pubsub.v1.Subscriber')
+        new_credentials = credentials.with_claims(audience=new_audience)
+    """
+
+
+class OnDemandCredentials(
+    jwt.OnDemandCredentials, _credentials_async.Signing, _credentials_async.Credentials
+):
+    """On-demand JWT credentials.
+
+    Like :class:`Credentials`, this class uses a JWT as the bearer token for
+    authentication. However, this class does not require the audience at
+    construction time. Instead, it will generate a new token on-demand for
+    each request using the request URI as the audience. It caches tokens
+    so that multiple requests to the same URI do not incur the overhead
+    of generating a new token every time.
+
+    This behavior is especially useful for `gRPC`_ clients. A gRPC service may
+    have multiple audience and gRPC clients may not know all of the audiences
+    required for accessing a particular service. With these credentials,
+    no knowledge of the audiences is required ahead of time.
+
+    .. _grpc: http://www.grpc.io/
+    """
diff --git a/.venv/lib/python3.12/site-packages/google/auth/_oauth2client.py b/.venv/lib/python3.12/site-packages/google/auth/_oauth2client.py
new file mode 100644
index 00000000..8b83ff23
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/_oauth2client.py
@@ -0,0 +1,167 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Helpers for transitioning from oauth2client to google-auth.
+
+.. warning::
+    This module is private as it is intended to assist first-party downstream
+    clients with the transition from oauth2client to google-auth.
+"""
+
+from __future__ import absolute_import
+
+from google.auth import _helpers
+import google.auth.app_engine
+import google.auth.compute_engine
+import google.oauth2.credentials
+import google.oauth2.service_account
+
+try:
+    import oauth2client.client  # type: ignore
+    import oauth2client.contrib.gce  # type: ignore
+    import oauth2client.service_account  # type: ignore
+except ImportError as caught_exc:
+    raise ImportError("oauth2client is not installed.") from caught_exc
+
+try:
+    import oauth2client.contrib.appengine  # type: ignore
+
+    _HAS_APPENGINE = True
+except ImportError:
+    _HAS_APPENGINE = False
+
+
+_CONVERT_ERROR_TMPL = "Unable to convert {} to a google-auth credentials class."
+
+
+def _convert_oauth2_credentials(credentials):
+    """Converts to :class:`google.oauth2.credentials.Credentials`.
+
+    Args:
+        credentials (Union[oauth2client.client.OAuth2Credentials,
+            oauth2client.client.GoogleCredentials]): The credentials to
+            convert.
+
+    Returns:
+        google.oauth2.credentials.Credentials: The converted credentials.
+    """
+    new_credentials = google.oauth2.credentials.Credentials(
+        token=credentials.access_token,
+        refresh_token=credentials.refresh_token,
+        token_uri=credentials.token_uri,
+        client_id=credentials.client_id,
+        client_secret=credentials.client_secret,
+        scopes=credentials.scopes,
+    )
+
+    new_credentials._expires = credentials.token_expiry
+
+    return new_credentials
+
+
+def _convert_service_account_credentials(credentials):
+    """Converts to :class:`google.oauth2.service_account.Credentials`.
+
+    Args:
+        credentials (Union[
+            oauth2client.service_account.ServiceAccountCredentials,
+            oauth2client.service_account._JWTAccessCredentials]): The
+            credentials to convert.
+
+    Returns:
+        google.oauth2.service_account.Credentials: The converted credentials.
+    """
+    info = credentials.serialization_data.copy()
+    info["token_uri"] = credentials.token_uri
+    return google.oauth2.service_account.Credentials.from_service_account_info(info)
+
+
+def _convert_gce_app_assertion_credentials(credentials):
+    """Converts to :class:`google.auth.compute_engine.Credentials`.
+
+    Args:
+        credentials (oauth2client.contrib.gce.AppAssertionCredentials): The
+            credentials to convert.
+
+    Returns:
+        google.oauth2.service_account.Credentials: The converted credentials.
+    """
+    return google.auth.compute_engine.Credentials(
+        service_account_email=credentials.service_account_email
+    )
+
+
+def _convert_appengine_app_assertion_credentials(credentials):
+    """Converts to :class:`google.auth.app_engine.Credentials`.
+
+    Args:
+        credentials (oauth2client.contrib.app_engine.AppAssertionCredentials):
+            The credentials to convert.
+
+    Returns:
+        google.oauth2.service_account.Credentials: The converted credentials.
+    """
+    # pylint: disable=invalid-name
+    return google.auth.app_engine.Credentials(
+        scopes=_helpers.string_to_scopes(credentials.scope),
+        service_account_id=credentials.service_account_id,
+    )
+
+
+_CLASS_CONVERSION_MAP = {
+    oauth2client.client.OAuth2Credentials: _convert_oauth2_credentials,
+    oauth2client.client.GoogleCredentials: _convert_oauth2_credentials,
+    oauth2client.service_account.ServiceAccountCredentials: _convert_service_account_credentials,
+    oauth2client.service_account._JWTAccessCredentials: _convert_service_account_credentials,
+    oauth2client.contrib.gce.AppAssertionCredentials: _convert_gce_app_assertion_credentials,
+}
+
+if _HAS_APPENGINE:
+    _CLASS_CONVERSION_MAP[
+        oauth2client.contrib.appengine.AppAssertionCredentials
+    ] = _convert_appengine_app_assertion_credentials
+
+
+def convert(credentials):
+    """Convert oauth2client credentials to google-auth credentials.
+
+    This class converts:
+
+    - :class:`oauth2client.client.OAuth2Credentials` to
+      :class:`google.oauth2.credentials.Credentials`.
+    - :class:`oauth2client.client.GoogleCredentials` to
+      :class:`google.oauth2.credentials.Credentials`.
+    - :class:`oauth2client.service_account.ServiceAccountCredentials` to
+      :class:`google.oauth2.service_account.Credentials`.
+    - :class:`oauth2client.service_account._JWTAccessCredentials` to
+      :class:`google.oauth2.service_account.Credentials`.
+    - :class:`oauth2client.contrib.gce.AppAssertionCredentials` to
+      :class:`google.auth.compute_engine.Credentials`.
+    - :class:`oauth2client.contrib.appengine.AppAssertionCredentials` to
+      :class:`google.auth.app_engine.Credentials`.
+
+    Returns:
+        google.auth.credentials.Credentials: The converted credentials.
+
+    Raises:
+        ValueError: If the credentials could not be converted.
+    """
+
+    credentials_class = type(credentials)
+
+    try:
+        return _CLASS_CONVERSION_MAP[credentials_class](credentials)
+    except KeyError as caught_exc:
+        new_exc = ValueError(_CONVERT_ERROR_TMPL.format(credentials_class))
+        raise new_exc from caught_exc
diff --git a/.venv/lib/python3.12/site-packages/google/auth/_refresh_worker.py b/.venv/lib/python3.12/site-packages/google/auth/_refresh_worker.py
new file mode 100644
index 00000000..674032d8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/_refresh_worker.py
@@ -0,0 +1,109 @@
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import copy
+import logging
+import threading
+
+import google.auth.exceptions as e
+
+_LOGGER = logging.getLogger(__name__)
+
+
+class RefreshThreadManager:
+    """
+    Organizes exactly one background job that refresh a token.
+    """
+
+    def __init__(self):
+        """Initializes the manager."""
+
+        self._worker = None
+        self._lock = threading.Lock()  # protects access to worker threads.
+
+    def start_refresh(self, cred, request):
+        """Starts a refresh thread for the given credentials.
+        The credentials are refreshed using the request parameter.
+        request and cred MUST not be None
+
+        Returns True if a background refresh was kicked off. False otherwise.
+
+        Args:
+            cred: A credentials object.
+            request: A request object.
+        Returns:
+          bool
+        """
+        if cred is None or request is None:
+            raise e.InvalidValue(
+                "Unable to start refresh. cred and request must be valid and instantiated objects."
+            )
+
+        with self._lock:
+            if self._worker is not None and self._worker._error_info is not None:
+                return False
+
+            if self._worker is None or not self._worker.is_alive():  # pragma: NO COVER
+                self._worker = RefreshThread(cred=cred, request=copy.deepcopy(request))
+                self._worker.start()
+        return True
+
+    def clear_error(self):
+        """
+      Removes any errors that were stored from previous background refreshes.
+      """
+        with self._lock:
+            if self._worker:
+                self._worker._error_info = None
+
+    def __getstate__(self):
+        """Pickle helper that serializes the _lock attribute."""
+        state = self.__dict__.copy()
+        state["_lock"] = None
+        return state
+
+    def __setstate__(self, state):
+        """Pickle helper that deserializes the _lock attribute."""
+        state["_lock"] = threading.Lock()
+        self.__dict__.update(state)
+
+
+class RefreshThread(threading.Thread):
+    """
+    Thread that refreshes credentials.
+    """
+
+    def __init__(self, cred, request, **kwargs):
+        """Initializes the thread.
+
+        Args:
+            cred: A Credential object to refresh.
+            request: A Request object used to perform a credential refresh.
+            **kwargs: Additional keyword arguments.
+        """
+
+        super().__init__(**kwargs)
+        self._cred = cred
+        self._request = request
+        self._error_info = None
+
+    def run(self):
+        """
+        Perform the credential refresh.
+        """
+        try:
+            self._cred.refresh(self._request)
+        except Exception as err:  # pragma: NO COVER
+            _LOGGER.error(f"Background refresh failed due to: {err}")
+            self._error_info = err
diff --git a/.venv/lib/python3.12/site-packages/google/auth/_service_account_info.py b/.venv/lib/python3.12/site-packages/google/auth/_service_account_info.py
new file mode 100644
index 00000000..6b64adca
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/_service_account_info.py
@@ -0,0 +1,80 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Helper functions for loading data from a Google service account file."""
+
+import io
+import json
+
+from google.auth import crypt
+from google.auth import exceptions
+
+
+def from_dict(data, require=None, use_rsa_signer=True):
+    """Validates a dictionary containing Google service account data.
+
+    Creates and returns a :class:`google.auth.crypt.Signer` instance from the
+    private key specified in the data.
+
+    Args:
+        data (Mapping[str, str]): The service account data
+        require (Sequence[str]): List of keys required to be present in the
+            info.
+        use_rsa_signer (Optional[bool]): Whether to use RSA signer or EC signer.
+            We use RSA signer by default.
+
+    Returns:
+        google.auth.crypt.Signer: A signer created from the private key in the
+            service account file.
+
+    Raises:
+        MalformedError: if the data was in the wrong format, or if one of the
+            required keys is missing.
+    """
+    keys_needed = set(require if require is not None else [])
+
+    missing = keys_needed.difference(data.keys())
+
+    if missing:
+        raise exceptions.MalformedError(
+            "Service account info was not in the expected format, missing "
+            "fields {}.".format(", ".join(missing))
+        )
+
+    # Create a signer.
+    if use_rsa_signer:
+        signer = crypt.RSASigner.from_service_account_info(data)
+    else:
+        signer = crypt.ES256Signer.from_service_account_info(data)
+
+    return signer
+
+
+def from_filename(filename, require=None, use_rsa_signer=True):
+    """Reads a Google service account JSON file and returns its parsed info.
+
+    Args:
+        filename (str): The path to the service account .json file.
+        require (Sequence[str]): List of keys required to be present in the
+            info.
+        use_rsa_signer (Optional[bool]): Whether to use RSA signer or EC signer.
+            We use RSA signer by default.
+
+    Returns:
+        Tuple[ Mapping[str, str], google.auth.crypt.Signer ]: The verified
+            info and a signer instance.
+    """
+    with io.open(filename, "r", encoding="utf-8") as json_file:
+        data = json.load(json_file)
+        return data, from_dict(data, require=require, use_rsa_signer=use_rsa_signer)
diff --git a/.venv/lib/python3.12/site-packages/google/auth/aio/__init__.py b/.venv/lib/python3.12/site-packages/google/auth/aio/__init__.py
new file mode 100644
index 00000000..331708cb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/aio/__init__.py
@@ -0,0 +1,25 @@
+# Copyright 2024 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Google Auth AIO Library for Python."""
+
+import logging
+
+from google.auth import version as google_auth_version
+
+
+__version__ = google_auth_version.__version__
+
+# Set default logging handler to avoid "No handler found" warnings.
+logging.getLogger(__name__).addHandler(logging.NullHandler())
diff --git a/.venv/lib/python3.12/site-packages/google/auth/aio/credentials.py b/.venv/lib/python3.12/site-packages/google/auth/aio/credentials.py
new file mode 100644
index 00000000..3bc6a5a6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/aio/credentials.py
@@ -0,0 +1,143 @@
+# Copyright 2024 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""Interfaces for asynchronous credentials."""
+
+
+from google.auth import _helpers
+from google.auth import exceptions
+from google.auth._credentials_base import _BaseCredentials
+
+
+class Credentials(_BaseCredentials):
+    """Base class for all asynchronous credentials.
+
+    All credentials have a :attr:`token` that is used for authentication and
+    may also optionally set an :attr:`expiry` to indicate when the token will
+    no longer be valid.
+
+    Most credentials will be :attr:`invalid` until :meth:`refresh` is called.
+    Credentials can do this automatically before the first HTTP request in
+    :meth:`before_request`.
+
+    Although the token and expiration will change as the credentials are
+    :meth:`refreshed <refresh>` and used, credentials should be considered
+    immutable. Various credentials will accept configuration such as private
+    keys, scopes, and other options. These options are not changeable after
+    construction. Some classes will provide mechanisms to copy the credentials
+    with modifications such as :meth:`ScopedCredentials.with_scopes`.
+    """
+
+    def __init__(self):
+        super(Credentials, self).__init__()
+
+    async def apply(self, headers, token=None):
+        """Apply the token to the authentication header.
+
+        Args:
+            headers (Mapping): The HTTP request headers.
+            token (Optional[str]): If specified, overrides the current access
+                token.
+        """
+        self._apply(headers, token=token)
+
+    async def refresh(self, request):
+        """Refreshes the access token.
+
+        Args:
+            request (google.auth.aio.transport.Request): The object used to make
+                HTTP requests.
+
+        Raises:
+            google.auth.exceptions.RefreshError: If the credentials could
+                not be refreshed.
+        """
+        raise NotImplementedError("Refresh must be implemented")
+
+    async def before_request(self, request, method, url, headers):
+        """Performs credential-specific before request logic.
+
+        Refreshes the credentials if necessary, then calls :meth:`apply` to
+        apply the token to the authentication header.
+
+        Args:
+            request (google.auth.aio.transport.Request): The object used to make
+                HTTP requests.
+            method (str): The request's HTTP method or the RPC method being
+                invoked.
+            url (str): The request's URI or the RPC service's URI.
+            headers (Mapping): The request's headers.
+        """
+        await self.apply(headers)
+
+
+class StaticCredentials(Credentials):
+    """Asynchronous Credentials representing an immutable access token.
+
+    The credentials are considered immutable except the tokens which can be
+    configured in the constructor ::
+
+        credentials = StaticCredentials(token="token123")
+
+    StaticCredentials does not support :meth `refresh` and assumes that the configured
+    token is valid and not expired. StaticCredentials will never attempt to
+    refresh the token.
+    """
+
+    def __init__(self, token):
+        """
+        Args:
+            token (str): The access token.
+        """
+        super(StaticCredentials, self).__init__()
+        self.token = token
+
+    @_helpers.copy_docstring(Credentials)
+    async def refresh(self, request):
+        raise exceptions.InvalidOperation("Static credentials cannot be refreshed.")
+
+    # Note: before_request should never try to refresh access tokens.
+    # StaticCredentials intentionally does not support it.
+    @_helpers.copy_docstring(Credentials)
+    async def before_request(self, request, method, url, headers):
+        await self.apply(headers)
+
+
+class AnonymousCredentials(Credentials):
+    """Asynchronous Credentials that do not provide any authentication information.
+
+    These are useful in the case of services that support anonymous access or
+    local service emulators that do not use credentials.
+    """
+
+    async def refresh(self, request):
+        """Raises :class:``InvalidOperation``, anonymous credentials cannot be
+        refreshed."""
+        raise exceptions.InvalidOperation("Anonymous credentials cannot be refreshed.")
+
+    async def apply(self, headers, token=None):
+        """Anonymous credentials do nothing to the request.
+
+        The optional ``token`` argument is not supported.
+
+        Raises:
+            google.auth.exceptions.InvalidValue: If a token was specified.
+        """
+        if token is not None:
+            raise exceptions.InvalidValue("Anonymous credentials don't support tokens.")
+
+    async def before_request(self, request, method, url, headers):
+        """Anonymous credentials do nothing to the request."""
+        pass
diff --git a/.venv/lib/python3.12/site-packages/google/auth/aio/transport/__init__.py b/.venv/lib/python3.12/site-packages/google/auth/aio/transport/__init__.py
new file mode 100644
index 00000000..166a3be5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/aio/transport/__init__.py
@@ -0,0 +1,144 @@
+# Copyright 2024 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Transport - Asynchronous HTTP client library support.
+
+:mod:`google.auth.aio` is designed to work with various asynchronous client libraries such
+as aiohttp. In order to work across these libraries with different
+interfaces some abstraction is needed.
+
+This module provides two interfaces that are implemented by transport adapters
+to support HTTP libraries. :class:`Request` defines the interface expected by
+:mod:`google.auth` to make asynchronous requests. :class:`Response` defines the interface
+for the return value of :class:`Request`.
+"""
+
+import abc
+from typing import AsyncGenerator, Mapping, Optional
+
+import google.auth.transport
+
+
+_DEFAULT_TIMEOUT_SECONDS = 180
+
+DEFAULT_RETRYABLE_STATUS_CODES = google.auth.transport.DEFAULT_RETRYABLE_STATUS_CODES
+"""Sequence[int]:  HTTP status codes indicating a request can be retried.
+"""
+
+
+DEFAULT_MAX_RETRY_ATTEMPTS = 3
+"""int: How many times to retry a request."""
+
+
+class Response(metaclass=abc.ABCMeta):
+    """Asynchronous HTTP Response Interface."""
+
+    @property
+    @abc.abstractmethod
+    def status_code(self) -> int:
+        """
+        The HTTP response status code.
+
+        Returns:
+            int: The HTTP response status code.
+
+        """
+        raise NotImplementedError("status_code must be implemented.")
+
+    @property
+    @abc.abstractmethod
+    def headers(self) -> Mapping[str, str]:
+        """The HTTP response headers.
+
+        Returns:
+            Mapping[str, str]: The HTTP response headers.
+        """
+        raise NotImplementedError("headers must be implemented.")
+
+    @abc.abstractmethod
+    async def content(self, chunk_size: int) -> AsyncGenerator[bytes, None]:
+        """The raw response content.
+
+        Args:
+            chunk_size (int): The size of each chunk.
+
+        Yields:
+            AsyncGenerator[bytes, None]: An asynchronous generator yielding
+            response chunks as bytes.
+        """
+        raise NotImplementedError("content must be implemented.")
+
+    @abc.abstractmethod
+    async def read(self) -> bytes:
+        """Read the entire response content as bytes.
+
+        Returns:
+            bytes: The entire response content.
+        """
+        raise NotImplementedError("read must be implemented.")
+
+    @abc.abstractmethod
+    async def close(self):
+        """Close the response after it is fully consumed to resource."""
+        raise NotImplementedError("close must be implemented.")
+
+
+class Request(metaclass=abc.ABCMeta):
+    """Interface for a callable that makes HTTP requests.
+
+    Specific transport implementations should provide an implementation of
+    this that adapts their specific request / response API.
+
+    .. automethod:: __call__
+    """
+
+    @abc.abstractmethod
+    async def __call__(
+        self,
+        url: str,
+        method: str,
+        body: Optional[bytes],
+        headers: Optional[Mapping[str, str]],
+        timeout: float,
+        **kwargs
+    ) -> Response:
+        """Make an HTTP request.
+
+        Args:
+            url (str): The URI to be requested.
+            method (str): The HTTP method to use for the request. Defaults
+                to 'GET'.
+            body (Optional[bytes]): The payload / body in HTTP request.
+            headers (Mapping[str, str]): Request headers.
+            timeout (float): The number of seconds to wait for a
+                response from the server. If not specified or if None, the
+                transport-specific default timeout will be used.
+            kwargs: Additional arguments passed on to the transport's
+                request method.
+
+        Returns:
+            google.auth.aio.transport.Response: The HTTP response.
+
+        Raises:
+            google.auth.exceptions.TransportError: If any exception occurred.
+        """
+        # pylint: disable=redundant-returns-doc, missing-raises-doc
+        # (pylint doesn't play well with abstract docstrings.)
+        raise NotImplementedError("__call__ must be implemented.")
+
+    async def close(self) -> None:
+        """
+        Close the underlying session.
+        """
+        raise NotImplementedError("close must be implemented.")
diff --git a/.venv/lib/python3.12/site-packages/google/auth/aio/transport/aiohttp.py b/.venv/lib/python3.12/site-packages/google/auth/aio/transport/aiohttp.py
new file mode 100644
index 00000000..074d1491
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/aio/transport/aiohttp.py
@@ -0,0 +1,184 @@
+# Copyright 2024 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Transport adapter for Asynchronous HTTP Requests based on aiohttp.
+"""
+
+import asyncio
+from typing import AsyncGenerator, Mapping, Optional
+
+try:
+    import aiohttp  # type: ignore
+except ImportError as caught_exc:  # pragma: NO COVER
+    raise ImportError(
+        "The aiohttp library is not installed from please install the aiohttp package to use the aiohttp transport."
+    ) from caught_exc
+
+from google.auth import _helpers
+from google.auth import exceptions
+from google.auth.aio import transport
+
+
+class Response(transport.Response):
+    """
+    Represents an HTTP response and its data. It is returned by ``google.auth.aio.transport.sessions.AsyncAuthorizedSession``.
+
+    Args:
+        response (aiohttp.ClientResponse): An instance of aiohttp.ClientResponse.
+
+    Attributes:
+        status_code (int): The HTTP status code of the response.
+        headers (Mapping[str, str]): The HTTP headers of the response.
+    """
+
+    def __init__(self, response: aiohttp.ClientResponse):
+        self._response = response
+
+    @property
+    @_helpers.copy_docstring(transport.Response)
+    def status_code(self) -> int:
+        return self._response.status
+
+    @property
+    @_helpers.copy_docstring(transport.Response)
+    def headers(self) -> Mapping[str, str]:
+        return {key: value for key, value in self._response.headers.items()}
+
+    @_helpers.copy_docstring(transport.Response)
+    async def content(self, chunk_size: int = 1024) -> AsyncGenerator[bytes, None]:
+        try:
+            async for chunk in self._response.content.iter_chunked(
+                chunk_size
+            ):  # pragma: no branch
+                yield chunk
+        except aiohttp.ClientPayloadError as exc:
+            raise exceptions.ResponseError(
+                "Failed to read from the payload stream."
+            ) from exc
+
+    @_helpers.copy_docstring(transport.Response)
+    async def read(self) -> bytes:
+        try:
+            return await self._response.read()
+        except aiohttp.ClientResponseError as exc:
+            raise exceptions.ResponseError("Failed to read the response body.") from exc
+
+    @_helpers.copy_docstring(transport.Response)
+    async def close(self):
+        self._response.close()
+
+
+class Request(transport.Request):
+    """Asynchronous Requests request adapter.
+
+    This class is used internally for making requests using aiohttp
+    in a consistent way. If you use :class:`google.auth.aio.transport.sessions.AsyncAuthorizedSession`
+    you do not need to construct or use this class directly.
+
+    This class can be useful if you want to configure a Request callable
+    with a custom ``aiohttp.ClientSession`` in :class:`AuthorizedSession` or if
+    you want to manually refresh a :class:`~google.auth.aio.credentials.Credentials` instance::
+
+        import aiohttp
+        import google.auth.aio.transport.aiohttp
+
+        # Default example:
+        request = google.auth.aio.transport.aiohttp.Request()
+        await credentials.refresh(request)
+
+        # Custom aiohttp Session Example:
+        session = session=aiohttp.ClientSession(auto_decompress=False)
+        request = google.auth.aio.transport.aiohttp.Request(session=session)
+        auth_sesion = google.auth.aio.transport.sessions.AsyncAuthorizedSession(auth_request=request)
+
+    Args:
+        session (aiohttp.ClientSession): An instance :class:`aiohttp.ClientSession` used
+            to make HTTP requests. If not specified, a session will be created.
+
+    .. automethod:: __call__
+    """
+
+    def __init__(self, session: aiohttp.ClientSession = None):
+        self._session = session
+        self._closed = False
+
+    async def __call__(
+        self,
+        url: str,
+        method: str = "GET",
+        body: Optional[bytes] = None,
+        headers: Optional[Mapping[str, str]] = None,
+        timeout: float = transport._DEFAULT_TIMEOUT_SECONDS,
+        **kwargs,
+    ) -> transport.Response:
+        """
+        Make an HTTP request using aiohttp.
+
+        Args:
+            url (str): The URL to be requested.
+            method (Optional[str]):
+                The HTTP method to use for the request. Defaults to 'GET'.
+            body (Optional[bytes]):
+                The payload or body in HTTP request.
+            headers (Optional[Mapping[str, str]]):
+                Request headers.
+            timeout (float): The number of seconds to wait for a
+                response from the server. If not specified or if None, the
+                requests default timeout will be used.
+            kwargs: Additional arguments passed through to the underlying
+                aiohttp :meth:`aiohttp.Session.request` method.
+
+        Returns:
+            google.auth.aio.transport.Response: The HTTP response.
+
+        Raises:
+            - google.auth.exceptions.TransportError: If the request fails or if the session is closed.
+            - google.auth.exceptions.TimeoutError: If the request times out.
+        """
+
+        try:
+            if self._closed:
+                raise exceptions.TransportError("session is closed.")
+
+            if not self._session:
+                self._session = aiohttp.ClientSession()
+
+            client_timeout = aiohttp.ClientTimeout(total=timeout)
+            response = await self._session.request(
+                method,
+                url,
+                data=body,
+                headers=headers,
+                timeout=client_timeout,
+                **kwargs,
+            )
+            return Response(response)
+
+        except aiohttp.ClientError as caught_exc:
+            client_exc = exceptions.TransportError(f"Failed to send request to {url}.")
+            raise client_exc from caught_exc
+
+        except asyncio.TimeoutError as caught_exc:
+            timeout_exc = exceptions.TimeoutError(
+                f"Request timed out after {timeout} seconds."
+            )
+            raise timeout_exc from caught_exc
+
+    async def close(self) -> None:
+        """
+        Close the underlying aiohttp session to release the acquired resources.
+        """
+        if not self._closed and self._session:
+            await self._session.close()
+        self._closed = True
diff --git a/.venv/lib/python3.12/site-packages/google/auth/aio/transport/sessions.py b/.venv/lib/python3.12/site-packages/google/auth/aio/transport/sessions.py
new file mode 100644
index 00000000..fea7cbbb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/aio/transport/sessions.py
@@ -0,0 +1,268 @@
+# Copyright 2024 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import asyncio
+from contextlib import asynccontextmanager
+import functools
+import time
+from typing import Mapping, Optional
+
+from google.auth import _exponential_backoff, exceptions
+from google.auth.aio import transport
+from google.auth.aio.credentials import Credentials
+from google.auth.exceptions import TimeoutError
+
+try:
+    from google.auth.aio.transport.aiohttp import Request as AiohttpRequest
+
+    AIOHTTP_INSTALLED = True
+except ImportError:  # pragma: NO COVER
+    AIOHTTP_INSTALLED = False
+
+
+@asynccontextmanager
+async def timeout_guard(timeout):
+    """
+    timeout_guard is an asynchronous context manager to apply a timeout to an asynchronous block of code.
+
+    Args:
+        timeout (float): The time in seconds before the context manager times out.
+
+    Raises:
+        google.auth.exceptions.TimeoutError: If the code within the context exceeds the provided timeout.
+
+    Usage:
+        async with timeout_guard(10) as with_timeout:
+            await with_timeout(async_function())
+    """
+    start = time.monotonic()
+    total_timeout = timeout
+
+    def _remaining_time():
+        elapsed = time.monotonic() - start
+        remaining = total_timeout - elapsed
+        if remaining <= 0:
+            raise TimeoutError(
+                f"Context manager exceeded the configured timeout of {total_timeout}s."
+            )
+        return remaining
+
+    async def with_timeout(coro):
+        try:
+            remaining = _remaining_time()
+            response = await asyncio.wait_for(coro, remaining)
+            return response
+        except (asyncio.TimeoutError, TimeoutError) as e:
+            raise TimeoutError(
+                f"The operation {coro} exceeded the configured timeout of {total_timeout}s."
+            ) from e
+
+    try:
+        yield with_timeout
+
+    finally:
+        _remaining_time()
+
+
+class AsyncAuthorizedSession:
+    """This is an asynchronous implementation of :class:`google.auth.requests.AuthorizedSession` class.
+    We utilize an instance of a class that implements :class:`google.auth.aio.transport.Request` configured
+    by the caller or otherwise default to `google.auth.aio.transport.aiohttp.Request` if the external aiohttp
+    package is installed.
+
+    A Requests Session class with credentials.
+
+    This class is used to perform asynchronous requests to API endpoints that require
+    authorization::
+
+        import aiohttp
+        from google.auth.aio.transport import sessions
+
+        async with sessions.AsyncAuthorizedSession(credentials) as authed_session:
+            response = await authed_session.request(
+                'GET', 'https://www.googleapis.com/storage/v1/b')
+
+    The underlying :meth:`request` implementation handles adding the
+    credentials' headers to the request and refreshing credentials as needed.
+
+    Args:
+        credentials (google.auth.aio.credentials.Credentials):
+            The credentials to add to the request.
+        auth_request (Optional[google.auth.aio.transport.Request]):
+            An instance of a class that implements
+            :class:`~google.auth.aio.transport.Request` used to make requests
+            and refresh credentials. If not passed,
+            an instance of :class:`~google.auth.aio.transport.aiohttp.Request`
+            is created.
+
+    Raises:
+        - google.auth.exceptions.TransportError: If `auth_request` is `None`
+            and the external package `aiohttp` is not installed.
+        - google.auth.exceptions.InvalidType: If the provided credentials are
+            not of type `google.auth.aio.credentials.Credentials`.
+    """
+
+    def __init__(
+        self, credentials: Credentials, auth_request: Optional[transport.Request] = None
+    ):
+        if not isinstance(credentials, Credentials):
+            raise exceptions.InvalidType(
+                f"The configured credentials of type {type(credentials)} are invalid and must be of type `google.auth.aio.credentials.Credentials`"
+            )
+        self._credentials = credentials
+        _auth_request = auth_request
+        if not _auth_request and AIOHTTP_INSTALLED:
+            _auth_request = AiohttpRequest()
+        if _auth_request is None:
+            raise exceptions.TransportError(
+                "`auth_request` must either be configured or the external package `aiohttp` must be installed to use the default value."
+            )
+        self._auth_request = _auth_request
+
+    async def request(
+        self,
+        method: str,
+        url: str,
+        data: Optional[bytes] = None,
+        headers: Optional[Mapping[str, str]] = None,
+        max_allowed_time: float = transport._DEFAULT_TIMEOUT_SECONDS,
+        timeout: float = transport._DEFAULT_TIMEOUT_SECONDS,
+        **kwargs,
+    ) -> transport.Response:
+        """
+        Args:
+                method (str): The http method used to make the request.
+                url (str): The URI to be requested.
+                data (Optional[bytes]): The payload or body in HTTP request.
+                headers (Optional[Mapping[str, str]]): Request headers.
+                timeout (float):
+                The amount of time in seconds to wait for the server response
+                with each individual request.
+                max_allowed_time (float):
+                If the method runs longer than this, a ``Timeout`` exception is
+                automatically raised. Unlike the ``timeout`` parameter, this
+                value applies to the total method execution time, even if
+                multiple requests are made under the hood.
+
+                Mind that it is not guaranteed that the timeout error is raised
+                at ``max_allowed_time``. It might take longer, for example, if
+                an underlying request takes a lot of time, but the request
+                itself does not timeout, e.g. if a large file is being
+                transmitted. The timout error will be raised after such
+                request completes.
+
+        Returns:
+                google.auth.aio.transport.Response: The HTTP response.
+
+        Raises:
+                google.auth.exceptions.TimeoutError: If the method does not complete within
+                the configured `max_allowed_time` or the request exceeds the configured
+                `timeout`.
+        """
+
+        retries = _exponential_backoff.AsyncExponentialBackoff(
+            total_attempts=transport.DEFAULT_MAX_RETRY_ATTEMPTS
+        )
+        async with timeout_guard(max_allowed_time) as with_timeout:
+            await with_timeout(
+                # Note: before_request will attempt to refresh credentials if expired.
+                self._credentials.before_request(
+                    self._auth_request, method, url, headers
+                )
+            )
+            # Workaround issue in python 3.9 related to code coverage by adding `# pragma: no branch`
+            # See https://github.com/googleapis/gapic-generator-python/pull/1174#issuecomment-1025132372
+            async for _ in retries:  # pragma: no branch
+                response = await with_timeout(
+                    self._auth_request(url, method, data, headers, timeout, **kwargs)
+                )
+                if response.status_code not in transport.DEFAULT_RETRYABLE_STATUS_CODES:
+                    break
+        return response
+
+    @functools.wraps(request)
+    async def get(
+        self,
+        url: str,
+        data: Optional[bytes] = None,
+        headers: Optional[Mapping[str, str]] = None,
+        max_allowed_time: float = transport._DEFAULT_TIMEOUT_SECONDS,
+        timeout: float = transport._DEFAULT_TIMEOUT_SECONDS,
+        **kwargs,
+    ) -> transport.Response:
+        return await self.request(
+            "GET", url, data, headers, max_allowed_time, timeout, **kwargs
+        )
+
+    @functools.wraps(request)
+    async def post(
+        self,
+        url: str,
+        data: Optional[bytes] = None,
+        headers: Optional[Mapping[str, str]] = None,
+        max_allowed_time: float = transport._DEFAULT_TIMEOUT_SECONDS,
+        timeout: float = transport._DEFAULT_TIMEOUT_SECONDS,
+        **kwargs,
+    ) -> transport.Response:
+        return await self.request(
+            "POST", url, data, headers, max_allowed_time, timeout, **kwargs
+        )
+
+    @functools.wraps(request)
+    async def put(
+        self,
+        url: str,
+        data: Optional[bytes] = None,
+        headers: Optional[Mapping[str, str]] = None,
+        max_allowed_time: float = transport._DEFAULT_TIMEOUT_SECONDS,
+        timeout: float = transport._DEFAULT_TIMEOUT_SECONDS,
+        **kwargs,
+    ) -> transport.Response:
+        return await self.request(
+            "PUT", url, data, headers, max_allowed_time, timeout, **kwargs
+        )
+
+    @functools.wraps(request)
+    async def patch(
+        self,
+        url: str,
+        data: Optional[bytes] = None,
+        headers: Optional[Mapping[str, str]] = None,
+        max_allowed_time: float = transport._DEFAULT_TIMEOUT_SECONDS,
+        timeout: float = transport._DEFAULT_TIMEOUT_SECONDS,
+        **kwargs,
+    ) -> transport.Response:
+        return await self.request(
+            "PATCH", url, data, headers, max_allowed_time, timeout, **kwargs
+        )
+
+    @functools.wraps(request)
+    async def delete(
+        self,
+        url: str,
+        data: Optional[bytes] = None,
+        headers: Optional[Mapping[str, str]] = None,
+        max_allowed_time: float = transport._DEFAULT_TIMEOUT_SECONDS,
+        timeout: float = transport._DEFAULT_TIMEOUT_SECONDS,
+        **kwargs,
+    ) -> transport.Response:
+        return await self.request(
+            "DELETE", url, data, headers, max_allowed_time, timeout, **kwargs
+        )
+
+    async def close(self) -> None:
+        """
+        Close the underlying auth request session.
+        """
+        await self._auth_request.close()
diff --git a/.venv/lib/python3.12/site-packages/google/auth/api_key.py b/.venv/lib/python3.12/site-packages/google/auth/api_key.py
new file mode 100644
index 00000000..4fdf7f27
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/api_key.py
@@ -0,0 +1,76 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Google API key support.
+This module provides authentication using the `API key`_.
+.. _API key:
+    https://cloud.google.com/docs/authentication/api-keys/
+"""
+
+from google.auth import _helpers
+from google.auth import credentials
+from google.auth import exceptions
+
+
+class Credentials(credentials.Credentials):
+    """API key credentials.
+    These credentials use API key to provide authorization to applications.
+    """
+
+    def __init__(self, token):
+        """
+        Args:
+            token (str): API key string
+        Raises:
+            ValueError: If the provided API key is not a non-empty string.
+        """
+        super(Credentials, self).__init__()
+        if not token:
+            raise exceptions.InvalidValue("Token must be a non-empty API key string")
+        self.token = token
+
+    @property
+    def expired(self):
+        return False
+
+    @property
+    def valid(self):
+        return True
+
+    @_helpers.copy_docstring(credentials.Credentials)
+    def refresh(self, request):
+        return
+
+    def apply(self, headers, token=None):
+        """Apply the API key token to the x-goog-api-key header.
+        Args:
+            headers (Mapping): The HTTP request headers.
+            token (Optional[str]): If specified, overrides the current access
+                token.
+        """
+        headers["x-goog-api-key"] = token or self.token
+
+    def before_request(self, request, method, url, headers):
+        """Performs credential-specific before request logic.
+        Refreshes the credentials if necessary, then calls :meth:`apply` to
+        apply the token to the x-goog-api-key header.
+        Args:
+            request (google.auth.transport.Request): The object used to make
+                HTTP requests.
+            method (str): The request's HTTP method or the RPC method being
+                invoked.
+            url (str): The request's URI or the RPC service's URI.
+            headers (Mapping): The request's headers.
+        """
+        self.apply(headers)
diff --git a/.venv/lib/python3.12/site-packages/google/auth/app_engine.py b/.venv/lib/python3.12/site-packages/google/auth/app_engine.py
new file mode 100644
index 00000000..7083ee61
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/app_engine.py
@@ -0,0 +1,180 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Google App Engine standard environment support.
+
+This module provides authentication and signing for applications running on App
+Engine in the standard environment using the `App Identity API`_.
+
+
+.. _App Identity API:
+    https://cloud.google.com/appengine/docs/python/appidentity/
+"""
+
+import datetime
+
+from google.auth import _helpers
+from google.auth import credentials
+from google.auth import crypt
+from google.auth import exceptions
+
+# pytype: disable=import-error
+try:
+    from google.appengine.api import app_identity  # type: ignore
+except ImportError:
+    app_identity = None  # type: ignore
+# pytype: enable=import-error
+
+
+class Signer(crypt.Signer):
+    """Signs messages using the App Engine App Identity service.
+
+    This can be used in place of :class:`google.auth.crypt.Signer` when
+    running in the App Engine standard environment.
+    """
+
+    @property
+    def key_id(self):
+        """Optional[str]: The key ID used to identify this private key.
+
+        .. warning::
+           This is always ``None``. The key ID used by App Engine can not
+           be reliably determined ahead of time.
+        """
+        return None
+
+    @_helpers.copy_docstring(crypt.Signer)
+    def sign(self, message):
+        message = _helpers.to_bytes(message)
+        _, signature = app_identity.sign_blob(message)
+        return signature
+
+
+def get_project_id():
+    """Gets the project ID for the current App Engine application.
+
+    Returns:
+        str: The project ID
+
+    Raises:
+        google.auth.exceptions.OSError: If the App Engine APIs are unavailable.
+    """
+    # pylint: disable=missing-raises-doc
+    # Pylint rightfully thinks google.auth.exceptions.OSError is OSError, but doesn't
+    # realize it's a valid alias.
+    if app_identity is None:
+        raise exceptions.OSError("The App Engine APIs are not available.")
+    return app_identity.get_application_id()
+
+
+class Credentials(
+    credentials.Scoped, credentials.Signing, credentials.CredentialsWithQuotaProject
+):
+    """App Engine standard environment credentials.
+
+    These credentials use the App Engine App Identity API to obtain access
+    tokens.
+    """
+
+    def __init__(
+        self,
+        scopes=None,
+        default_scopes=None,
+        service_account_id=None,
+        quota_project_id=None,
+    ):
+        """
+        Args:
+            scopes (Sequence[str]): Scopes to request from the App Identity
+                API.
+            default_scopes (Sequence[str]): Default scopes passed by a
+                Google client library. Use 'scopes' for user-defined scopes.
+            service_account_id (str): The service account ID passed into
+                :func:`google.appengine.api.app_identity.get_access_token`.
+                If not specified, the default application service account
+                ID will be used.
+            quota_project_id (Optional[str]): The project ID used for quota
+                and billing.
+
+        Raises:
+            google.auth.exceptions.OSError: If the App Engine APIs are unavailable.
+        """
+        # pylint: disable=missing-raises-doc
+        # Pylint rightfully thinks google.auth.exceptions.OSError is OSError, but doesn't
+        # realize it's a valid alias.
+        if app_identity is None:
+            raise exceptions.OSError("The App Engine APIs are not available.")
+
+        super(Credentials, self).__init__()
+        self._scopes = scopes
+        self._default_scopes = default_scopes
+        self._service_account_id = service_account_id
+        self._signer = Signer()
+        self._quota_project_id = quota_project_id
+
+    @_helpers.copy_docstring(credentials.Credentials)
+    def refresh(self, request):
+        scopes = self._scopes if self._scopes is not None else self._default_scopes
+        # pylint: disable=unused-argument
+        token, ttl = app_identity.get_access_token(scopes, self._service_account_id)
+        expiry = datetime.datetime.utcfromtimestamp(ttl)
+
+        self.token, self.expiry = token, expiry
+
+    @property
+    def service_account_email(self):
+        """The service account email."""
+        if self._service_account_id is None:
+            self._service_account_id = app_identity.get_service_account_name()
+        return self._service_account_id
+
+    @property
+    def requires_scopes(self):
+        """Checks if the credentials requires scopes.
+
+        Returns:
+            bool: True if there are no scopes set otherwise False.
+        """
+        return not self._scopes and not self._default_scopes
+
+    @_helpers.copy_docstring(credentials.Scoped)
+    def with_scopes(self, scopes, default_scopes=None):
+        return self.__class__(
+            scopes=scopes,
+            default_scopes=default_scopes,
+            service_account_id=self._service_account_id,
+            quota_project_id=self.quota_project_id,
+        )
+
+    @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
+    def with_quota_project(self, quota_project_id):
+        return self.__class__(
+            scopes=self._scopes,
+            service_account_id=self._service_account_id,
+            quota_project_id=quota_project_id,
+        )
+
+    @_helpers.copy_docstring(credentials.Signing)
+    def sign_bytes(self, message):
+        return self._signer.sign(message)
+
+    @property  # type: ignore
+    @_helpers.copy_docstring(credentials.Signing)
+    def signer_email(self):
+        return self.service_account_email
+
+    @property  # type: ignore
+    @_helpers.copy_docstring(credentials.Signing)
+    def signer(self):
+        return self._signer
diff --git a/.venv/lib/python3.12/site-packages/google/auth/aws.py b/.venv/lib/python3.12/site-packages/google/auth/aws.py
new file mode 100644
index 00000000..28c065d3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/aws.py
@@ -0,0 +1,861 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""AWS Credentials and AWS Signature V4 Request Signer.
+
+This module provides credentials to access Google Cloud resources from Amazon
+Web Services (AWS) workloads. These credentials are recommended over the
+use of service account credentials in AWS as they do not involve the management
+of long-live service account private keys.
+
+AWS Credentials are initialized using external_account arguments which are
+typically loaded from the external credentials JSON file.
+
+This module also provides a definition for an abstract AWS security credentials supplier.
+This supplier can be implemented to return valid AWS security credentials and an AWS region
+and used to create AWS credentials. The credentials will then call the
+supplier instead of using pre-defined methods such as calling the EC2 metadata endpoints.
+
+This module also provides a basic implementation of the
+`AWS Signature Version 4`_ request signing algorithm.
+
+AWS Credentials use serialized signed requests to the
+`AWS STS GetCallerIdentity`_ API that can be exchanged for Google access tokens
+via the GCP STS endpoint.
+
+.. _AWS Signature Version 4: https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
+.. _AWS STS GetCallerIdentity: https://docs.aws.amazon.com/STS/latest/APIReference/API_GetCallerIdentity.html
+"""
+
+import abc
+from dataclasses import dataclass
+import hashlib
+import hmac
+import http.client as http_client
+import json
+import os
+import posixpath
+import re
+from typing import Optional
+import urllib
+from urllib.parse import urljoin
+
+from google.auth import _helpers
+from google.auth import environment_vars
+from google.auth import exceptions
+from google.auth import external_account
+
+# AWS Signature Version 4 signing algorithm identifier.
+_AWS_ALGORITHM = "AWS4-HMAC-SHA256"
+# The termination string for the AWS credential scope value as defined in
+# https://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
+_AWS_REQUEST_TYPE = "aws4_request"
+# The AWS authorization header name for the security session token if available.
+_AWS_SECURITY_TOKEN_HEADER = "x-amz-security-token"
+# The AWS authorization header name for the auto-generated date.
+_AWS_DATE_HEADER = "x-amz-date"
+# The default AWS regional credential verification URL.
+_DEFAULT_AWS_REGIONAL_CREDENTIAL_VERIFICATION_URL = (
+    "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15"
+)
+# IMDSV2 session token lifetime. This is set to a low value because the session token is used immediately.
+_IMDSV2_SESSION_TOKEN_TTL_SECONDS = "300"
+
+
+class RequestSigner(object):
+    """Implements an AWS request signer based on the AWS Signature Version 4 signing
+    process.
+    https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
+    """
+
+    def __init__(self, region_name):
+        """Instantiates an AWS request signer used to compute authenticated signed
+        requests to AWS APIs based on the AWS Signature Version 4 signing process.
+
+        Args:
+            region_name (str): The AWS region to use.
+        """
+
+        self._region_name = region_name
+
+    def get_request_options(
+        self,
+        aws_security_credentials,
+        url,
+        method,
+        request_payload="",
+        additional_headers={},
+    ):
+        """Generates the signed request for the provided HTTP request for calling
+        an AWS API. This follows the steps described at:
+        https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
+
+        Args:
+            aws_security_credentials (AWSSecurityCredentials): The AWS security credentials.
+            url (str): The AWS service URL containing the canonical URI and
+                query string.
+            method (str): The HTTP method used to call this API.
+            request_payload (Optional[str]): The optional request payload if
+                available.
+            additional_headers (Optional[Mapping[str, str]]): The optional
+                additional headers needed for the requested AWS API.
+
+        Returns:
+            Mapping[str, str]: The AWS signed request dictionary object.
+        """
+
+        additional_headers = additional_headers or {}
+
+        uri = urllib.parse.urlparse(url)
+        # Normalize the URL path. This is needed for the canonical_uri.
+        # os.path.normpath can't be used since it normalizes "/" paths
+        # to "\\" in Windows OS.
+        normalized_uri = urllib.parse.urlparse(
+            urljoin(url, posixpath.normpath(uri.path))
+        )
+        # Validate provided URL.
+        if not uri.hostname or uri.scheme != "https":
+            raise exceptions.InvalidResource("Invalid AWS service URL")
+
+        header_map = _generate_authentication_header_map(
+            host=uri.hostname,
+            canonical_uri=normalized_uri.path or "/",
+            canonical_querystring=_get_canonical_querystring(uri.query),
+            method=method,
+            region=self._region_name,
+            aws_security_credentials=aws_security_credentials,
+            request_payload=request_payload,
+            additional_headers=additional_headers,
+        )
+        headers = {
+            "Authorization": header_map.get("authorization_header"),
+            "host": uri.hostname,
+        }
+        # Add x-amz-date if available.
+        if "amz_date" in header_map:
+            headers[_AWS_DATE_HEADER] = header_map.get("amz_date")
+        # Append additional optional headers, eg. X-Amz-Target, Content-Type, etc.
+        for key in additional_headers:
+            headers[key] = additional_headers[key]
+
+        # Add session token if available.
+        if aws_security_credentials.session_token is not None:
+            headers[_AWS_SECURITY_TOKEN_HEADER] = aws_security_credentials.session_token
+
+        signed_request = {"url": url, "method": method, "headers": headers}
+        if request_payload:
+            signed_request["data"] = request_payload
+        return signed_request
+
+
+def _get_canonical_querystring(query):
+    """Generates the canonical query string given a raw query string.
+    Logic is based on
+    https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
+
+    Args:
+        query (str): The raw query string.
+
+    Returns:
+        str: The canonical query string.
+    """
+    # Parse raw query string.
+    querystring = urllib.parse.parse_qs(query)
+    querystring_encoded_map = {}
+    for key in querystring:
+        quote_key = urllib.parse.quote(key, safe="-_.~")
+        # URI encode key.
+        querystring_encoded_map[quote_key] = []
+        for item in querystring[key]:
+            # For each key, URI encode all values for that key.
+            querystring_encoded_map[quote_key].append(
+                urllib.parse.quote(item, safe="-_.~")
+            )
+        # Sort values for each key.
+        querystring_encoded_map[quote_key].sort()
+    # Sort keys.
+    sorted_keys = list(querystring_encoded_map.keys())
+    sorted_keys.sort()
+    # Reconstruct the query string. Preserve keys with multiple values.
+    querystring_encoded_pairs = []
+    for key in sorted_keys:
+        for item in querystring_encoded_map[key]:
+            querystring_encoded_pairs.append("{}={}".format(key, item))
+    return "&".join(querystring_encoded_pairs)
+
+
+def _sign(key, msg):
+    """Creates the HMAC-SHA256 hash of the provided message using the provided
+    key.
+
+    Args:
+        key (str): The HMAC-SHA256 key to use.
+        msg (str): The message to hash.
+
+    Returns:
+        str: The computed hash bytes.
+    """
+    return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()
+
+
+def _get_signing_key(key, date_stamp, region_name, service_name):
+    """Calculates the signing key used to calculate the signature for
+    AWS Signature Version 4 based on:
+    https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
+
+    Args:
+        key (str): The AWS secret access key.
+        date_stamp (str): The '%Y%m%d' date format.
+        region_name (str): The AWS region.
+        service_name (str): The AWS service name, eg. sts.
+
+    Returns:
+        str: The signing key bytes.
+    """
+    k_date = _sign(("AWS4" + key).encode("utf-8"), date_stamp)
+    k_region = _sign(k_date, region_name)
+    k_service = _sign(k_region, service_name)
+    k_signing = _sign(k_service, "aws4_request")
+    return k_signing
+
+
+def _generate_authentication_header_map(
+    host,
+    canonical_uri,
+    canonical_querystring,
+    method,
+    region,
+    aws_security_credentials,
+    request_payload="",
+    additional_headers={},
+):
+    """Generates the authentication header map needed for generating the AWS
+    Signature Version 4 signed request.
+
+    Args:
+        host (str): The AWS service URL hostname.
+        canonical_uri (str): The AWS service URL path name.
+        canonical_querystring (str): The AWS service URL query string.
+        method (str): The HTTP method used to call this API.
+        region (str): The AWS region.
+        aws_security_credentials (AWSSecurityCredentials): The AWS security credentials.
+        request_payload (Optional[str]): The optional request payload if
+            available.
+        additional_headers (Optional[Mapping[str, str]]): The optional
+            additional headers needed for the requested AWS API.
+
+    Returns:
+        Mapping[str, str]: The AWS authentication header dictionary object.
+            This contains the x-amz-date and authorization header information.
+    """
+    # iam.amazonaws.com host => iam service.
+    # sts.us-east-2.amazonaws.com host => sts service.
+    service_name = host.split(".")[0]
+
+    current_time = _helpers.utcnow()
+    amz_date = current_time.strftime("%Y%m%dT%H%M%SZ")
+    date_stamp = current_time.strftime("%Y%m%d")
+
+    # Change all additional headers to be lower case.
+    full_headers = {}
+    for key in additional_headers:
+        full_headers[key.lower()] = additional_headers[key]
+    # Add AWS session token if available.
+    if aws_security_credentials.session_token is not None:
+        full_headers[
+            _AWS_SECURITY_TOKEN_HEADER
+        ] = aws_security_credentials.session_token
+
+    # Required headers
+    full_headers["host"] = host
+    # Do not use generated x-amz-date if the date header is provided.
+    # Previously the date was not fixed with x-amz- and could be provided
+    # manually.
+    # https://github.com/boto/botocore/blob/879f8440a4e9ace5d3cf145ce8b3d5e5ffb892ef/tests/unit/auth/aws4_testsuite/get-header-value-trim.req
+    if "date" not in full_headers:
+        full_headers[_AWS_DATE_HEADER] = amz_date
+
+    # Header keys need to be sorted alphabetically.
+    canonical_headers = ""
+    header_keys = list(full_headers.keys())
+    header_keys.sort()
+    for key in header_keys:
+        canonical_headers = "{}{}:{}\n".format(
+            canonical_headers, key, full_headers[key]
+        )
+    signed_headers = ";".join(header_keys)
+
+    payload_hash = hashlib.sha256((request_payload or "").encode("utf-8")).hexdigest()
+
+    # https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
+    canonical_request = "{}\n{}\n{}\n{}\n{}\n{}".format(
+        method,
+        canonical_uri,
+        canonical_querystring,
+        canonical_headers,
+        signed_headers,
+        payload_hash,
+    )
+
+    credential_scope = "{}/{}/{}/{}".format(
+        date_stamp, region, service_name, _AWS_REQUEST_TYPE
+    )
+
+    # https://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
+    string_to_sign = "{}\n{}\n{}\n{}".format(
+        _AWS_ALGORITHM,
+        amz_date,
+        credential_scope,
+        hashlib.sha256(canonical_request.encode("utf-8")).hexdigest(),
+    )
+
+    # https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
+    signing_key = _get_signing_key(
+        aws_security_credentials.secret_access_key, date_stamp, region, service_name
+    )
+    signature = hmac.new(
+        signing_key, string_to_sign.encode("utf-8"), hashlib.sha256
+    ).hexdigest()
+
+    # https://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html
+    authorization_header = "{} Credential={}/{}, SignedHeaders={}, Signature={}".format(
+        _AWS_ALGORITHM,
+        aws_security_credentials.access_key_id,
+        credential_scope,
+        signed_headers,
+        signature,
+    )
+
+    authentication_header = {"authorization_header": authorization_header}
+    # Do not use generated x-amz-date if the date header is provided.
+    if "date" not in full_headers:
+        authentication_header["amz_date"] = amz_date
+    return authentication_header
+
+
+@dataclass
+class AwsSecurityCredentials:
+    """A class that models AWS security credentials with an optional session token.
+
+        Attributes:
+            access_key_id (str): The AWS security credentials access key id.
+            secret_access_key (str): The AWS security credentials secret access key.
+            session_token (Optional[str]): The optional AWS security credentials session token. This should be set when using temporary credentials.
+    """
+
+    access_key_id: str
+    secret_access_key: str
+    session_token: Optional[str] = None
+
+
+class AwsSecurityCredentialsSupplier(metaclass=abc.ABCMeta):
+    """Base class for AWS security credential suppliers. This can be implemented with custom logic to retrieve
+    AWS security credentials to exchange for a Google Cloud access token. The AWS external account credential does
+    not cache the AWS security credentials, so caching logic should be added in the implementation.
+    """
+
+    @abc.abstractmethod
+    def get_aws_security_credentials(self, context, request):
+        """Returns the AWS security credentials for the requested context.
+
+        .. warning: This is not cached by the calling Google credential, so caching logic should be implemented in the supplier.
+
+        Args:
+            context (google.auth.externalaccount.SupplierContext): The context object
+                containing information about the requested audience and subject token type.
+            request (google.auth.transport.Request): The object used to make
+                HTTP requests.
+
+        Raises:
+            google.auth.exceptions.RefreshError: If an error is encountered during
+                security credential retrieval logic.
+
+        Returns:
+            AwsSecurityCredentials: The requested AWS security credentials.
+        """
+        raise NotImplementedError("")
+
+    @abc.abstractmethod
+    def get_aws_region(self, context, request):
+        """Returns the AWS region for the requested context.
+
+        Args:
+            context (google.auth.externalaccount.SupplierContext): The context object
+                containing information about the requested audience and subject token type.
+            request (google.auth.transport.Request): The object used to make
+                HTTP requests.
+
+        Raises:
+            google.auth.exceptions.RefreshError: If an error is encountered during
+                region retrieval logic.
+
+        Returns:
+            str: The AWS region.
+        """
+        raise NotImplementedError("")
+
+
+class _DefaultAwsSecurityCredentialsSupplier(AwsSecurityCredentialsSupplier):
+    """Default implementation of AWS security credentials supplier. Supports retrieving
+    credentials and region via EC2 metadata endpoints and environment variables.
+    """
+
+    def __init__(self, credential_source):
+        self._region_url = credential_source.get("region_url")
+        self._security_credentials_url = credential_source.get("url")
+        self._imdsv2_session_token_url = credential_source.get(
+            "imdsv2_session_token_url"
+        )
+
+    @_helpers.copy_docstring(AwsSecurityCredentialsSupplier)
+    def get_aws_security_credentials(self, context, request):
+
+        # Check environment variables for permanent credentials first.
+        # https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html
+        env_aws_access_key_id = os.environ.get(environment_vars.AWS_ACCESS_KEY_ID)
+        env_aws_secret_access_key = os.environ.get(
+            environment_vars.AWS_SECRET_ACCESS_KEY
+        )
+        # This is normally not available for permanent credentials.
+        env_aws_session_token = os.environ.get(environment_vars.AWS_SESSION_TOKEN)
+        if env_aws_access_key_id and env_aws_secret_access_key:
+            return AwsSecurityCredentials(
+                env_aws_access_key_id, env_aws_secret_access_key, env_aws_session_token
+            )
+
+        imdsv2_session_token = self._get_imdsv2_session_token(request)
+        role_name = self._get_metadata_role_name(request, imdsv2_session_token)
+
+        # Get security credentials.
+        credentials = self._get_metadata_security_credentials(
+            request, role_name, imdsv2_session_token
+        )
+
+        return AwsSecurityCredentials(
+            credentials.get("AccessKeyId"),
+            credentials.get("SecretAccessKey"),
+            credentials.get("Token"),
+        )
+
+    @_helpers.copy_docstring(AwsSecurityCredentialsSupplier)
+    def get_aws_region(self, context, request):
+        # The AWS metadata server is not available in some AWS environments
+        # such as AWS lambda. Instead, it is available via environment
+        # variable.
+        env_aws_region = os.environ.get(environment_vars.AWS_REGION)
+        if env_aws_region is not None:
+            return env_aws_region
+
+        env_aws_region = os.environ.get(environment_vars.AWS_DEFAULT_REGION)
+        if env_aws_region is not None:
+            return env_aws_region
+
+        if not self._region_url:
+            raise exceptions.RefreshError("Unable to determine AWS region")
+
+        headers = None
+        imdsv2_session_token = self._get_imdsv2_session_token(request)
+        if imdsv2_session_token is not None:
+            headers = {"X-aws-ec2-metadata-token": imdsv2_session_token}
+
+        response = request(url=self._region_url, method="GET", headers=headers)
+
+        # Support both string and bytes type response.data.
+        response_body = (
+            response.data.decode("utf-8")
+            if hasattr(response.data, "decode")
+            else response.data
+        )
+
+        if response.status != http_client.OK:
+            raise exceptions.RefreshError(
+                "Unable to retrieve AWS region: {}".format(response_body)
+            )
+
+        # This endpoint will return the region in format: us-east-2b.
+        # Only the us-east-2 part should be used.
+        return response_body[:-1]
+
+    def _get_imdsv2_session_token(self, request):
+        if request is not None and self._imdsv2_session_token_url is not None:
+            headers = {
+                "X-aws-ec2-metadata-token-ttl-seconds": _IMDSV2_SESSION_TOKEN_TTL_SECONDS
+            }
+
+            imdsv2_session_token_response = request(
+                url=self._imdsv2_session_token_url, method="PUT", headers=headers
+            )
+
+            if imdsv2_session_token_response.status != http_client.OK:
+                raise exceptions.RefreshError(
+                    "Unable to retrieve AWS Session Token: {}".format(
+                        imdsv2_session_token_response.data
+                    )
+                )
+
+            return imdsv2_session_token_response.data
+        else:
+            return None
+
+    def _get_metadata_security_credentials(
+        self, request, role_name, imdsv2_session_token
+    ):
+        """Retrieves the AWS security credentials required for signing AWS
+        requests from the AWS metadata server.
+
+        Args:
+            request (google.auth.transport.Request): A callable used to make
+                HTTP requests.
+            role_name (str): The AWS role name required by the AWS metadata
+                server security_credentials endpoint in order to return the
+                credentials.
+            imdsv2_session_token (str): The AWS IMDSv2 session token to be added as a
+                header in the requests to AWS metadata endpoint.
+
+        Returns:
+            Mapping[str, str]: The AWS metadata server security credentials
+                response.
+
+        Raises:
+            google.auth.exceptions.RefreshError: If an error occurs while
+                retrieving the AWS security credentials.
+        """
+        headers = {"Content-Type": "application/json"}
+        if imdsv2_session_token is not None:
+            headers["X-aws-ec2-metadata-token"] = imdsv2_session_token
+
+        response = request(
+            url="{}/{}".format(self._security_credentials_url, role_name),
+            method="GET",
+            headers=headers,
+        )
+
+        # support both string and bytes type response.data
+        response_body = (
+            response.data.decode("utf-8")
+            if hasattr(response.data, "decode")
+            else response.data
+        )
+
+        if response.status != http_client.OK:
+            raise exceptions.RefreshError(
+                "Unable to retrieve AWS security credentials: {}".format(response_body)
+            )
+
+        credentials_response = json.loads(response_body)
+
+        return credentials_response
+
+    def _get_metadata_role_name(self, request, imdsv2_session_token):
+        """Retrieves the AWS role currently attached to the current AWS
+        workload by querying the AWS metadata server. This is needed for the
+        AWS metadata server security credentials endpoint in order to retrieve
+        the AWS security credentials needed to sign requests to AWS APIs.
+
+        Args:
+            request (google.auth.transport.Request): A callable used to make
+                HTTP requests.
+            imdsv2_session_token (str): The AWS IMDSv2 session token to be added as a
+                header in the requests to AWS metadata endpoint.
+
+        Returns:
+            str: The AWS role name.
+
+        Raises:
+            google.auth.exceptions.RefreshError: If an error occurs while
+                retrieving the AWS role name.
+        """
+        if self._security_credentials_url is None:
+            raise exceptions.RefreshError(
+                "Unable to determine the AWS metadata server security credentials endpoint"
+            )
+
+        headers = None
+        if imdsv2_session_token is not None:
+            headers = {"X-aws-ec2-metadata-token": imdsv2_session_token}
+
+        response = request(
+            url=self._security_credentials_url, method="GET", headers=headers
+        )
+
+        # support both string and bytes type response.data
+        response_body = (
+            response.data.decode("utf-8")
+            if hasattr(response.data, "decode")
+            else response.data
+        )
+
+        if response.status != http_client.OK:
+            raise exceptions.RefreshError(
+                "Unable to retrieve AWS role name {}".format(response_body)
+            )
+
+        return response_body
+
+
+class Credentials(external_account.Credentials):
+    """AWS external account credentials.
+    This is used to exchange serialized AWS signature v4 signed requests to
+    AWS STS GetCallerIdentity service for Google access tokens.
+    """
+
+    def __init__(
+        self,
+        audience,
+        subject_token_type,
+        token_url=external_account._DEFAULT_TOKEN_URL,
+        credential_source=None,
+        aws_security_credentials_supplier=None,
+        *args,
+        **kwargs
+    ):
+        """Instantiates an AWS workload external account credentials object.
+
+        Args:
+            audience (str): The STS audience field.
+            subject_token_type (str): The subject token type based on the Oauth2.0 token exchange spec.
+                Expected values include::
+
+                    “urn:ietf:params:aws:token-type:aws4_request”
+
+            token_url (Optional [str]): The STS endpoint URL. If not provided, will default to "https://sts.googleapis.com/v1/token".
+            credential_source (Optional [Mapping]): The credential source dictionary used
+                to provide instructions on how to retrieve external credential to be exchanged for Google access tokens.
+                Either a credential source or an AWS security credentials supplier must be provided.
+
+                Example credential_source for AWS credential::
+
+                    {
+                        "environment_id": "aws1",
+                        "regional_cred_verification_url": "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
+                        "region_url": "http://169.254.169.254/latest/meta-data/placement/availability-zone",
+                        "url": "http://169.254.169.254/latest/meta-data/iam/security-credentials",
+                        imdsv2_session_token_url": "http://169.254.169.254/latest/api/token"
+                    }
+
+            aws_security_credentials_supplier (Optional [AwsSecurityCredentialsSupplier]): Optional AWS security credentials supplier.
+                This will be called to supply valid AWS security credentails which will then
+                be exchanged for Google access tokens. Either an AWS security credentials supplier
+                or a credential source must be provided.
+            args (List): Optional positional arguments passed into the underlying :meth:`~external_account.Credentials.__init__` method.
+            kwargs (Mapping): Optional keyword arguments passed into the underlying :meth:`~external_account.Credentials.__init__` method.
+
+        Raises:
+            google.auth.exceptions.RefreshError: If an error is encountered during
+                access token retrieval logic.
+            ValueError: For invalid parameters.
+
+        .. note:: Typically one of the helper constructors
+            :meth:`from_file` or
+            :meth:`from_info` are used instead of calling the constructor directly.
+        """
+        super(Credentials, self).__init__(
+            audience=audience,
+            subject_token_type=subject_token_type,
+            token_url=token_url,
+            credential_source=credential_source,
+            *args,
+            **kwargs
+        )
+        if credential_source is None and aws_security_credentials_supplier is None:
+            raise exceptions.InvalidValue(
+                "A valid credential source or AWS security credentials supplier must be provided."
+            )
+        if (
+            credential_source is not None
+            and aws_security_credentials_supplier is not None
+        ):
+            raise exceptions.InvalidValue(
+                "AWS credential cannot have both a credential source and an AWS security credentials supplier."
+            )
+
+        if aws_security_credentials_supplier:
+            self._aws_security_credentials_supplier = aws_security_credentials_supplier
+            # The regional cred verification URL would normally be provided through the credential source. So set it to the default one here.
+            self._cred_verification_url = (
+                _DEFAULT_AWS_REGIONAL_CREDENTIAL_VERIFICATION_URL
+            )
+        else:
+            environment_id = credential_source.get("environment_id") or ""
+            self._aws_security_credentials_supplier = _DefaultAwsSecurityCredentialsSupplier(
+                credential_source
+            )
+            self._cred_verification_url = credential_source.get(
+                "regional_cred_verification_url"
+            )
+
+            # Get the environment ID, i.e. "aws1". Currently, only one version supported (1).
+            matches = re.match(r"^(aws)([\d]+)$", environment_id)
+            if matches:
+                env_id, env_version = matches.groups()
+            else:
+                env_id, env_version = (None, None)
+
+            if env_id != "aws" or self._cred_verification_url is None:
+                raise exceptions.InvalidResource(
+                    "No valid AWS 'credential_source' provided"
+                )
+            elif env_version is None or int(env_version) != 1:
+                raise exceptions.InvalidValue(
+                    "aws version '{}' is not supported in the current build.".format(
+                        env_version
+                    )
+                )
+
+        self._target_resource = audience
+        self._request_signer = None
+
+    def retrieve_subject_token(self, request):
+        """Retrieves the subject token using the credential_source object.
+        The subject token is a serialized `AWS GetCallerIdentity signed request`_.
+
+        The logic is summarized as:
+
+        Retrieve the AWS region from the AWS_REGION or AWS_DEFAULT_REGION
+        environment variable or from the AWS metadata server availability-zone
+        if not found in the environment variable.
+
+        Check AWS credentials in environment variables. If not found, retrieve
+        from the AWS metadata server security-credentials endpoint.
+
+        When retrieving AWS credentials from the metadata server
+        security-credentials endpoint, the AWS role needs to be determined by
+        calling the security-credentials endpoint without any argument. Then the
+        credentials can be retrieved via: security-credentials/role_name
+
+        Generate the signed request to AWS STS GetCallerIdentity action.
+
+        Inject x-goog-cloud-target-resource into header and serialize the
+        signed request. This will be the subject-token to pass to GCP STS.
+
+        .. _AWS GetCallerIdentity signed request:
+            https://cloud.google.com/iam/docs/access-resources-aws#exchange-token
+
+        Args:
+            request (google.auth.transport.Request): A callable used to make
+                HTTP requests.
+        Returns:
+            str: The retrieved subject token.
+        """
+
+        # Initialize the request signer if not yet initialized after determining
+        # the current AWS region.
+        if self._request_signer is None:
+            self._region = self._aws_security_credentials_supplier.get_aws_region(
+                self._supplier_context, request
+            )
+            self._request_signer = RequestSigner(self._region)
+
+        # Retrieve the AWS security credentials needed to generate the signed
+        # request.
+        aws_security_credentials = self._aws_security_credentials_supplier.get_aws_security_credentials(
+            self._supplier_context, request
+        )
+        # Generate the signed request to AWS STS GetCallerIdentity API.
+        # Use the required regional endpoint. Otherwise, the request will fail.
+        request_options = self._request_signer.get_request_options(
+            aws_security_credentials,
+            self._cred_verification_url.replace("{region}", self._region),
+            "POST",
+        )
+        # The GCP STS endpoint expects the headers to be formatted as:
+        # [
+        #   {key: 'x-amz-date', value: '...'},
+        #   {key: 'Authorization', value: '...'},
+        #   ...
+        # ]
+        # And then serialized as:
+        # quote(json.dumps({
+        #   url: '...',
+        #   method: 'POST',
+        #   headers: [{key: 'x-amz-date', value: '...'}, ...]
+        # }))
+        request_headers = request_options.get("headers")
+        # The full, canonical resource name of the workload identity pool
+        # provider, with or without the HTTPS prefix.
+        # Including this header as part of the signature is recommended to
+        # ensure data integrity.
+        request_headers["x-goog-cloud-target-resource"] = self._target_resource
+
+        # Serialize AWS signed request.
+        aws_signed_req = {}
+        aws_signed_req["url"] = request_options.get("url")
+        aws_signed_req["method"] = request_options.get("method")
+        aws_signed_req["headers"] = []
+        # Reformat header to GCP STS expected format.
+        for key in request_headers.keys():
+            aws_signed_req["headers"].append(
+                {"key": key, "value": request_headers[key]}
+            )
+
+        return urllib.parse.quote(
+            json.dumps(aws_signed_req, separators=(",", ":"), sort_keys=True)
+        )
+
+    def _create_default_metrics_options(self):
+        metrics_options = super(Credentials, self)._create_default_metrics_options()
+        metrics_options["source"] = "aws"
+        if self._has_custom_supplier():
+            metrics_options["source"] = "programmatic"
+        return metrics_options
+
+    def _has_custom_supplier(self):
+        return self._credential_source is None
+
+    def _constructor_args(self):
+        args = super(Credentials, self)._constructor_args()
+        # If a custom supplier was used, append it to the args dict.
+        if self._has_custom_supplier():
+            args.update(
+                {
+                    "aws_security_credentials_supplier": self._aws_security_credentials_supplier
+                }
+            )
+        return args
+
+    @classmethod
+    def from_info(cls, info, **kwargs):
+        """Creates an AWS Credentials instance from parsed external account info.
+
+        Args:
+            info (Mapping[str, str]): The AWS external account info in Google
+                format.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.aws.Credentials: The constructed credentials.
+
+        Raises:
+            ValueError: For invalid parameters.
+        """
+        aws_security_credentials_supplier = info.get(
+            "aws_security_credentials_supplier"
+        )
+        kwargs.update(
+            {"aws_security_credentials_supplier": aws_security_credentials_supplier}
+        )
+        return super(Credentials, cls).from_info(info, **kwargs)
+
+    @classmethod
+    def from_file(cls, filename, **kwargs):
+        """Creates an AWS Credentials instance from an external account json file.
+
+        Args:
+            filename (str): The path to the AWS external account json file.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.aws.Credentials: The constructed credentials.
+        """
+        return super(Credentials, cls).from_file(filename, **kwargs)
diff --git a/.venv/lib/python3.12/site-packages/google/auth/compute_engine/__init__.py b/.venv/lib/python3.12/site-packages/google/auth/compute_engine/__init__.py
new file mode 100644
index 00000000..7e1206fc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/compute_engine/__init__.py
@@ -0,0 +1,22 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Google Compute Engine authentication."""
+
+from google.auth.compute_engine._metadata import detect_gce_residency_linux
+from google.auth.compute_engine.credentials import Credentials
+from google.auth.compute_engine.credentials import IDTokenCredentials
+
+
+__all__ = ["Credentials", "IDTokenCredentials", "detect_gce_residency_linux"]
diff --git a/.venv/lib/python3.12/site-packages/google/auth/compute_engine/_metadata.py b/.venv/lib/python3.12/site-packages/google/auth/compute_engine/_metadata.py
new file mode 100644
index 00000000..06f99de0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/compute_engine/_metadata.py
@@ -0,0 +1,375 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Provides helper methods for talking to the Compute Engine metadata server.
+
+See https://cloud.google.com/compute/docs/metadata for more details.
+"""
+
+import datetime
+import http.client as http_client
+import json
+import logging
+import os
+from urllib.parse import urljoin
+
+from google.auth import _helpers
+from google.auth import environment_vars
+from google.auth import exceptions
+from google.auth import metrics
+from google.auth import transport
+from google.auth._exponential_backoff import ExponentialBackoff
+
+_LOGGER = logging.getLogger(__name__)
+
+# Environment variable GCE_METADATA_HOST is originally named
+# GCE_METADATA_ROOT. For compatibility reasons, here it checks
+# the new variable first; if not set, the system falls back
+# to the old variable.
+_GCE_METADATA_HOST = os.getenv(environment_vars.GCE_METADATA_HOST, None)
+if not _GCE_METADATA_HOST:
+    _GCE_METADATA_HOST = os.getenv(
+        environment_vars.GCE_METADATA_ROOT, "metadata.google.internal"
+    )
+_METADATA_ROOT = "http://{}/computeMetadata/v1/".format(_GCE_METADATA_HOST)
+
+# This is used to ping the metadata server, it avoids the cost of a DNS
+# lookup.
+_METADATA_IP_ROOT = "http://{}".format(
+    os.getenv(environment_vars.GCE_METADATA_IP, "169.254.169.254")
+)
+_METADATA_FLAVOR_HEADER = "metadata-flavor"
+_METADATA_FLAVOR_VALUE = "Google"
+_METADATA_HEADERS = {_METADATA_FLAVOR_HEADER: _METADATA_FLAVOR_VALUE}
+
+# Timeout in seconds to wait for the GCE metadata server when detecting the
+# GCE environment.
+try:
+    _METADATA_DEFAULT_TIMEOUT = int(os.getenv("GCE_METADATA_TIMEOUT", 3))
+except ValueError:  # pragma: NO COVER
+    _METADATA_DEFAULT_TIMEOUT = 3
+
+# Detect GCE Residency
+_GOOGLE = "Google"
+_GCE_PRODUCT_NAME_FILE = "/sys/class/dmi/id/product_name"
+
+
+def is_on_gce(request):
+    """Checks to see if the code runs on Google Compute Engine
+
+    Args:
+        request (google.auth.transport.Request): A callable used to make
+            HTTP requests.
+
+    Returns:
+        bool: True if the code runs on Google Compute Engine, False otherwise.
+    """
+    if ping(request):
+        return True
+
+    if os.name == "nt":
+        # TODO: implement GCE residency detection on Windows
+        return False
+
+    # Detect GCE residency on Linux
+    return detect_gce_residency_linux()
+
+
+def detect_gce_residency_linux():
+    """Detect Google Compute Engine residency by smbios check on Linux
+
+    Returns:
+        bool: True if the GCE product name file is detected, False otherwise.
+    """
+    try:
+        with open(_GCE_PRODUCT_NAME_FILE, "r") as file_obj:
+            content = file_obj.read().strip()
+
+    except Exception:
+        return False
+
+    return content.startswith(_GOOGLE)
+
+
+def ping(request, timeout=_METADATA_DEFAULT_TIMEOUT, retry_count=3):
+    """Checks to see if the metadata server is available.
+
+    Args:
+        request (google.auth.transport.Request): A callable used to make
+            HTTP requests.
+        timeout (int): How long to wait for the metadata server to respond.
+        retry_count (int): How many times to attempt connecting to metadata
+            server using above timeout.
+
+    Returns:
+        bool: True if the metadata server is reachable, False otherwise.
+    """
+    # NOTE: The explicit ``timeout`` is a workaround. The underlying
+    #       issue is that resolving an unknown host on some networks will take
+    #       20-30 seconds; making this timeout short fixes the issue, but
+    #       could lead to false negatives in the event that we are on GCE, but
+    #       the metadata resolution was particularly slow. The latter case is
+    #       "unlikely".
+    headers = _METADATA_HEADERS.copy()
+    headers[metrics.API_CLIENT_HEADER] = metrics.mds_ping()
+
+    backoff = ExponentialBackoff(total_attempts=retry_count)
+
+    for attempt in backoff:
+        try:
+            response = request(
+                url=_METADATA_IP_ROOT, method="GET", headers=headers, timeout=timeout
+            )
+
+            metadata_flavor = response.headers.get(_METADATA_FLAVOR_HEADER)
+            return (
+                response.status == http_client.OK
+                and metadata_flavor == _METADATA_FLAVOR_VALUE
+            )
+
+        except exceptions.TransportError as e:
+            _LOGGER.warning(
+                "Compute Engine Metadata server unavailable on "
+                "attempt %s of %s. Reason: %s",
+                attempt,
+                retry_count,
+                e,
+            )
+
+    return False
+
+
+def get(
+    request,
+    path,
+    root=_METADATA_ROOT,
+    params=None,
+    recursive=False,
+    retry_count=5,
+    headers=None,
+    return_none_for_not_found_error=False,
+):
+    """Fetch a resource from the metadata server.
+
+    Args:
+        request (google.auth.transport.Request): A callable used to make
+            HTTP requests.
+        path (str): The resource to retrieve. For example,
+            ``'instance/service-accounts/default'``.
+        root (str): The full path to the metadata server root.
+        params (Optional[Mapping[str, str]]): A mapping of query parameter
+            keys to values.
+        recursive (bool): Whether to do a recursive query of metadata. See
+            https://cloud.google.com/compute/docs/metadata#aggcontents for more
+            details.
+        retry_count (int): How many times to attempt connecting to metadata
+            server using above timeout.
+        headers (Optional[Mapping[str, str]]): Headers for the request.
+        return_none_for_not_found_error (Optional[bool]): If True, returns None
+            for 404 error instead of throwing an exception.
+
+    Returns:
+        Union[Mapping, str]: If the metadata server returns JSON, a mapping of
+            the decoded JSON is returned. Otherwise, the response content is
+            returned as a string.
+
+    Raises:
+        google.auth.exceptions.TransportError: if an error occurred while
+            retrieving metadata.
+    """
+    base_url = urljoin(root, path)
+    query_params = {} if params is None else params
+
+    headers_to_use = _METADATA_HEADERS.copy()
+    if headers:
+        headers_to_use.update(headers)
+
+    if recursive:
+        query_params["recursive"] = "true"
+
+    url = _helpers.update_query(base_url, query_params)
+
+    backoff = ExponentialBackoff(total_attempts=retry_count)
+    failure_reason = None
+    for attempt in backoff:
+        try:
+            response = request(url=url, method="GET", headers=headers_to_use)
+            if response.status in transport.DEFAULT_RETRYABLE_STATUS_CODES:
+                _LOGGER.warning(
+                    "Compute Engine Metadata server unavailable on "
+                    "attempt %s of %s. Response status: %s",
+                    attempt,
+                    retry_count,
+                    response.status,
+                )
+                failure_reason = (
+                    response.data.decode("utf-8")
+                    if hasattr(response.data, "decode")
+                    else response.data
+                )
+                continue
+            else:
+                break
+
+        except exceptions.TransportError as e:
+            _LOGGER.warning(
+                "Compute Engine Metadata server unavailable on "
+                "attempt %s of %s. Reason: %s",
+                attempt,
+                retry_count,
+                e,
+            )
+            failure_reason = e
+    else:
+        raise exceptions.TransportError(
+            "Failed to retrieve {} from the Google Compute Engine "
+            "metadata service. Compute Engine Metadata server unavailable due to {}".format(
+                url, failure_reason
+            )
+        )
+
+    content = _helpers.from_bytes(response.data)
+
+    if response.status == http_client.NOT_FOUND and return_none_for_not_found_error:
+        return None
+
+    if response.status == http_client.OK:
+        if (
+            _helpers.parse_content_type(response.headers["content-type"])
+            == "application/json"
+        ):
+            try:
+                return json.loads(content)
+            except ValueError as caught_exc:
+                new_exc = exceptions.TransportError(
+                    "Received invalid JSON from the Google Compute Engine "
+                    "metadata service: {:.20}".format(content)
+                )
+                raise new_exc from caught_exc
+        else:
+            return content
+
+    raise exceptions.TransportError(
+        "Failed to retrieve {} from the Google Compute Engine "
+        "metadata service. Status: {} Response:\n{}".format(
+            url, response.status, response.data
+        ),
+        response,
+    )
+
+
+def get_project_id(request):
+    """Get the Google Cloud Project ID from the metadata server.
+
+    Args:
+        request (google.auth.transport.Request): A callable used to make
+            HTTP requests.
+
+    Returns:
+        str: The project ID
+
+    Raises:
+        google.auth.exceptions.TransportError: if an error occurred while
+            retrieving metadata.
+    """
+    return get(request, "project/project-id")
+
+
+def get_universe_domain(request):
+    """Get the universe domain value from the metadata server.
+
+    Args:
+        request (google.auth.transport.Request): A callable used to make
+            HTTP requests.
+
+    Returns:
+        str: The universe domain value. If the universe domain endpoint is not
+        not found, return the default value, which is googleapis.com
+
+    Raises:
+        google.auth.exceptions.TransportError: if an error other than
+            404 occurs while retrieving metadata.
+    """
+    universe_domain = get(
+        request, "universe/universe-domain", return_none_for_not_found_error=True
+    )
+    if not universe_domain:
+        return "googleapis.com"
+    return universe_domain
+
+
+def get_service_account_info(request, service_account="default"):
+    """Get information about a service account from the metadata server.
+
+    Args:
+        request (google.auth.transport.Request): A callable used to make
+            HTTP requests.
+        service_account (str): The string 'default' or a service account email
+            address. The determines which service account for which to acquire
+            information.
+
+    Returns:
+        Mapping: The service account's information, for example::
+
+            {
+                'email': '...',
+                'scopes': ['scope', ...],
+                'aliases': ['default', '...']
+            }
+
+    Raises:
+        google.auth.exceptions.TransportError: if an error occurred while
+            retrieving metadata.
+    """
+    path = "instance/service-accounts/{0}/".format(service_account)
+    # See https://cloud.google.com/compute/docs/metadata#aggcontents
+    # for more on the use of 'recursive'.
+    return get(request, path, params={"recursive": "true"})
+
+
+def get_service_account_token(request, service_account="default", scopes=None):
+    """Get the OAuth 2.0 access token for a service account.
+
+    Args:
+        request (google.auth.transport.Request): A callable used to make
+            HTTP requests.
+        service_account (str): The string 'default' or a service account email
+            address. The determines which service account for which to acquire
+            an access token.
+        scopes (Optional[Union[str, List[str]]]): Optional string or list of
+            strings with auth scopes.
+    Returns:
+        Tuple[str, datetime]: The access token and its expiration.
+
+    Raises:
+        google.auth.exceptions.TransportError: if an error occurred while
+            retrieving metadata.
+    """
+    if scopes:
+        if not isinstance(scopes, str):
+            scopes = ",".join(scopes)
+        params = {"scopes": scopes}
+    else:
+        params = None
+
+    metrics_header = {
+        metrics.API_CLIENT_HEADER: metrics.token_request_access_token_mds()
+    }
+
+    path = "instance/service-accounts/{0}/token".format(service_account)
+    token_json = get(request, path, params=params, headers=metrics_header)
+    token_expiry = _helpers.utcnow() + datetime.timedelta(
+        seconds=token_json["expires_in"]
+    )
+    return token_json["access_token"], token_expiry
diff --git a/.venv/lib/python3.12/site-packages/google/auth/compute_engine/credentials.py b/.venv/lib/python3.12/site-packages/google/auth/compute_engine/credentials.py
new file mode 100644
index 00000000..f0126c0a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/compute_engine/credentials.py
@@ -0,0 +1,496 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Google Compute Engine credentials.
+
+This module provides authentication for an application running on Google
+Compute Engine using the Compute Engine metadata server.
+
+"""
+
+import datetime
+
+from google.auth import _helpers
+from google.auth import credentials
+from google.auth import exceptions
+from google.auth import iam
+from google.auth import jwt
+from google.auth import metrics
+from google.auth.compute_engine import _metadata
+from google.oauth2 import _client
+
+
+class Credentials(
+    credentials.Scoped,
+    credentials.CredentialsWithQuotaProject,
+    credentials.CredentialsWithUniverseDomain,
+):
+    """Compute Engine Credentials.
+
+    These credentials use the Google Compute Engine metadata server to obtain
+    OAuth 2.0 access tokens associated with the instance's service account,
+    and are also used for Cloud Run, Flex and App Engine (except for the Python
+    2.7 runtime, which is supported only on older versions of this library).
+
+    For more information about Compute Engine authentication, including how
+    to configure scopes, see the `Compute Engine authentication
+    documentation`_.
+
+    .. note:: On Compute Engine the metadata server ignores requested scopes.
+        On Cloud Run, Flex and App Engine the server honours requested scopes.
+
+    .. _Compute Engine authentication documentation:
+        https://cloud.google.com/compute/docs/authentication#using
+    """
+
+    def __init__(
+        self,
+        service_account_email="default",
+        quota_project_id=None,
+        scopes=None,
+        default_scopes=None,
+        universe_domain=None,
+    ):
+        """
+        Args:
+            service_account_email (str): The service account email to use, or
+                'default'. A Compute Engine instance may have multiple service
+                accounts.
+            quota_project_id (Optional[str]): The project ID used for quota and
+                billing.
+            scopes (Optional[Sequence[str]]): The list of scopes for the credentials.
+            default_scopes (Optional[Sequence[str]]): Default scopes passed by a
+                Google client library. Use 'scopes' for user-defined scopes.
+            universe_domain (Optional[str]): The universe domain. If not
+                provided or None, credential will attempt to fetch the value
+                from metadata server. If metadata server doesn't have universe
+                domain endpoint, then the default googleapis.com will be used.
+        """
+        super(Credentials, self).__init__()
+        self._service_account_email = service_account_email
+        self._quota_project_id = quota_project_id
+        self._scopes = scopes
+        self._default_scopes = default_scopes
+        self._universe_domain_cached = False
+        if universe_domain:
+            self._universe_domain = universe_domain
+            self._universe_domain_cached = True
+
+    def _retrieve_info(self, request):
+        """Retrieve information about the service account.
+
+        Updates the scopes and retrieves the full service account email.
+
+        Args:
+            request (google.auth.transport.Request): The object used to make
+                HTTP requests.
+        """
+        info = _metadata.get_service_account_info(
+            request, service_account=self._service_account_email
+        )
+
+        self._service_account_email = info["email"]
+
+        # Don't override scopes requested by the user.
+        if self._scopes is None:
+            self._scopes = info["scopes"]
+
+    def _metric_header_for_usage(self):
+        return metrics.CRED_TYPE_SA_MDS
+
+    def refresh(self, request):
+        """Refresh the access token and scopes.
+
+        Args:
+            request (google.auth.transport.Request): The object used to make
+                HTTP requests.
+
+        Raises:
+            google.auth.exceptions.RefreshError: If the Compute Engine metadata
+                service can't be reached if if the instance has not
+                credentials.
+        """
+        scopes = self._scopes if self._scopes is not None else self._default_scopes
+        try:
+            self._retrieve_info(request)
+            self.token, self.expiry = _metadata.get_service_account_token(
+                request, service_account=self._service_account_email, scopes=scopes
+            )
+        except exceptions.TransportError as caught_exc:
+            new_exc = exceptions.RefreshError(caught_exc)
+            raise new_exc from caught_exc
+
+    @property
+    def service_account_email(self):
+        """The service account email.
+
+        .. note:: This is not guaranteed to be set until :meth:`refresh` has been
+            called.
+        """
+        return self._service_account_email
+
+    @property
+    def requires_scopes(self):
+        return not self._scopes
+
+    @property
+    def universe_domain(self):
+        if self._universe_domain_cached:
+            return self._universe_domain
+
+        from google.auth.transport import requests as google_auth_requests
+
+        self._universe_domain = _metadata.get_universe_domain(
+            google_auth_requests.Request()
+        )
+        self._universe_domain_cached = True
+        return self._universe_domain
+
+    @_helpers.copy_docstring(credentials.Credentials)
+    def get_cred_info(self):
+        return {
+            "credential_source": "metadata server",
+            "credential_type": "VM credentials",
+            "principal": self.service_account_email,
+        }
+
+    @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
+    def with_quota_project(self, quota_project_id):
+        creds = self.__class__(
+            service_account_email=self._service_account_email,
+            quota_project_id=quota_project_id,
+            scopes=self._scopes,
+            default_scopes=self._default_scopes,
+        )
+        creds._universe_domain = self._universe_domain
+        creds._universe_domain_cached = self._universe_domain_cached
+        return creds
+
+    @_helpers.copy_docstring(credentials.Scoped)
+    def with_scopes(self, scopes, default_scopes=None):
+        # Compute Engine credentials can not be scoped (the metadata service
+        # ignores the scopes parameter). App Engine, Cloud Run and Flex support
+        # requesting scopes.
+        creds = self.__class__(
+            scopes=scopes,
+            default_scopes=default_scopes,
+            service_account_email=self._service_account_email,
+            quota_project_id=self._quota_project_id,
+        )
+        creds._universe_domain = self._universe_domain
+        creds._universe_domain_cached = self._universe_domain_cached
+        return creds
+
+    @_helpers.copy_docstring(credentials.CredentialsWithUniverseDomain)
+    def with_universe_domain(self, universe_domain):
+        return self.__class__(
+            scopes=self._scopes,
+            default_scopes=self._default_scopes,
+            service_account_email=self._service_account_email,
+            quota_project_id=self._quota_project_id,
+            universe_domain=universe_domain,
+        )
+
+
+_DEFAULT_TOKEN_LIFETIME_SECS = 3600  # 1 hour in seconds
+_DEFAULT_TOKEN_URI = "https://www.googleapis.com/oauth2/v4/token"
+
+
+class IDTokenCredentials(
+    credentials.CredentialsWithQuotaProject,
+    credentials.Signing,
+    credentials.CredentialsWithTokenUri,
+):
+    """Open ID Connect ID Token-based service account credentials.
+
+    These credentials relies on the default service account of a GCE instance.
+
+    ID token can be requested from `GCE metadata server identity endpoint`_, IAM
+    token endpoint or other token endpoints you specify. If metadata server
+    identity endpoint is not used, the GCE instance must have been started with
+    a service account that has access to the IAM Cloud API.
+
+    .. _GCE metadata server identity endpoint:
+        https://cloud.google.com/compute/docs/instances/verifying-instance-identity
+    """
+
+    def __init__(
+        self,
+        request,
+        target_audience,
+        token_uri=None,
+        additional_claims=None,
+        service_account_email=None,
+        signer=None,
+        use_metadata_identity_endpoint=False,
+        quota_project_id=None,
+    ):
+        """
+        Args:
+            request (google.auth.transport.Request): The object used to make
+                HTTP requests.
+            target_audience (str): The intended audience for these credentials,
+                used when requesting the ID Token. The ID Token's ``aud`` claim
+                will be set to this string.
+            token_uri (str): The OAuth 2.0 Token URI.
+            additional_claims (Mapping[str, str]): Any additional claims for
+                the JWT assertion used in the authorization grant.
+            service_account_email (str): Optional explicit service account to
+                use to sign JWT tokens.
+                By default, this is the default GCE service account.
+            signer (google.auth.crypt.Signer): The signer used to sign JWTs.
+                In case the signer is specified, the request argument will be
+                ignored.
+            use_metadata_identity_endpoint (bool): Whether to use GCE metadata
+                identity endpoint. For backward compatibility the default value
+                is False. If set to True, ``token_uri``, ``additional_claims``,
+                ``service_account_email``, ``signer`` argument should not be set;
+                otherwise ValueError will be raised.
+            quota_project_id (Optional[str]): The project ID used for quota and
+                billing.
+
+        Raises:
+            ValueError:
+                If ``use_metadata_identity_endpoint`` is set to True, and one of
+                ``token_uri``, ``additional_claims``, ``service_account_email``,
+                 ``signer`` arguments is set.
+        """
+        super(IDTokenCredentials, self).__init__()
+
+        self._quota_project_id = quota_project_id
+        self._use_metadata_identity_endpoint = use_metadata_identity_endpoint
+        self._target_audience = target_audience
+
+        if use_metadata_identity_endpoint:
+            if token_uri or additional_claims or service_account_email or signer:
+                raise exceptions.MalformedError(
+                    "If use_metadata_identity_endpoint is set, token_uri, "
+                    "additional_claims, service_account_email, signer arguments"
+                    " must not be set"
+                )
+            self._token_uri = None
+            self._additional_claims = None
+            self._signer = None
+
+        if service_account_email is None:
+            sa_info = _metadata.get_service_account_info(request)
+            self._service_account_email = sa_info["email"]
+        else:
+            self._service_account_email = service_account_email
+
+        if not use_metadata_identity_endpoint:
+            if signer is None:
+                signer = iam.Signer(
+                    request=request,
+                    credentials=Credentials(),
+                    service_account_email=self._service_account_email,
+                )
+            self._signer = signer
+            self._token_uri = token_uri or _DEFAULT_TOKEN_URI
+
+            if additional_claims is not None:
+                self._additional_claims = additional_claims
+            else:
+                self._additional_claims = {}
+
+    def with_target_audience(self, target_audience):
+        """Create a copy of these credentials with the specified target
+        audience.
+        Args:
+            target_audience (str): The intended audience for these credentials,
+            used when requesting the ID Token.
+        Returns:
+            google.auth.service_account.IDTokenCredentials: A new credentials
+                instance.
+        """
+        # since the signer is already instantiated,
+        # the request is not needed
+        if self._use_metadata_identity_endpoint:
+            return self.__class__(
+                None,
+                target_audience=target_audience,
+                use_metadata_identity_endpoint=True,
+                quota_project_id=self._quota_project_id,
+            )
+        else:
+            return self.__class__(
+                None,
+                service_account_email=self._service_account_email,
+                token_uri=self._token_uri,
+                target_audience=target_audience,
+                additional_claims=self._additional_claims.copy(),
+                signer=self.signer,
+                use_metadata_identity_endpoint=False,
+                quota_project_id=self._quota_project_id,
+            )
+
+    @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
+    def with_quota_project(self, quota_project_id):
+
+        # since the signer is already instantiated,
+        # the request is not needed
+        if self._use_metadata_identity_endpoint:
+            return self.__class__(
+                None,
+                target_audience=self._target_audience,
+                use_metadata_identity_endpoint=True,
+                quota_project_id=quota_project_id,
+            )
+        else:
+            return self.__class__(
+                None,
+                service_account_email=self._service_account_email,
+                token_uri=self._token_uri,
+                target_audience=self._target_audience,
+                additional_claims=self._additional_claims.copy(),
+                signer=self.signer,
+                use_metadata_identity_endpoint=False,
+                quota_project_id=quota_project_id,
+            )
+
+    @_helpers.copy_docstring(credentials.CredentialsWithTokenUri)
+    def with_token_uri(self, token_uri):
+
+        # since the signer is already instantiated,
+        # the request is not needed
+        if self._use_metadata_identity_endpoint:
+            raise exceptions.MalformedError(
+                "If use_metadata_identity_endpoint is set, token_uri" " must not be set"
+            )
+        else:
+            return self.__class__(
+                None,
+                service_account_email=self._service_account_email,
+                token_uri=token_uri,
+                target_audience=self._target_audience,
+                additional_claims=self._additional_claims.copy(),
+                signer=self.signer,
+                use_metadata_identity_endpoint=False,
+                quota_project_id=self.quota_project_id,
+            )
+
+    def _make_authorization_grant_assertion(self):
+        """Create the OAuth 2.0 assertion.
+        This assertion is used during the OAuth 2.0 grant to acquire an
+        ID token.
+        Returns:
+            bytes: The authorization grant assertion.
+        """
+        now = _helpers.utcnow()
+        lifetime = datetime.timedelta(seconds=_DEFAULT_TOKEN_LIFETIME_SECS)
+        expiry = now + lifetime
+
+        payload = {
+            "iat": _helpers.datetime_to_secs(now),
+            "exp": _helpers.datetime_to_secs(expiry),
+            # The issuer must be the service account email.
+            "iss": self.service_account_email,
+            # The audience must be the auth token endpoint's URI
+            "aud": self._token_uri,
+            # The target audience specifies which service the ID token is
+            # intended for.
+            "target_audience": self._target_audience,
+        }
+
+        payload.update(self._additional_claims)
+
+        token = jwt.encode(self._signer, payload)
+
+        return token
+
+    def _call_metadata_identity_endpoint(self, request):
+        """Request ID token from metadata identity endpoint.
+
+        Args:
+            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.
+            ValueError: If extracting expiry from the obtained ID token fails.
+        """
+        try:
+            path = "instance/service-accounts/default/identity"
+            params = {"audience": self._target_audience, "format": "full"}
+            metrics_header = {
+                metrics.API_CLIENT_HEADER: metrics.token_request_id_token_mds()
+            }
+            id_token = _metadata.get(
+                request, path, params=params, headers=metrics_header
+            )
+        except exceptions.TransportError as caught_exc:
+            new_exc = exceptions.RefreshError(caught_exc)
+            raise new_exc from caught_exc
+
+        _, payload, _, _ = jwt._unverified_decode(id_token)
+        return id_token, datetime.datetime.utcfromtimestamp(payload["exp"])
+
+    def refresh(self, request):
+        """Refreshes the ID token.
+
+        Args:
+            request (google.auth.transport.Request): The object used to make
+                HTTP requests.
+
+        Raises:
+            google.auth.exceptions.RefreshError: If the credentials could
+                not be refreshed.
+            ValueError: If extracting expiry from the obtained ID token fails.
+        """
+        if self._use_metadata_identity_endpoint:
+            self.token, self.expiry = self._call_metadata_identity_endpoint(request)
+        else:
+            assertion = self._make_authorization_grant_assertion()
+            access_token, expiry, _ = _client.id_token_jwt_grant(
+                request, self._token_uri, assertion
+            )
+            self.token = access_token
+            self.expiry = expiry
+
+    @property  # type: ignore
+    @_helpers.copy_docstring(credentials.Signing)
+    def signer(self):
+        return self._signer
+
+    def sign_bytes(self, message):
+        """Signs the given message.
+
+        Args:
+            message (bytes): The message to sign.
+
+        Returns:
+            bytes: The message's cryptographic signature.
+
+        Raises:
+            ValueError:
+                Signer is not available if metadata identity endpoint is used.
+        """
+        if self._use_metadata_identity_endpoint:
+            raise exceptions.InvalidOperation(
+                "Signer is not available if metadata identity endpoint is used"
+            )
+        return self._signer.sign(message)
+
+    @property
+    def service_account_email(self):
+        """The service account email."""
+        return self._service_account_email
+
+    @property
+    def signer_email(self):
+        return self._service_account_email
diff --git a/.venv/lib/python3.12/site-packages/google/auth/credentials.py b/.venv/lib/python3.12/site-packages/google/auth/credentials.py
new file mode 100644
index 00000000..2c67e044
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/credentials.py
@@ -0,0 +1,522 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+"""Interfaces for credentials."""
+
+import abc
+from enum import Enum
+import os
+
+from google.auth import _helpers, environment_vars
+from google.auth import exceptions
+from google.auth import metrics
+from google.auth._credentials_base import _BaseCredentials
+from google.auth._refresh_worker import RefreshThreadManager
+
+DEFAULT_UNIVERSE_DOMAIN = "googleapis.com"
+
+
+class Credentials(_BaseCredentials):
+    """Base class for all credentials.
+
+    All credentials have a :attr:`token` that is used for authentication and
+    may also optionally set an :attr:`expiry` to indicate when the token will
+    no longer be valid.
+
+    Most credentials will be :attr:`invalid` until :meth:`refresh` is called.
+    Credentials can do this automatically before the first HTTP request in
+    :meth:`before_request`.
+
+    Although the token and expiration will change as the credentials are
+    :meth:`refreshed <refresh>` and used, credentials should be considered
+    immutable. Various credentials will accept configuration such as private
+    keys, scopes, and other options. These options are not changeable after
+    construction. Some classes will provide mechanisms to copy the credentials
+    with modifications such as :meth:`ScopedCredentials.with_scopes`.
+    """
+
+    def __init__(self):
+        super(Credentials, self).__init__()
+
+        self.expiry = None
+        """Optional[datetime]: When the token expires and is no longer valid.
+        If this is None, the token is assumed to never expire."""
+        self._quota_project_id = None
+        """Optional[str]: Project to use for quota and billing purposes."""
+        self._trust_boundary = None
+        """Optional[dict]: Cache of a trust boundary response which has a list
+        of allowed regions and an encoded string representation of credentials
+        trust boundary."""
+        self._universe_domain = DEFAULT_UNIVERSE_DOMAIN
+        """Optional[str]: The universe domain value, default is googleapis.com
+        """
+
+        self._use_non_blocking_refresh = False
+        self._refresh_worker = RefreshThreadManager()
+
+    @property
+    def expired(self):
+        """Checks if the credentials are expired.
+
+        Note that credentials can be invalid but not expired because
+        Credentials with :attr:`expiry` set to None is considered to never
+        expire.
+
+        .. deprecated:: v2.24.0
+          Prefer checking :attr:`token_state` instead.
+        """
+        if not self.expiry:
+            return False
+        # Remove some threshold from expiry to err on the side of reporting
+        # expiration early so that we avoid the 401-refresh-retry loop.
+        skewed_expiry = self.expiry - _helpers.REFRESH_THRESHOLD
+        return _helpers.utcnow() >= skewed_expiry
+
+    @property
+    def valid(self):
+        """Checks the validity of the credentials.
+
+        This is True if the credentials have a :attr:`token` and the token
+        is not :attr:`expired`.
+
+        .. deprecated:: v2.24.0
+          Prefer checking :attr:`token_state` instead.
+        """
+        return self.token is not None and not self.expired
+
+    @property
+    def token_state(self):
+        """
+        See `:obj:`TokenState`
+        """
+        if self.token is None:
+            return TokenState.INVALID
+
+        # Credentials that can't expire are always treated as fresh.
+        if self.expiry is None:
+            return TokenState.FRESH
+
+        expired = _helpers.utcnow() >= self.expiry
+        if expired:
+            return TokenState.INVALID
+
+        is_stale = _helpers.utcnow() >= (self.expiry - _helpers.REFRESH_THRESHOLD)
+        if is_stale:
+            return TokenState.STALE
+
+        return TokenState.FRESH
+
+    @property
+    def quota_project_id(self):
+        """Project to use for quota and billing purposes."""
+        return self._quota_project_id
+
+    @property
+    def universe_domain(self):
+        """The universe domain value."""
+        return self._universe_domain
+
+    def get_cred_info(self):
+        """The credential information JSON.
+
+        The credential information will be added to auth related error messages
+        by client library.
+
+        Returns:
+            Mapping[str, str]: The credential information JSON.
+        """
+        return None
+
+    @abc.abstractmethod
+    def refresh(self, request):
+        """Refreshes the access token.
+
+        Args:
+            request (google.auth.transport.Request): The object used to make
+                HTTP requests.
+
+        Raises:
+            google.auth.exceptions.RefreshError: If the credentials could
+                not be refreshed.
+        """
+        # pylint: disable=missing-raises-doc
+        # (pylint doesn't recognize that this is abstract)
+        raise NotImplementedError("Refresh must be implemented")
+
+    def _metric_header_for_usage(self):
+        """The x-goog-api-client header for token usage metric.
+
+        This header will be added to the API service requests in before_request
+        method. For example, "cred-type/sa-jwt" means service account self
+        signed jwt access token is used in the API service request
+        authorization header. Children credentials classes need to override
+        this method to provide the header value, if the token usage metric is
+        needed.
+
+        Returns:
+            str: The x-goog-api-client header value.
+        """
+        return None
+
+    def apply(self, headers, token=None):
+        """Apply the token to the authentication header.
+
+        Args:
+            headers (Mapping): The HTTP request headers.
+            token (Optional[str]): If specified, overrides the current access
+                token.
+        """
+        self._apply(headers, token=token)
+        """Trust boundary value will be a cached value from global lookup.
+
+        The response of trust boundary will be a list of regions and a hex
+        encoded representation.
+
+        An example of global lookup response:
+        {
+          "locations": [
+            "us-central1", "us-east1", "europe-west1", "asia-east1"
+          ]
+          "encoded_locations": "0xA30"
+        }
+        """
+        if self._trust_boundary is not None:
+            headers["x-allowed-locations"] = self._trust_boundary["encoded_locations"]
+        if self.quota_project_id:
+            headers["x-goog-user-project"] = self.quota_project_id
+
+    def _blocking_refresh(self, request):
+        if not self.valid:
+            self.refresh(request)
+
+    def _non_blocking_refresh(self, request):
+        use_blocking_refresh_fallback = False
+
+        if self.token_state == TokenState.STALE:
+            use_blocking_refresh_fallback = not self._refresh_worker.start_refresh(
+                self, request
+            )
+
+        if self.token_state == TokenState.INVALID or use_blocking_refresh_fallback:
+            self.refresh(request)
+            # If the blocking refresh succeeds then we can clear the error info
+            # on the background refresh worker, and perform refreshes in a
+            # background thread.
+            self._refresh_worker.clear_error()
+
+    def before_request(self, request, method, url, headers):
+        """Performs credential-specific before request logic.
+
+        Refreshes the credentials if necessary, then calls :meth:`apply` to
+        apply the token to the authentication header.
+
+        Args:
+            request (google.auth.transport.Request): The object used to make
+                HTTP requests.
+            method (str): The request's HTTP method or the RPC method being
+                invoked.
+            url (str): The request's URI or the RPC service's URI.
+            headers (Mapping): The request's headers.
+        """
+        # pylint: disable=unused-argument
+        # (Subclasses may use these arguments to ascertain information about
+        # the http request.)
+        if self._use_non_blocking_refresh:
+            self._non_blocking_refresh(request)
+        else:
+            self._blocking_refresh(request)
+
+        metrics.add_metric_header(headers, self._metric_header_for_usage())
+        self.apply(headers)
+
+    def with_non_blocking_refresh(self):
+        self._use_non_blocking_refresh = True
+
+
+class CredentialsWithQuotaProject(Credentials):
+    """Abstract base for credentials supporting ``with_quota_project`` factory"""
+
+    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.credentials.Credentials: A new credentials instance.
+        """
+        raise NotImplementedError("This credential does not support quota project.")
+
+    def with_quota_project_from_environment(self):
+        quota_from_env = os.environ.get(environment_vars.GOOGLE_CLOUD_QUOTA_PROJECT)
+        if quota_from_env:
+            return self.with_quota_project(quota_from_env)
+        return self
+
+
+class CredentialsWithTokenUri(Credentials):
+    """Abstract base for credentials supporting ``with_token_uri`` factory"""
+
+    def with_token_uri(self, token_uri):
+        """Returns a copy of these credentials with a modified token uri.
+
+        Args:
+            token_uri (str): The uri to use for fetching/exchanging tokens
+
+        Returns:
+            google.auth.credentials.Credentials: A new credentials instance.
+        """
+        raise NotImplementedError("This credential does not use token uri.")
+
+
+class CredentialsWithUniverseDomain(Credentials):
+    """Abstract base for credentials supporting ``with_universe_domain`` factory"""
+
+    def with_universe_domain(self, universe_domain):
+        """Returns a copy of these credentials with a modified universe domain.
+
+        Args:
+            universe_domain (str): The universe domain to use
+
+        Returns:
+            google.auth.credentials.Credentials: A new credentials instance.
+        """
+        raise NotImplementedError(
+            "This credential does not support with_universe_domain."
+        )
+
+
+class AnonymousCredentials(Credentials):
+    """Credentials that do not provide any authentication information.
+
+    These are useful in the case of services that support anonymous access or
+    local service emulators that do not use credentials.
+    """
+
+    @property
+    def expired(self):
+        """Returns `False`, anonymous credentials never expire."""
+        return False
+
+    @property
+    def valid(self):
+        """Returns `True`, anonymous credentials are always valid."""
+        return True
+
+    def refresh(self, request):
+        """Raises :class:``InvalidOperation``, anonymous credentials cannot be
+        refreshed."""
+        raise exceptions.InvalidOperation("Anonymous credentials cannot be refreshed.")
+
+    def apply(self, headers, token=None):
+        """Anonymous credentials do nothing to the request.
+
+        The optional ``token`` argument is not supported.
+
+        Raises:
+            google.auth.exceptions.InvalidValue: If a token was specified.
+        """
+        if token is not None:
+            raise exceptions.InvalidValue("Anonymous credentials don't support tokens.")
+
+    def before_request(self, request, method, url, headers):
+        """Anonymous credentials do nothing to the request."""
+
+
+class ReadOnlyScoped(metaclass=abc.ABCMeta):
+    """Interface for credentials whose scopes can be queried.
+
+    OAuth 2.0-based credentials allow limiting access using scopes as described
+    in `RFC6749 Section 3.3`_.
+    If a credential class implements this interface then the credentials either
+    use scopes in their implementation.
+
+    Some credentials require scopes in order to obtain a token. You can check
+    if scoping is necessary with :attr:`requires_scopes`::
+
+        if credentials.requires_scopes:
+            # Scoping is required.
+            credentials = credentials.with_scopes(scopes=['one', 'two'])
+
+    Credentials that require scopes must either be constructed with scopes::
+
+        credentials = SomeScopedCredentials(scopes=['one', 'two'])
+
+    Or must copy an existing instance using :meth:`with_scopes`::
+
+        scoped_credentials = credentials.with_scopes(scopes=['one', 'two'])
+
+    Some credentials have scopes but do not allow or require scopes to be set,
+    these credentials can be used as-is.
+
+    .. _RFC6749 Section 3.3: https://tools.ietf.org/html/rfc6749#section-3.3
+    """
+
+    def __init__(self):
+        super(ReadOnlyScoped, self).__init__()
+        self._scopes = None
+        self._default_scopes = None
+
+    @property
+    def scopes(self):
+        """Sequence[str]: the credentials' current set of scopes."""
+        return self._scopes
+
+    @property
+    def default_scopes(self):
+        """Sequence[str]: the credentials' current set of default scopes."""
+        return self._default_scopes
+
+    @abc.abstractproperty
+    def requires_scopes(self):
+        """True if these credentials require scopes to obtain an access token.
+        """
+        return False
+
+    def has_scopes(self, scopes):
+        """Checks if the credentials have the given scopes.
+
+        .. warning: This method is not guaranteed to be accurate if the
+            credentials are :attr:`~Credentials.invalid`.
+
+        Args:
+            scopes (Sequence[str]): The list of scopes to check.
+
+        Returns:
+            bool: True if the credentials have the given scopes.
+        """
+        credential_scopes = (
+            self._scopes if self._scopes is not None else self._default_scopes
+        )
+        return set(scopes).issubset(set(credential_scopes or []))
+
+
+class Scoped(ReadOnlyScoped):
+    """Interface for credentials whose scopes can be replaced while copying.
+
+    OAuth 2.0-based credentials allow limiting access using scopes as described
+    in `RFC6749 Section 3.3`_.
+    If a credential class implements this interface then the credentials either
+    use scopes in their implementation.
+
+    Some credentials require scopes in order to obtain a token. You can check
+    if scoping is necessary with :attr:`requires_scopes`::
+
+        if credentials.requires_scopes:
+            # Scoping is required.
+            credentials = credentials.create_scoped(['one', 'two'])
+
+    Credentials that require scopes must either be constructed with scopes::
+
+        credentials = SomeScopedCredentials(scopes=['one', 'two'])
+
+    Or must copy an existing instance using :meth:`with_scopes`::
+
+        scoped_credentials = credentials.with_scopes(scopes=['one', 'two'])
+
+    Some credentials have scopes but do not allow or require scopes to be set,
+    these credentials can be used as-is.
+
+    .. _RFC6749 Section 3.3: https://tools.ietf.org/html/rfc6749#section-3.3
+    """
+
+    @abc.abstractmethod
+    def with_scopes(self, scopes, default_scopes=None):
+        """Create a copy of these credentials with the specified scopes.
+
+        Args:
+            scopes (Sequence[str]): The list of scopes to attach to the
+                current credentials.
+
+        Raises:
+            NotImplementedError: If the credentials' scopes can not be changed.
+                This can be avoided by checking :attr:`requires_scopes` before
+                calling this method.
+        """
+        raise NotImplementedError("This class does not require scoping.")
+
+
+def with_scopes_if_required(credentials, scopes, default_scopes=None):
+    """Creates a copy of the credentials with scopes if scoping is required.
+
+    This helper function is useful when you do not know (or care to know) the
+    specific type of credentials you are using (such as when you use
+    :func:`google.auth.default`). This function will call
+    :meth:`Scoped.with_scopes` if the credentials are scoped credentials and if
+    the credentials require scoping. Otherwise, it will return the credentials
+    as-is.
+
+    Args:
+        credentials (google.auth.credentials.Credentials): The credentials to
+            scope if necessary.
+        scopes (Sequence[str]): The list of scopes to use.
+        default_scopes (Sequence[str]): Default scopes passed by a
+            Google client library. Use 'scopes' for user-defined scopes.
+
+    Returns:
+        google.auth.credentials.Credentials: Either a new set of scoped
+            credentials, or the passed in credentials instance if no scoping
+            was required.
+    """
+    if isinstance(credentials, Scoped) and credentials.requires_scopes:
+        return credentials.with_scopes(scopes, default_scopes=default_scopes)
+    else:
+        return credentials
+
+
+class Signing(metaclass=abc.ABCMeta):
+    """Interface for credentials that can cryptographically sign messages."""
+
+    @abc.abstractmethod
+    def sign_bytes(self, message):
+        """Signs the given message.
+
+        Args:
+            message (bytes): The message to sign.
+
+        Returns:
+            bytes: The message's cryptographic signature.
+        """
+        # pylint: disable=missing-raises-doc,redundant-returns-doc
+        # (pylint doesn't recognize that this is abstract)
+        raise NotImplementedError("Sign bytes must be implemented.")
+
+    @abc.abstractproperty
+    def signer_email(self):
+        """Optional[str]: An email address that identifies the signer."""
+        # pylint: disable=missing-raises-doc
+        # (pylint doesn't recognize that this is abstract)
+        raise NotImplementedError("Signer email must be implemented.")
+
+    @abc.abstractproperty
+    def signer(self):
+        """google.auth.crypt.Signer: The signer used to sign bytes."""
+        # pylint: disable=missing-raises-doc
+        # (pylint doesn't recognize that this is abstract)
+        raise NotImplementedError("Signer must be implemented.")
+
+
+class TokenState(Enum):
+    """
+    Tracks the state of a token.
+    FRESH: The token is valid. It is not expired or close to expired, or the token has no expiry.
+    STALE: The token is close to expired, and should be refreshed. The token can be used normally.
+    INVALID: The token is expired or invalid. The token cannot be used for a normal operation.
+    """
+
+    FRESH = 1
+    STALE = 2
+    INVALID = 3
diff --git a/.venv/lib/python3.12/site-packages/google/auth/crypt/__init__.py b/.venv/lib/python3.12/site-packages/google/auth/crypt/__init__.py
new file mode 100644
index 00000000..6d147e70
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/crypt/__init__.py
@@ -0,0 +1,98 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Cryptography helpers for verifying and signing messages.
+
+The simplest way to verify signatures is using :func:`verify_signature`::
+
+    cert = open('certs.pem').read()
+    valid = crypt.verify_signature(message, signature, cert)
+
+If you're going to verify many messages with the same certificate, you can use
+:class:`RSAVerifier`::
+
+    cert = open('certs.pem').read()
+    verifier = crypt.RSAVerifier.from_string(cert)
+    valid = verifier.verify(message, signature)
+
+To sign messages use :class:`RSASigner` with a private key::
+
+    private_key = open('private_key.pem').read()
+    signer = crypt.RSASigner.from_string(private_key)
+    signature = signer.sign(message)
+
+The code above also works for :class:`ES256Signer` and :class:`ES256Verifier`.
+Note that these two classes are only available if your `cryptography` dependency
+version is at least 1.4.0.
+"""
+
+from google.auth.crypt import base
+from google.auth.crypt import rsa
+
+try:
+    from google.auth.crypt import es256
+except ImportError:  # pragma: NO COVER
+    es256 = None  # type: ignore
+
+if es256 is not None:  # pragma: NO COVER
+    __all__ = [
+        "ES256Signer",
+        "ES256Verifier",
+        "RSASigner",
+        "RSAVerifier",
+        "Signer",
+        "Verifier",
+    ]
+else:  # pragma: NO COVER
+    __all__ = ["RSASigner", "RSAVerifier", "Signer", "Verifier"]
+
+
+# Aliases to maintain the v1.0.0 interface, as the crypt module was split
+# into submodules.
+Signer = base.Signer
+Verifier = base.Verifier
+RSASigner = rsa.RSASigner
+RSAVerifier = rsa.RSAVerifier
+
+if es256 is not None:  # pragma: NO COVER
+    ES256Signer = es256.ES256Signer
+    ES256Verifier = es256.ES256Verifier
+
+
+def verify_signature(message, signature, certs, verifier_cls=rsa.RSAVerifier):
+    """Verify an RSA or ECDSA cryptographic signature.
+
+    Checks that the provided ``signature`` was generated from ``bytes`` using
+    the private key associated with the ``cert``.
+
+    Args:
+        message (Union[str, bytes]): The plaintext message.
+        signature (Union[str, bytes]): The cryptographic signature to check.
+        certs (Union[Sequence, str, bytes]): The certificate or certificates
+            to use to check the signature.
+        verifier_cls (Optional[~google.auth.crypt.base.Signer]): Which verifier
+            class to use for verification. This can be used to select different
+            algorithms, such as RSA or ECDSA. Default value is :class:`RSAVerifier`.
+
+    Returns:
+        bool: True if the signature is valid, otherwise False.
+    """
+    if isinstance(certs, (str, bytes)):
+        certs = [certs]
+
+    for cert in certs:
+        verifier = verifier_cls.from_string(cert)
+        if verifier.verify(message, signature):
+            return True
+    return False
diff --git a/.venv/lib/python3.12/site-packages/google/auth/crypt/_cryptography_rsa.py b/.venv/lib/python3.12/site-packages/google/auth/crypt/_cryptography_rsa.py
new file mode 100644
index 00000000..1a3e9ff5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/crypt/_cryptography_rsa.py
@@ -0,0 +1,151 @@
+# Copyright 2017 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""RSA verifier and signer that use the ``cryptography`` library.
+
+This is a much faster implementation than the default (in
+``google.auth.crypt._python_rsa``), which depends on the pure-Python
+``rsa`` library.
+"""
+
+import cryptography.exceptions
+from cryptography.hazmat import backends
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric import padding
+import cryptography.x509
+
+from google.auth import _helpers
+from google.auth.crypt import base
+
+_CERTIFICATE_MARKER = b"-----BEGIN CERTIFICATE-----"
+_BACKEND = backends.default_backend()
+_PADDING = padding.PKCS1v15()
+_SHA256 = hashes.SHA256()
+
+
+class RSAVerifier(base.Verifier):
+    """Verifies RSA cryptographic signatures using public keys.
+
+    Args:
+        public_key (
+                cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey):
+            The public key used to verify signatures.
+    """
+
+    def __init__(self, public_key):
+        self._pubkey = public_key
+
+    @_helpers.copy_docstring(base.Verifier)
+    def verify(self, message, signature):
+        message = _helpers.to_bytes(message)
+        try:
+            self._pubkey.verify(signature, message, _PADDING, _SHA256)
+            return True
+        except (ValueError, cryptography.exceptions.InvalidSignature):
+            return False
+
+    @classmethod
+    def from_string(cls, public_key):
+        """Construct an Verifier instance from a public key or public
+        certificate string.
+
+        Args:
+            public_key (Union[str, bytes]): The public key in PEM format or the
+                x509 public key certificate.
+
+        Returns:
+            Verifier: The constructed verifier.
+
+        Raises:
+            ValueError: If the public key can't be parsed.
+        """
+        public_key_data = _helpers.to_bytes(public_key)
+
+        if _CERTIFICATE_MARKER in public_key_data:
+            cert = cryptography.x509.load_pem_x509_certificate(
+                public_key_data, _BACKEND
+            )
+            pubkey = cert.public_key()
+
+        else:
+            pubkey = serialization.load_pem_public_key(public_key_data, _BACKEND)
+
+        return cls(pubkey)
+
+
+class RSASigner(base.Signer, base.FromServiceAccountMixin):
+    """Signs messages with an RSA private key.
+
+    Args:
+        private_key (
+                cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey):
+            The private key to sign with.
+        key_id (str): Optional key ID used to identify this private key. This
+            can be useful to associate the private key with its associated
+            public key or certificate.
+    """
+
+    def __init__(self, private_key, key_id=None):
+        self._key = private_key
+        self._key_id = key_id
+
+    @property  # type: ignore
+    @_helpers.copy_docstring(base.Signer)
+    def key_id(self):
+        return self._key_id
+
+    @_helpers.copy_docstring(base.Signer)
+    def sign(self, message):
+        message = _helpers.to_bytes(message)
+        return self._key.sign(message, _PADDING, _SHA256)
+
+    @classmethod
+    def from_string(cls, key, key_id=None):
+        """Construct a RSASigner from a private key in PEM format.
+
+        Args:
+            key (Union[bytes, str]): Private key in PEM format.
+            key_id (str): An optional key id used to identify the private key.
+
+        Returns:
+            google.auth.crypt._cryptography_rsa.RSASigner: The
+            constructed signer.
+
+        Raises:
+            ValueError: If ``key`` is not ``bytes`` or ``str`` (unicode).
+            UnicodeDecodeError: If ``key`` is ``bytes`` but cannot be decoded
+                into a UTF-8 ``str``.
+            ValueError: If ``cryptography`` "Could not deserialize key data."
+        """
+        key = _helpers.to_bytes(key)
+        private_key = serialization.load_pem_private_key(
+            key, password=None, backend=_BACKEND
+        )
+        return cls(private_key, key_id=key_id)
+
+    def __getstate__(self):
+        """Pickle helper that serializes the _key attribute."""
+        state = self.__dict__.copy()
+        state["_key"] = self._key.private_bytes(
+            encoding=serialization.Encoding.PEM,
+            format=serialization.PrivateFormat.PKCS8,
+            encryption_algorithm=serialization.NoEncryption(),
+        )
+        return state
+
+    def __setstate__(self, state):
+        """Pickle helper that deserializes the _key attribute."""
+        state["_key"] = serialization.load_pem_private_key(state["_key"], None)
+        self.__dict__.update(state)
diff --git a/.venv/lib/python3.12/site-packages/google/auth/crypt/_helpers.py b/.venv/lib/python3.12/site-packages/google/auth/crypt/_helpers.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/crypt/_helpers.py
diff --git a/.venv/lib/python3.12/site-packages/google/auth/crypt/_python_rsa.py b/.venv/lib/python3.12/site-packages/google/auth/crypt/_python_rsa.py
new file mode 100644
index 00000000..e553c25e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/crypt/_python_rsa.py
@@ -0,0 +1,175 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Pure-Python RSA cryptography implementation.
+
+Uses the ``rsa``, ``pyasn1`` and ``pyasn1_modules`` packages
+to parse PEM files storing PKCS#1 or PKCS#8 keys as well as
+certificates. There is no support for p12 files.
+"""
+
+from __future__ import absolute_import
+
+import io
+
+from pyasn1.codec.der import decoder  # type: ignore
+from pyasn1_modules import pem  # type: ignore
+from pyasn1_modules.rfc2459 import Certificate  # type: ignore
+from pyasn1_modules.rfc5208 import PrivateKeyInfo  # type: ignore
+import rsa  # type: ignore
+
+from google.auth import _helpers
+from google.auth import exceptions
+from google.auth.crypt import base
+
+_POW2 = (128, 64, 32, 16, 8, 4, 2, 1)
+_CERTIFICATE_MARKER = b"-----BEGIN CERTIFICATE-----"
+_PKCS1_MARKER = ("-----BEGIN RSA PRIVATE KEY-----", "-----END RSA PRIVATE KEY-----")
+_PKCS8_MARKER = ("-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----")
+_PKCS8_SPEC = PrivateKeyInfo()
+
+
+def _bit_list_to_bytes(bit_list):
+    """Converts an iterable of 1s and 0s to bytes.
+
+    Combines the list 8 at a time, treating each group of 8 bits
+    as a single byte.
+
+    Args:
+        bit_list (Sequence): Sequence of 1s and 0s.
+
+    Returns:
+        bytes: The decoded bytes.
+    """
+    num_bits = len(bit_list)
+    byte_vals = bytearray()
+    for start in range(0, num_bits, 8):
+        curr_bits = bit_list[start : start + 8]
+        char_val = sum(val * digit for val, digit in zip(_POW2, curr_bits))
+        byte_vals.append(char_val)
+    return bytes(byte_vals)
+
+
+class RSAVerifier(base.Verifier):
+    """Verifies RSA cryptographic signatures using public keys.
+
+    Args:
+        public_key (rsa.key.PublicKey): The public key used to verify
+            signatures.
+    """
+
+    def __init__(self, public_key):
+        self._pubkey = public_key
+
+    @_helpers.copy_docstring(base.Verifier)
+    def verify(self, message, signature):
+        message = _helpers.to_bytes(message)
+        try:
+            return rsa.pkcs1.verify(message, signature, self._pubkey)
+        except (ValueError, rsa.pkcs1.VerificationError):
+            return False
+
+    @classmethod
+    def from_string(cls, public_key):
+        """Construct an Verifier instance from a public key or public
+        certificate string.
+
+        Args:
+            public_key (Union[str, bytes]): The public key in PEM format or the
+                x509 public key certificate.
+
+        Returns:
+            google.auth.crypt._python_rsa.RSAVerifier: The constructed verifier.
+
+        Raises:
+            ValueError: If the public_key can't be parsed.
+        """
+        public_key = _helpers.to_bytes(public_key)
+        is_x509_cert = _CERTIFICATE_MARKER in public_key
+
+        # If this is a certificate, extract the public key info.
+        if is_x509_cert:
+            der = rsa.pem.load_pem(public_key, "CERTIFICATE")
+            asn1_cert, remaining = decoder.decode(der, asn1Spec=Certificate())
+            if remaining != b"":
+                raise exceptions.InvalidValue("Unused bytes", remaining)
+
+            cert_info = asn1_cert["tbsCertificate"]["subjectPublicKeyInfo"]
+            key_bytes = _bit_list_to_bytes(cert_info["subjectPublicKey"])
+            pubkey = rsa.PublicKey.load_pkcs1(key_bytes, "DER")
+        else:
+            pubkey = rsa.PublicKey.load_pkcs1(public_key, "PEM")
+        return cls(pubkey)
+
+
+class RSASigner(base.Signer, base.FromServiceAccountMixin):
+    """Signs messages with an RSA private key.
+
+    Args:
+        private_key (rsa.key.PrivateKey): The private key to sign with.
+        key_id (str): Optional key ID used to identify this private key. This
+            can be useful to associate the private key with its associated
+            public key or certificate.
+    """
+
+    def __init__(self, private_key, key_id=None):
+        self._key = private_key
+        self._key_id = key_id
+
+    @property  # type: ignore
+    @_helpers.copy_docstring(base.Signer)
+    def key_id(self):
+        return self._key_id
+
+    @_helpers.copy_docstring(base.Signer)
+    def sign(self, message):
+        message = _helpers.to_bytes(message)
+        return rsa.pkcs1.sign(message, self._key, "SHA-256")
+
+    @classmethod
+    def from_string(cls, key, key_id=None):
+        """Construct an Signer instance from a private key in PEM format.
+
+        Args:
+            key (str): Private key in PEM format.
+            key_id (str): An optional key id used to identify the private key.
+
+        Returns:
+            google.auth.crypt.Signer: The constructed signer.
+
+        Raises:
+            ValueError: If the key cannot be parsed as PKCS#1 or PKCS#8 in
+                PEM format.
+        """
+        key = _helpers.from_bytes(key)  # PEM expects str in Python 3
+        marker_id, key_bytes = pem.readPemBlocksFromFile(
+            io.StringIO(key), _PKCS1_MARKER, _PKCS8_MARKER
+        )
+
+        # Key is in pkcs1 format.
+        if marker_id == 0:
+            private_key = rsa.key.PrivateKey.load_pkcs1(key_bytes, format="DER")
+        # Key is in pkcs8.
+        elif marker_id == 1:
+            key_info, remaining = decoder.decode(key_bytes, asn1Spec=_PKCS8_SPEC)
+            if remaining != b"":
+                raise exceptions.InvalidValue("Unused bytes", remaining)
+            private_key_info = key_info.getComponentByName("privateKey")
+            private_key = rsa.key.PrivateKey.load_pkcs1(
+                private_key_info.asOctets(), format="DER"
+            )
+        else:
+            raise exceptions.MalformedError("No key could be detected.")
+
+        return cls(private_key, key_id=key_id)
diff --git a/.venv/lib/python3.12/site-packages/google/auth/crypt/base.py b/.venv/lib/python3.12/site-packages/google/auth/crypt/base.py
new file mode 100644
index 00000000..ad871c31
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/crypt/base.py
@@ -0,0 +1,127 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Base classes for cryptographic signers and verifiers."""
+
+import abc
+import io
+import json
+
+from google.auth import exceptions
+
+_JSON_FILE_PRIVATE_KEY = "private_key"
+_JSON_FILE_PRIVATE_KEY_ID = "private_key_id"
+
+
+class Verifier(metaclass=abc.ABCMeta):
+    """Abstract base class for crytographic signature verifiers."""
+
+    @abc.abstractmethod
+    def verify(self, message, signature):
+        """Verifies a message against a cryptographic signature.
+
+        Args:
+            message (Union[str, bytes]): The message to verify.
+            signature (Union[str, bytes]): The cryptography signature to check.
+
+        Returns:
+            bool: True if message was signed by the private key associated
+            with the public key that this object was constructed with.
+        """
+        # pylint: disable=missing-raises-doc,redundant-returns-doc
+        # (pylint doesn't recognize that this is abstract)
+        raise NotImplementedError("Verify must be implemented")
+
+
+class Signer(metaclass=abc.ABCMeta):
+    """Abstract base class for cryptographic signers."""
+
+    @abc.abstractproperty
+    def key_id(self):
+        """Optional[str]: The key ID used to identify this private key."""
+        raise NotImplementedError("Key id must be implemented")
+
+    @abc.abstractmethod
+    def sign(self, message):
+        """Signs a message.
+
+        Args:
+            message (Union[str, bytes]): The message to be signed.
+
+        Returns:
+            bytes: The signature of the message.
+        """
+        # pylint: disable=missing-raises-doc,redundant-returns-doc
+        # (pylint doesn't recognize that this is abstract)
+        raise NotImplementedError("Sign must be implemented")
+
+
+class FromServiceAccountMixin(metaclass=abc.ABCMeta):
+    """Mix-in to enable factory constructors for a Signer."""
+
+    @abc.abstractmethod
+    def from_string(cls, key, key_id=None):
+        """Construct an Signer instance from a private key string.
+
+        Args:
+            key (str): Private key as a string.
+            key_id (str): An optional key id used to identify the private key.
+
+        Returns:
+            google.auth.crypt.Signer: The constructed signer.
+
+        Raises:
+            ValueError: If the key cannot be parsed.
+        """
+        raise NotImplementedError("from_string must be implemented")
+
+    @classmethod
+    def from_service_account_info(cls, info):
+        """Creates a Signer instance instance from a dictionary containing
+        service account info in Google format.
+
+        Args:
+            info (Mapping[str, str]): The service account info in Google
+                format.
+
+        Returns:
+            google.auth.crypt.Signer: The constructed signer.
+
+        Raises:
+            ValueError: If the info is not in the expected format.
+        """
+        if _JSON_FILE_PRIVATE_KEY not in info:
+            raise exceptions.MalformedError(
+                "The private_key field was not found in the service account " "info."
+            )
+
+        return cls.from_string(
+            info[_JSON_FILE_PRIVATE_KEY], info.get(_JSON_FILE_PRIVATE_KEY_ID)
+        )
+
+    @classmethod
+    def from_service_account_file(cls, filename):
+        """Creates a Signer instance from a service account .json file
+        in Google format.
+
+        Args:
+            filename (str): The path to the service account .json file.
+
+        Returns:
+            google.auth.crypt.Signer: The constructed signer.
+        """
+        with io.open(filename, "r", encoding="utf-8") as json_file:
+            data = json.load(json_file)
+
+        return cls.from_service_account_info(data)
diff --git a/.venv/lib/python3.12/site-packages/google/auth/crypt/es256.py b/.venv/lib/python3.12/site-packages/google/auth/crypt/es256.py
new file mode 100644
index 00000000..820e4bec
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/crypt/es256.py
@@ -0,0 +1,175 @@
+# Copyright 2017 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""ECDSA (ES256) verifier and signer that use the ``cryptography`` library.
+"""
+
+from cryptography import utils  # type: ignore
+import cryptography.exceptions
+from cryptography.hazmat import backends
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric import ec
+from cryptography.hazmat.primitives.asymmetric import padding
+from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
+from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature
+import cryptography.x509
+
+from google.auth import _helpers
+from google.auth.crypt import base
+
+
+_CERTIFICATE_MARKER = b"-----BEGIN CERTIFICATE-----"
+_BACKEND = backends.default_backend()
+_PADDING = padding.PKCS1v15()
+
+
+class ES256Verifier(base.Verifier):
+    """Verifies ECDSA cryptographic signatures using public keys.
+
+    Args:
+        public_key (
+                cryptography.hazmat.primitives.asymmetric.ec.ECDSAPublicKey):
+            The public key used to verify signatures.
+    """
+
+    def __init__(self, public_key):
+        self._pubkey = public_key
+
+    @_helpers.copy_docstring(base.Verifier)
+    def verify(self, message, signature):
+        # First convert (r||s) raw signature to ASN1 encoded signature.
+        sig_bytes = _helpers.to_bytes(signature)
+        if len(sig_bytes) != 64:
+            return False
+        r = (
+            int.from_bytes(sig_bytes[:32], byteorder="big")
+            if _helpers.is_python_3()
+            else utils.int_from_bytes(sig_bytes[:32], byteorder="big")
+        )
+        s = (
+            int.from_bytes(sig_bytes[32:], byteorder="big")
+            if _helpers.is_python_3()
+            else utils.int_from_bytes(sig_bytes[32:], byteorder="big")
+        )
+        asn1_sig = encode_dss_signature(r, s)
+
+        message = _helpers.to_bytes(message)
+        try:
+            self._pubkey.verify(asn1_sig, message, ec.ECDSA(hashes.SHA256()))
+            return True
+        except (ValueError, cryptography.exceptions.InvalidSignature):
+            return False
+
+    @classmethod
+    def from_string(cls, public_key):
+        """Construct an Verifier instance from a public key or public
+        certificate string.
+
+        Args:
+            public_key (Union[str, bytes]): The public key in PEM format or the
+                x509 public key certificate.
+
+        Returns:
+            Verifier: The constructed verifier.
+
+        Raises:
+            ValueError: If the public key can't be parsed.
+        """
+        public_key_data = _helpers.to_bytes(public_key)
+
+        if _CERTIFICATE_MARKER in public_key_data:
+            cert = cryptography.x509.load_pem_x509_certificate(
+                public_key_data, _BACKEND
+            )
+            pubkey = cert.public_key()
+
+        else:
+            pubkey = serialization.load_pem_public_key(public_key_data, _BACKEND)
+
+        return cls(pubkey)
+
+
+class ES256Signer(base.Signer, base.FromServiceAccountMixin):
+    """Signs messages with an ECDSA private key.
+
+    Args:
+        private_key (
+                cryptography.hazmat.primitives.asymmetric.ec.ECDSAPrivateKey):
+            The private key to sign with.
+        key_id (str): Optional key ID used to identify this private key. This
+            can be useful to associate the private key with its associated
+            public key or certificate.
+    """
+
+    def __init__(self, private_key, key_id=None):
+        self._key = private_key
+        self._key_id = key_id
+
+    @property  # type: ignore
+    @_helpers.copy_docstring(base.Signer)
+    def key_id(self):
+        return self._key_id
+
+    @_helpers.copy_docstring(base.Signer)
+    def sign(self, message):
+        message = _helpers.to_bytes(message)
+        asn1_signature = self._key.sign(message, ec.ECDSA(hashes.SHA256()))
+
+        # Convert ASN1 encoded signature to (r||s) raw signature.
+        (r, s) = decode_dss_signature(asn1_signature)
+        return (
+            (r.to_bytes(32, byteorder="big") + s.to_bytes(32, byteorder="big"))
+            if _helpers.is_python_3()
+            else (utils.int_to_bytes(r, 32) + utils.int_to_bytes(s, 32))
+        )
+
+    @classmethod
+    def from_string(cls, key, key_id=None):
+        """Construct a RSASigner from a private key in PEM format.
+
+        Args:
+            key (Union[bytes, str]): Private key in PEM format.
+            key_id (str): An optional key id used to identify the private key.
+
+        Returns:
+            google.auth.crypt._cryptography_rsa.RSASigner: The
+            constructed signer.
+
+        Raises:
+            ValueError: If ``key`` is not ``bytes`` or ``str`` (unicode).
+            UnicodeDecodeError: If ``key`` is ``bytes`` but cannot be decoded
+                into a UTF-8 ``str``.
+            ValueError: If ``cryptography`` "Could not deserialize key data."
+        """
+        key = _helpers.to_bytes(key)
+        private_key = serialization.load_pem_private_key(
+            key, password=None, backend=_BACKEND
+        )
+        return cls(private_key, key_id=key_id)
+
+    def __getstate__(self):
+        """Pickle helper that serializes the _key attribute."""
+        state = self.__dict__.copy()
+        state["_key"] = self._key.private_bytes(
+            encoding=serialization.Encoding.PEM,
+            format=serialization.PrivateFormat.PKCS8,
+            encryption_algorithm=serialization.NoEncryption(),
+        )
+        return state
+
+    def __setstate__(self, state):
+        """Pickle helper that deserializes the _key attribute."""
+        state["_key"] = serialization.load_pem_private_key(state["_key"], None)
+        self.__dict__.update(state)
diff --git a/.venv/lib/python3.12/site-packages/google/auth/crypt/rsa.py b/.venv/lib/python3.12/site-packages/google/auth/crypt/rsa.py
new file mode 100644
index 00000000..ed842d1e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/crypt/rsa.py
@@ -0,0 +1,30 @@
+# Copyright 2017 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""RSA cryptography signer and verifier."""
+
+
+try:
+    # Prefer cryptograph-based RSA implementation.
+    from google.auth.crypt import _cryptography_rsa
+
+    RSASigner = _cryptography_rsa.RSASigner
+    RSAVerifier = _cryptography_rsa.RSAVerifier
+except ImportError:  # pragma: NO COVER
+    # Fallback to pure-python RSA implementation if cryptography is
+    # unavailable.
+    from google.auth.crypt import _python_rsa
+
+    RSASigner = _python_rsa.RSASigner  # type: ignore
+    RSAVerifier = _python_rsa.RSAVerifier  # type: ignore
diff --git a/.venv/lib/python3.12/site-packages/google/auth/downscoped.py b/.venv/lib/python3.12/site-packages/google/auth/downscoped.py
new file mode 100644
index 00000000..ea75be90
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/downscoped.py
@@ -0,0 +1,512 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Downscoping with Credential Access Boundaries
+
+This module provides the ability to downscope credentials using
+`Downscoping with Credential Access Boundaries`_. This is useful to restrict the
+Identity and Access Management (IAM) permissions that a short-lived credential
+can use.
+
+To downscope permissions of a source credential, a Credential Access Boundary
+that specifies which resources the new credential can access, as well as
+an upper bound on the permissions that are available on each resource, has to
+be defined. A downscoped credential can then be instantiated using the source
+credential and the Credential Access Boundary.
+
+The common pattern of usage is to have a token broker with elevated access
+generate these downscoped credentials from higher access source credentials and
+pass the downscoped short-lived access tokens to a token consumer via some
+secure authenticated channel for limited access to Google Cloud Storage
+resources.
+
+For example, a token broker can be set up on a server in a private network.
+Various workloads (token consumers) in the same network will send authenticated
+requests to that broker for downscoped tokens to access or modify specific google
+cloud storage buckets.
+
+The broker will instantiate downscoped credentials instances that can be used to
+generate short lived downscoped access tokens that can be passed to the token
+consumer. These downscoped access tokens can be injected by the consumer into
+google.oauth2.Credentials and used to initialize a storage client instance to
+access Google Cloud Storage resources with restricted access.
+
+Note: Only Cloud Storage supports Credential Access Boundaries. Other Google
+Cloud services do not support this feature.
+
+.. _Downscoping with Credential Access Boundaries: https://cloud.google.com/iam/docs/downscoping-short-lived-credentials
+"""
+
+import datetime
+
+from google.auth import _helpers
+from google.auth import credentials
+from google.auth import exceptions
+from google.oauth2 import sts
+
+# The maximum number of access boundary rules a Credential Access Boundary can
+# contain.
+_MAX_ACCESS_BOUNDARY_RULES_COUNT = 10
+# The token exchange grant_type used for exchanging credentials.
+_STS_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange"
+# The token exchange requested_token_type. This is always an access_token.
+_STS_REQUESTED_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token"
+# The STS token URL used to exchanged a short lived access token for a downscoped one.
+_STS_TOKEN_URL_PATTERN = "https://sts.{}/v1/token"
+# The subject token type to use when exchanging a short lived access token for a
+# downscoped token.
+_STS_SUBJECT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token"
+
+
+class CredentialAccessBoundary(object):
+    """Defines a Credential Access Boundary which contains a list of access boundary
+    rules. Each rule contains information on the resource that the rule applies to,
+    the upper bound of the permissions that are available on that resource and an
+    optional condition to further restrict permissions.
+    """
+
+    def __init__(self, rules=[]):
+        """Instantiates a Credential Access Boundary. A Credential Access Boundary
+        can contain up to 10 access boundary rules.
+
+        Args:
+            rules (Sequence[google.auth.downscoped.AccessBoundaryRule]): The list of
+                access boundary rules limiting the access that a downscoped credential
+                will have.
+        Raises:
+            InvalidType: If any of the rules are not a valid type.
+            InvalidValue: If the provided rules exceed the maximum allowed.
+        """
+        self.rules = rules
+
+    @property
+    def rules(self):
+        """Returns the list of access boundary rules defined on the Credential
+        Access Boundary.
+
+        Returns:
+            Tuple[google.auth.downscoped.AccessBoundaryRule, ...]: The list of access
+                boundary rules defined on the Credential Access Boundary. These are returned
+                as an immutable tuple to prevent modification.
+        """
+        return tuple(self._rules)
+
+    @rules.setter
+    def rules(self, value):
+        """Updates the current rules on the Credential Access Boundary. This will overwrite
+        the existing set of rules.
+
+        Args:
+            value (Sequence[google.auth.downscoped.AccessBoundaryRule]): The list of
+                access boundary rules limiting the access that a downscoped credential
+                will have.
+        Raises:
+            InvalidType: If any of the rules are not a valid type.
+            InvalidValue: If the provided rules exceed the maximum allowed.
+        """
+        if len(value) > _MAX_ACCESS_BOUNDARY_RULES_COUNT:
+            raise exceptions.InvalidValue(
+                "Credential access boundary rules can have a maximum of {} rules.".format(
+                    _MAX_ACCESS_BOUNDARY_RULES_COUNT
+                )
+            )
+        for access_boundary_rule in value:
+            if not isinstance(access_boundary_rule, AccessBoundaryRule):
+                raise exceptions.InvalidType(
+                    "List of rules provided do not contain a valid 'google.auth.downscoped.AccessBoundaryRule'."
+                )
+        # Make a copy of the original list.
+        self._rules = list(value)
+
+    def add_rule(self, rule):
+        """Adds a single access boundary rule to the existing rules.
+
+        Args:
+            rule (google.auth.downscoped.AccessBoundaryRule): The access boundary rule,
+                limiting the access that a downscoped credential will have, to be added to
+                the existing rules.
+        Raises:
+            InvalidType: If any of the rules are not a valid type.
+            InvalidValue: If the provided rules exceed the maximum allowed.
+        """
+        if len(self.rules) == _MAX_ACCESS_BOUNDARY_RULES_COUNT:
+            raise exceptions.InvalidValue(
+                "Credential access boundary rules can have a maximum of {} rules.".format(
+                    _MAX_ACCESS_BOUNDARY_RULES_COUNT
+                )
+            )
+        if not isinstance(rule, AccessBoundaryRule):
+            raise exceptions.InvalidType(
+                "The provided rule does not contain a valid 'google.auth.downscoped.AccessBoundaryRule'."
+            )
+        self._rules.append(rule)
+
+    def to_json(self):
+        """Generates the dictionary representation of the Credential Access Boundary.
+        This uses the format expected by the Security Token Service API as documented in
+        `Defining a Credential Access Boundary`_.
+
+        .. _Defining a Credential Access Boundary:
+            https://cloud.google.com/iam/docs/downscoping-short-lived-credentials#define-boundary
+
+        Returns:
+            Mapping: Credential Access Boundary Rule represented in a dictionary object.
+        """
+        rules = []
+        for access_boundary_rule in self.rules:
+            rules.append(access_boundary_rule.to_json())
+
+        return {"accessBoundary": {"accessBoundaryRules": rules}}
+
+
+class AccessBoundaryRule(object):
+    """Defines an access boundary rule which contains information on the resource that
+    the rule applies to, the upper bound of the permissions that are available on that
+    resource and an optional condition to further restrict permissions.
+    """
+
+    def __init__(
+        self, available_resource, available_permissions, availability_condition=None
+    ):
+        """Instantiates a single access boundary rule.
+
+        Args:
+            available_resource (str): The full resource name of the Cloud Storage bucket
+                that the rule applies to. Use the format
+                "//storage.googleapis.com/projects/_/buckets/bucket-name".
+            available_permissions (Sequence[str]): A list defining the upper bound that
+                the downscoped token will have on the available permissions for the
+                resource. Each value is the identifier for an IAM predefined role or
+                custom role, with the prefix "inRole:". For example:
+                "inRole:roles/storage.objectViewer".
+                Only the permissions in these roles will be available.
+            availability_condition (Optional[google.auth.downscoped.AvailabilityCondition]):
+                Optional condition that restricts the availability of permissions to
+                specific Cloud Storage objects.
+
+        Raises:
+            InvalidType: If any of the parameters are not of the expected types.
+            InvalidValue: If any of the parameters are not of the expected values.
+        """
+        self.available_resource = available_resource
+        self.available_permissions = available_permissions
+        self.availability_condition = availability_condition
+
+    @property
+    def available_resource(self):
+        """Returns the current available resource.
+
+        Returns:
+           str: The current available resource.
+        """
+        return self._available_resource
+
+    @available_resource.setter
+    def available_resource(self, value):
+        """Updates the current available resource.
+
+        Args:
+            value (str): The updated value of the available resource.
+
+        Raises:
+            google.auth.exceptions.InvalidType: If the value is not a string.
+        """
+        if not isinstance(value, str):
+            raise exceptions.InvalidType(
+                "The provided available_resource is not a string."
+            )
+        self._available_resource = value
+
+    @property
+    def available_permissions(self):
+        """Returns the current available permissions.
+
+        Returns:
+           Tuple[str, ...]: The current available permissions. These are returned
+               as an immutable tuple to prevent modification.
+        """
+        return tuple(self._available_permissions)
+
+    @available_permissions.setter
+    def available_permissions(self, value):
+        """Updates the current available permissions.
+
+        Args:
+            value (Sequence[str]): The updated value of the available permissions.
+
+        Raises:
+            InvalidType: If the value is not a list of strings.
+            InvalidValue: If the value is not valid.
+        """
+        for available_permission in value:
+            if not isinstance(available_permission, str):
+                raise exceptions.InvalidType(
+                    "Provided available_permissions are not a list of strings."
+                )
+            if available_permission.find("inRole:") != 0:
+                raise exceptions.InvalidValue(
+                    "available_permissions must be prefixed with 'inRole:'."
+                )
+        # Make a copy of the original list.
+        self._available_permissions = list(value)
+
+    @property
+    def availability_condition(self):
+        """Returns the current availability condition.
+
+        Returns:
+           Optional[google.auth.downscoped.AvailabilityCondition]: The current
+               availability condition.
+        """
+        return self._availability_condition
+
+    @availability_condition.setter
+    def availability_condition(self, value):
+        """Updates the current availability condition.
+
+        Args:
+            value (Optional[google.auth.downscoped.AvailabilityCondition]): The updated
+                value of the availability condition.
+
+        Raises:
+            google.auth.exceptions.InvalidType: If the value is not of type google.auth.downscoped.AvailabilityCondition
+                or None.
+        """
+        if not isinstance(value, AvailabilityCondition) and value is not None:
+            raise exceptions.InvalidType(
+                "The provided availability_condition is not a 'google.auth.downscoped.AvailabilityCondition' or None."
+            )
+        self._availability_condition = value
+
+    def to_json(self):
+        """Generates the dictionary representation of the access boundary rule.
+        This uses the format expected by the Security Token Service API as documented in
+        `Defining a Credential Access Boundary`_.
+
+        .. _Defining a Credential Access Boundary:
+            https://cloud.google.com/iam/docs/downscoping-short-lived-credentials#define-boundary
+
+        Returns:
+            Mapping: The access boundary rule represented in a dictionary object.
+        """
+        json = {
+            "availablePermissions": list(self.available_permissions),
+            "availableResource": self.available_resource,
+        }
+        if self.availability_condition:
+            json["availabilityCondition"] = self.availability_condition.to_json()
+        return json
+
+
+class AvailabilityCondition(object):
+    """An optional condition that can be used as part of a Credential Access Boundary
+    to further restrict permissions."""
+
+    def __init__(self, expression, title=None, description=None):
+        """Instantiates an availability condition using the provided expression and
+        optional title or description.
+
+        Args:
+            expression (str): A condition expression that specifies the Cloud Storage
+                objects where permissions are available. For example, this expression
+                makes permissions available for objects whose name starts with "customer-a":
+                "resource.name.startsWith('projects/_/buckets/example-bucket/objects/customer-a')"
+            title (Optional[str]): An optional short string that identifies the purpose of
+                the condition.
+            description (Optional[str]): Optional details about the purpose of the condition.
+
+        Raises:
+            InvalidType: If any of the parameters are not of the expected types.
+            InvalidValue: If any of the parameters are not of the expected values.
+        """
+        self.expression = expression
+        self.title = title
+        self.description = description
+
+    @property
+    def expression(self):
+        """Returns the current condition expression.
+
+        Returns:
+           str: The current conditon expression.
+        """
+        return self._expression
+
+    @expression.setter
+    def expression(self, value):
+        """Updates the current condition expression.
+
+        Args:
+            value (str): The updated value of the condition expression.
+
+        Raises:
+            google.auth.exceptions.InvalidType: If the value is not of type string.
+        """
+        if not isinstance(value, str):
+            raise exceptions.InvalidType("The provided expression is not a string.")
+        self._expression = value
+
+    @property
+    def title(self):
+        """Returns the current title.
+
+        Returns:
+           Optional[str]: The current title.
+        """
+        return self._title
+
+    @title.setter
+    def title(self, value):
+        """Updates the current title.
+
+        Args:
+            value (Optional[str]): The updated value of the title.
+
+        Raises:
+            google.auth.exceptions.InvalidType: If the value is not of type string or None.
+        """
+        if not isinstance(value, str) and value is not None:
+            raise exceptions.InvalidType("The provided title is not a string or None.")
+        self._title = value
+
+    @property
+    def description(self):
+        """Returns the current description.
+
+        Returns:
+           Optional[str]: The current description.
+        """
+        return self._description
+
+    @description.setter
+    def description(self, value):
+        """Updates the current description.
+
+        Args:
+            value (Optional[str]): The updated value of the description.
+
+        Raises:
+            google.auth.exceptions.InvalidType: If the value is not of type string or None.
+        """
+        if not isinstance(value, str) and value is not None:
+            raise exceptions.InvalidType(
+                "The provided description is not a string or None."
+            )
+        self._description = value
+
+    def to_json(self):
+        """Generates the dictionary representation of the availability condition.
+        This uses the format expected by the Security Token Service API as documented in
+        `Defining a Credential Access Boundary`_.
+
+        .. _Defining a Credential Access Boundary:
+            https://cloud.google.com/iam/docs/downscoping-short-lived-credentials#define-boundary
+
+        Returns:
+            Mapping[str, str]: The availability condition represented in a dictionary
+                object.
+        """
+        json = {"expression": self.expression}
+        if self.title:
+            json["title"] = self.title
+        if self.description:
+            json["description"] = self.description
+        return json
+
+
+class Credentials(credentials.CredentialsWithQuotaProject):
+    """Defines a set of Google credentials that are downscoped from an existing set
+    of Google OAuth2 credentials. This is useful to restrict the Identity and Access
+    Management (IAM) permissions that a short-lived credential can use.
+    The common pattern of usage is to have a token broker with elevated access
+    generate these downscoped credentials from higher access source credentials and
+    pass the downscoped short-lived access tokens to a token consumer via some
+    secure authenticated channel for limited access to Google Cloud Storage
+    resources.
+    """
+
+    def __init__(
+        self,
+        source_credentials,
+        credential_access_boundary,
+        quota_project_id=None,
+        universe_domain=credentials.DEFAULT_UNIVERSE_DOMAIN,
+    ):
+        """Instantiates a downscoped credentials object using the provided source
+        credentials and credential access boundary rules.
+        To downscope permissions of a source credential, a Credential Access Boundary
+        that specifies which resources the new credential can access, as well as an
+        upper bound on the permissions that are available on each resource, has to be
+        defined. A downscoped credential can then be instantiated using the source
+        credential and the Credential Access Boundary.
+
+        Args:
+            source_credentials (google.auth.credentials.Credentials): The source credentials
+                to be downscoped based on the provided Credential Access Boundary rules.
+            credential_access_boundary (google.auth.downscoped.CredentialAccessBoundary):
+                The Credential Access Boundary which contains a list of access boundary
+                rules. Each rule contains information on the resource that the rule applies to,
+                the upper bound of the permissions that are available on that resource and an
+                optional condition to further restrict permissions.
+            quota_project_id (Optional[str]): The optional quota project ID.
+            universe_domain (Optional[str]): The universe domain value, default is googleapis.com
+        Raises:
+            google.auth.exceptions.RefreshError: If the source credentials
+                return an error on token refresh.
+            google.auth.exceptions.OAuthError: If the STS token exchange
+                endpoint returned an error during downscoped token generation.
+        """
+
+        super(Credentials, self).__init__()
+        self._source_credentials = source_credentials
+        self._credential_access_boundary = credential_access_boundary
+        self._quota_project_id = quota_project_id
+        self._universe_domain = universe_domain or credentials.DEFAULT_UNIVERSE_DOMAIN
+        self._sts_client = sts.Client(
+            _STS_TOKEN_URL_PATTERN.format(self.universe_domain)
+        )
+
+    @_helpers.copy_docstring(credentials.Credentials)
+    def refresh(self, request):
+        # Generate an access token from the source credentials.
+        self._source_credentials.refresh(request)
+        now = _helpers.utcnow()
+        # Exchange the access token for a downscoped access token.
+        response_data = self._sts_client.exchange_token(
+            request=request,
+            grant_type=_STS_GRANT_TYPE,
+            subject_token=self._source_credentials.token,
+            subject_token_type=_STS_SUBJECT_TOKEN_TYPE,
+            requested_token_type=_STS_REQUESTED_TOKEN_TYPE,
+            additional_options=self._credential_access_boundary.to_json(),
+        )
+        self.token = response_data.get("access_token")
+        # For downscoping CAB flow, the STS endpoint may not return the expiration
+        # field for some flows. The generated downscoped token should always have
+        # the same expiration time as the source credentials. When no expires_in
+        # field is returned in the response, we can just get the expiration time
+        # from the source credentials.
+        if response_data.get("expires_in"):
+            lifetime = datetime.timedelta(seconds=response_data.get("expires_in"))
+            self.expiry = now + lifetime
+        else:
+            self.expiry = self._source_credentials.expiry
+
+    @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
+    def with_quota_project(self, quota_project_id):
+        return self.__class__(
+            self._source_credentials,
+            self._credential_access_boundary,
+            quota_project_id=quota_project_id,
+        )
diff --git a/.venv/lib/python3.12/site-packages/google/auth/environment_vars.py b/.venv/lib/python3.12/site-packages/google/auth/environment_vars.py
new file mode 100644
index 00000000..81f31571
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/environment_vars.py
@@ -0,0 +1,84 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Environment variables used by :mod:`google.auth`."""
+
+
+PROJECT = "GOOGLE_CLOUD_PROJECT"
+"""Environment variable defining default project.
+
+This used by :func:`google.auth.default` to explicitly set a project ID. This
+environment variable is also used by the Google Cloud Python Library.
+"""
+
+LEGACY_PROJECT = "GCLOUD_PROJECT"
+"""Previously used environment variable defining the default project.
+
+This environment variable is used instead of the current one in some
+situations (such as Google App Engine).
+"""
+
+GOOGLE_CLOUD_QUOTA_PROJECT = "GOOGLE_CLOUD_QUOTA_PROJECT"
+"""Environment variable defining the project to be used for
+quota and billing."""
+
+CREDENTIALS = "GOOGLE_APPLICATION_CREDENTIALS"
+"""Environment variable defining the location of Google application default
+credentials."""
+
+# The environment variable name which can replace ~/.config if set.
+CLOUD_SDK_CONFIG_DIR = "CLOUDSDK_CONFIG"
+"""Environment variable defines the location of Google Cloud SDK's config
+files."""
+
+# These two variables allow for customization of the addresses used when
+# contacting the GCE metadata service.
+GCE_METADATA_HOST = "GCE_METADATA_HOST"
+"""Environment variable providing an alternate hostname or host:port to be
+used for GCE metadata requests.
+
+This environment variable was originally named GCE_METADATA_ROOT. The system will
+check this environemnt variable first; should there be no value present,
+the system will fall back to the old variable.
+"""
+
+GCE_METADATA_ROOT = "GCE_METADATA_ROOT"
+"""Old environment variable for GCE_METADATA_HOST."""
+
+GCE_METADATA_IP = "GCE_METADATA_IP"
+"""Environment variable providing an alternate ip:port to be used for ip-only
+GCE metadata requests."""
+
+GOOGLE_API_USE_CLIENT_CERTIFICATE = "GOOGLE_API_USE_CLIENT_CERTIFICATE"
+"""Environment variable controlling whether to use client certificate or not.
+
+The default value is false. Users have to explicitly set this value to true
+in order to use client certificate to establish a mutual TLS channel."""
+
+LEGACY_APPENGINE_RUNTIME = "APPENGINE_RUNTIME"
+"""Gen1 environment variable defining the App Engine Runtime.
+
+Used to distinguish between GAE gen1 and GAE gen2+.
+"""
+
+# AWS environment variables used with AWS workload identity pools to retrieve
+# AWS security credentials and the AWS region needed to create a serialized
+# signed requests to the AWS STS GetCalledIdentity API that can be exchanged
+# for a Google access tokens via the GCP STS endpoint.
+# When not available the AWS metadata server is used to retrieve these values.
+AWS_ACCESS_KEY_ID = "AWS_ACCESS_KEY_ID"
+AWS_SECRET_ACCESS_KEY = "AWS_SECRET_ACCESS_KEY"
+AWS_SESSION_TOKEN = "AWS_SESSION_TOKEN"
+AWS_REGION = "AWS_REGION"
+AWS_DEFAULT_REGION = "AWS_DEFAULT_REGION"
diff --git a/.venv/lib/python3.12/site-packages/google/auth/exceptions.py b/.venv/lib/python3.12/site-packages/google/auth/exceptions.py
new file mode 100644
index 00000000..feb9f741
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/exceptions.py
@@ -0,0 +1,108 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Exceptions used in the google.auth package."""
+
+
+class GoogleAuthError(Exception):
+    """Base class for all google.auth errors."""
+
+    def __init__(self, *args, **kwargs):
+        super(GoogleAuthError, self).__init__(*args)
+        retryable = kwargs.get("retryable", False)
+        self._retryable = retryable
+
+    @property
+    def retryable(self):
+        return self._retryable
+
+
+class TransportError(GoogleAuthError):
+    """Used to indicate an error occurred during an HTTP request."""
+
+
+class RefreshError(GoogleAuthError):
+    """Used to indicate that an refreshing the credentials' access token
+    failed."""
+
+
+class UserAccessTokenError(GoogleAuthError):
+    """Used to indicate ``gcloud auth print-access-token`` command failed."""
+
+
+class DefaultCredentialsError(GoogleAuthError):
+    """Used to indicate that acquiring default credentials failed."""
+
+
+class MutualTLSChannelError(GoogleAuthError):
+    """Used to indicate that mutual TLS channel creation is failed, or mutual
+    TLS channel credentials is missing or invalid."""
+
+
+class ClientCertError(GoogleAuthError):
+    """Used to indicate that client certificate is missing or invalid."""
+
+    @property
+    def retryable(self):
+        return False
+
+
+class OAuthError(GoogleAuthError):
+    """Used to indicate an error occurred during an OAuth related HTTP
+    request."""
+
+
+class ReauthFailError(RefreshError):
+    """An exception for when reauth failed."""
+
+    def __init__(self, message=None, **kwargs):
+        super(ReauthFailError, self).__init__(
+            "Reauthentication failed. {0}".format(message), **kwargs
+        )
+
+
+class ReauthSamlChallengeFailError(ReauthFailError):
+    """An exception for SAML reauth challenge failures."""
+
+
+class MalformedError(DefaultCredentialsError, ValueError):
+    """An exception for malformed data."""
+
+
+class InvalidResource(DefaultCredentialsError, ValueError):
+    """An exception for URL error."""
+
+
+class InvalidOperation(DefaultCredentialsError, ValueError):
+    """An exception for invalid operation."""
+
+
+class InvalidValue(DefaultCredentialsError, ValueError):
+    """Used to wrap general ValueError of python."""
+
+
+class InvalidType(DefaultCredentialsError, TypeError):
+    """Used to wrap general TypeError of python."""
+
+
+class OSError(DefaultCredentialsError, EnvironmentError):
+    """Used to wrap EnvironmentError(OSError after python3.3)."""
+
+
+class TimeoutError(GoogleAuthError):
+    """Used to indicate a timeout error occurred during an HTTP request."""
+
+
+class ResponseError(GoogleAuthError):
+    """Used to indicate an error occurred when reading an HTTP response."""
diff --git a/.venv/lib/python3.12/site-packages/google/auth/external_account.py b/.venv/lib/python3.12/site-packages/google/auth/external_account.py
new file mode 100644
index 00000000..161e6c50
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/external_account.py
@@ -0,0 +1,628 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""External Account Credentials.
+
+This module provides credentials that exchange workload identity pool external
+credentials for Google access tokens. This facilitates accessing Google Cloud
+Platform resources from on-prem and non-Google Cloud platforms (e.g. AWS,
+Microsoft Azure, OIDC identity providers), using native credentials retrieved
+from the current environment without the need to copy, save and manage
+long-lived service account credentials.
+
+Specifically, this is intended to use access tokens acquired using the GCP STS
+token exchange endpoint following the `OAuth 2.0 Token Exchange`_ spec.
+
+.. _OAuth 2.0 Token Exchange: https://tools.ietf.org/html/rfc8693
+"""
+
+import abc
+import copy
+from dataclasses import dataclass
+import datetime
+import functools
+import io
+import json
+import re
+
+from google.auth import _helpers
+from google.auth import credentials
+from google.auth import exceptions
+from google.auth import impersonated_credentials
+from google.auth import metrics
+from google.oauth2 import sts
+from google.oauth2 import utils
+
+# External account JSON type identifier.
+_EXTERNAL_ACCOUNT_JSON_TYPE = "external_account"
+# The token exchange grant_type used for exchanging credentials.
+_STS_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange"
+# The token exchange requested_token_type. This is always an access_token.
+_STS_REQUESTED_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:access_token"
+# Cloud resource manager URL used to retrieve project information.
+_CLOUD_RESOURCE_MANAGER = "https://cloudresourcemanager.googleapis.com/v1/projects/"
+# Default Google sts token url.
+_DEFAULT_TOKEN_URL = "https://sts.{universe_domain}/v1/token"
+
+
+@dataclass
+class SupplierContext:
+    """A context class that contains information about the requested third party credential that is passed
+        to AWS security credential and subject token suppliers.
+
+        Attributes:
+            subject_token_type (str): The requested subject token type based on the Oauth2.0 token exchange spec.
+                Expected values include::
+
+                    “urn:ietf:params:oauth:token-type:jwt”
+                    “urn:ietf:params:oauth:token-type:id-token”
+                    “urn:ietf:params:oauth:token-type:saml2”
+                    “urn:ietf:params:aws:token-type:aws4_request”
+
+            audience (str): The requested audience for the subject token.
+    """
+
+    subject_token_type: str
+    audience: str
+
+
+class Credentials(
+    credentials.Scoped,
+    credentials.CredentialsWithQuotaProject,
+    credentials.CredentialsWithTokenUri,
+    metaclass=abc.ABCMeta,
+):
+    """Base class for all external account credentials.
+
+    This is used to instantiate Credentials for exchanging external account
+    credentials for Google access token and authorizing requests to Google APIs.
+    The base class implements the common logic for exchanging external account
+    credentials for Google access tokens.
+    """
+
+    def __init__(
+        self,
+        audience,
+        subject_token_type,
+        token_url,
+        credential_source,
+        service_account_impersonation_url=None,
+        service_account_impersonation_options=None,
+        client_id=None,
+        client_secret=None,
+        token_info_url=None,
+        quota_project_id=None,
+        scopes=None,
+        default_scopes=None,
+        workforce_pool_user_project=None,
+        universe_domain=credentials.DEFAULT_UNIVERSE_DOMAIN,
+        trust_boundary=None,
+    ):
+        """Instantiates an external account credentials object.
+
+        Args:
+            audience (str): The STS audience field.
+            subject_token_type (str): The subject token type based on the Oauth2.0 token exchange spec.
+                Expected values include::
+
+                    “urn:ietf:params:oauth:token-type:jwt”
+                    “urn:ietf:params:oauth:token-type:id-token”
+                    “urn:ietf:params:oauth:token-type:saml2”
+                    “urn:ietf:params:aws:token-type:aws4_request”
+
+            token_url (str): The STS endpoint URL.
+            credential_source (Mapping): The credential source dictionary.
+            service_account_impersonation_url (Optional[str]): The optional service account
+                impersonation generateAccessToken URL.
+            client_id (Optional[str]): The optional client ID.
+            client_secret (Optional[str]): The optional client secret.
+            token_info_url (str): The optional STS endpoint URL for token introspection.
+            quota_project_id (Optional[str]): The optional quota project ID.
+            scopes (Optional[Sequence[str]]): Optional scopes to request during the
+                authorization grant.
+            default_scopes (Optional[Sequence[str]]): Default scopes passed by a
+                Google client library. Use 'scopes' for user-defined scopes.
+            workforce_pool_user_project (Optona[str]): The optional workforce pool user
+                project number when the credential corresponds to a workforce pool and not
+                a workload identity pool. The underlying principal must still have
+                serviceusage.services.use IAM permission to use the project for
+                billing/quota.
+            universe_domain (str): The universe domain. The default universe
+                domain is googleapis.com.
+            trust_boundary (str): String representation of trust boundary meta.
+        Raises:
+            google.auth.exceptions.RefreshError: If the generateAccessToken
+                endpoint returned an error.
+        """
+        super(Credentials, self).__init__()
+        self._audience = audience
+        self._subject_token_type = subject_token_type
+        self._universe_domain = universe_domain
+        self._token_url = token_url
+        if self._token_url == _DEFAULT_TOKEN_URL:
+            self._token_url = self._token_url.replace(
+                "{universe_domain}", self._universe_domain
+            )
+        self._token_info_url = token_info_url
+        self._credential_source = credential_source
+        self._service_account_impersonation_url = service_account_impersonation_url
+        self._service_account_impersonation_options = (
+            service_account_impersonation_options or {}
+        )
+        self._client_id = client_id
+        self._client_secret = client_secret
+        self._quota_project_id = quota_project_id
+        self._scopes = scopes
+        self._default_scopes = default_scopes
+        self._workforce_pool_user_project = workforce_pool_user_project
+        self._trust_boundary = {
+            "locations": [],
+            "encoded_locations": "0x0",
+        }  # expose a placeholder trust boundary value.
+
+        if self._client_id:
+            self._client_auth = utils.ClientAuthentication(
+                utils.ClientAuthType.basic, self._client_id, self._client_secret
+            )
+        else:
+            self._client_auth = None
+        self._sts_client = sts.Client(self._token_url, self._client_auth)
+
+        self._metrics_options = self._create_default_metrics_options()
+
+        self._impersonated_credentials = None
+        self._project_id = None
+        self._supplier_context = SupplierContext(
+            self._subject_token_type, self._audience
+        )
+        self._cred_file_path = None
+
+        if not self.is_workforce_pool and self._workforce_pool_user_project:
+            # Workload identity pools do not support workforce pool user projects.
+            raise exceptions.InvalidValue(
+                "workforce_pool_user_project should not be set for non-workforce pool "
+                "credentials"
+            )
+
+    @property
+    def info(self):
+        """Generates the dictionary representation of the current credentials.
+
+        Returns:
+            Mapping: The dictionary representation of the credentials. This is the
+                reverse of "from_info" defined on the subclasses of this class. It is
+                useful for serializing the current credentials so it can deserialized
+                later.
+        """
+        config_info = self._constructor_args()
+        config_info.update(
+            type=_EXTERNAL_ACCOUNT_JSON_TYPE,
+            service_account_impersonation=config_info.pop(
+                "service_account_impersonation_options", None
+            ),
+        )
+        config_info.pop("scopes", None)
+        config_info.pop("default_scopes", None)
+        return {key: value for key, value in config_info.items() if value is not None}
+
+    def _constructor_args(self):
+        args = {
+            "audience": self._audience,
+            "subject_token_type": self._subject_token_type,
+            "token_url": self._token_url,
+            "token_info_url": self._token_info_url,
+            "service_account_impersonation_url": self._service_account_impersonation_url,
+            "service_account_impersonation_options": copy.deepcopy(
+                self._service_account_impersonation_options
+            )
+            or None,
+            "credential_source": copy.deepcopy(self._credential_source),
+            "quota_project_id": self._quota_project_id,
+            "client_id": self._client_id,
+            "client_secret": self._client_secret,
+            "workforce_pool_user_project": self._workforce_pool_user_project,
+            "scopes": self._scopes,
+            "default_scopes": self._default_scopes,
+            "universe_domain": self._universe_domain,
+        }
+        if not self.is_workforce_pool:
+            args.pop("workforce_pool_user_project")
+        return args
+
+    @property
+    def service_account_email(self):
+        """Returns the service account email if service account impersonation is used.
+
+        Returns:
+            Optional[str]: The service account email if impersonation is used. Otherwise
+                None is returned.
+        """
+        if self._service_account_impersonation_url:
+            # Parse email from URL. The formal looks as follows:
+            # https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/name@project-id.iam.gserviceaccount.com:generateAccessToken
+            url = self._service_account_impersonation_url
+            start_index = url.rfind("/")
+            end_index = url.find(":generateAccessToken")
+            if start_index != -1 and end_index != -1 and start_index < end_index:
+                start_index = start_index + 1
+                return url[start_index:end_index]
+        return None
+
+    @property
+    def is_user(self):
+        """Returns whether the credentials represent a user (True) or workload (False).
+        Workloads behave similarly to service accounts. Currently workloads will use
+        service account impersonation but will eventually not require impersonation.
+        As a result, this property is more reliable than the service account email
+        property in determining if the credentials represent a user or workload.
+
+        Returns:
+            bool: True if the credentials represent a user. False if they represent a
+                workload.
+        """
+        # If service account impersonation is used, the credentials will always represent a
+        # service account.
+        if self._service_account_impersonation_url:
+            return False
+        return self.is_workforce_pool
+
+    @property
+    def is_workforce_pool(self):
+        """Returns whether the credentials represent a workforce pool (True) or
+        workload (False) based on the credentials' audience.
+
+        This will also return True for impersonated workforce pool credentials.
+
+        Returns:
+            bool: True if the credentials represent a workforce pool. False if they
+                represent a workload.
+        """
+        # Workforce pools representing users have the following audience format:
+        # //iam.googleapis.com/locations/$location/workforcePools/$poolId/providers/$providerId
+        p = re.compile(r"//iam\.googleapis\.com/locations/[^/]+/workforcePools/")
+        return p.match(self._audience or "") is not None
+
+    @property
+    def requires_scopes(self):
+        """Checks if the credentials requires scopes.
+
+        Returns:
+            bool: True if there are no scopes set otherwise False.
+        """
+        return not self._scopes and not self._default_scopes
+
+    @property
+    def project_number(self):
+        """Optional[str]: The project number corresponding to the workload identity pool."""
+
+        # STS audience pattern:
+        # //iam.googleapis.com/projects/$PROJECT_NUMBER/locations/...
+        components = self._audience.split("/")
+        try:
+            project_index = components.index("projects")
+            if project_index + 1 < len(components):
+                return components[project_index + 1] or None
+        except ValueError:
+            return None
+
+    @property
+    def token_info_url(self):
+        """Optional[str]: The STS token introspection endpoint."""
+
+        return self._token_info_url
+
+    @_helpers.copy_docstring(credentials.Credentials)
+    def get_cred_info(self):
+        if self._cred_file_path:
+            cred_info_json = {
+                "credential_source": self._cred_file_path,
+                "credential_type": "external account credentials",
+            }
+            if self.service_account_email:
+                cred_info_json["principal"] = self.service_account_email
+            return cred_info_json
+        return None
+
+    @_helpers.copy_docstring(credentials.Scoped)
+    def with_scopes(self, scopes, default_scopes=None):
+        kwargs = self._constructor_args()
+        kwargs.update(scopes=scopes, default_scopes=default_scopes)
+        scoped = self.__class__(**kwargs)
+        scoped._cred_file_path = self._cred_file_path
+        scoped._metrics_options = self._metrics_options
+        return scoped
+
+    @abc.abstractmethod
+    def retrieve_subject_token(self, request):
+        """Retrieves the subject token using the credential_source object.
+
+        Args:
+            request (google.auth.transport.Request): A callable used to make
+                HTTP requests.
+        Returns:
+            str: The retrieved subject token.
+        """
+        # pylint: disable=missing-raises-doc
+        # (pylint doesn't recognize that this is abstract)
+        raise NotImplementedError("retrieve_subject_token must be implemented")
+
+    def get_project_id(self, request):
+        """Retrieves the project ID corresponding to the workload identity or workforce pool.
+        For workforce pool credentials, it returns the project ID corresponding to
+        the workforce_pool_user_project.
+
+        When not determinable, None is returned.
+
+        This is introduced to support the current pattern of using the Auth library:
+
+            credentials, project_id = google.auth.default()
+
+        The resource may not have permission (resourcemanager.projects.get) to
+        call this API or the required scopes may not be selected:
+        https://cloud.google.com/resource-manager/reference/rest/v1/projects/get#authorization-scopes
+
+        Args:
+            request (google.auth.transport.Request): A callable used to make
+                HTTP requests.
+        Returns:
+            Optional[str]: The project ID corresponding to the workload identity pool
+                or workforce pool if determinable.
+        """
+        if self._project_id:
+            # If already retrieved, return the cached project ID value.
+            return self._project_id
+        scopes = self._scopes if self._scopes is not None else self._default_scopes
+        # Scopes are required in order to retrieve a valid access token.
+        project_number = self.project_number or self._workforce_pool_user_project
+        if project_number and scopes:
+            headers = {}
+            url = _CLOUD_RESOURCE_MANAGER + project_number
+            self.before_request(request, "GET", url, headers)
+            response = request(url=url, method="GET", headers=headers)
+
+            response_body = (
+                response.data.decode("utf-8")
+                if hasattr(response.data, "decode")
+                else response.data
+            )
+            response_data = json.loads(response_body)
+
+            if response.status == 200:
+                # Cache result as this field is immutable.
+                self._project_id = response_data.get("projectId")
+                return self._project_id
+
+        return None
+
+    @_helpers.copy_docstring(credentials.Credentials)
+    def refresh(self, request):
+        scopes = self._scopes if self._scopes is not None else self._default_scopes
+
+        # Inject client certificate into request.
+        if self._mtls_required():
+            request = functools.partial(
+                request, cert=self._get_mtls_cert_and_key_paths()
+            )
+
+        if self._should_initialize_impersonated_credentials():
+            self._impersonated_credentials = self._initialize_impersonated_credentials()
+
+        if self._impersonated_credentials:
+            self._impersonated_credentials.refresh(request)
+            self.token = self._impersonated_credentials.token
+            self.expiry = self._impersonated_credentials.expiry
+        else:
+            now = _helpers.utcnow()
+            additional_options = None
+            # Do not pass workforce_pool_user_project when client authentication
+            # is used. The client ID is sufficient for determining the user project.
+            if self._workforce_pool_user_project and not self._client_id:
+                additional_options = {"userProject": self._workforce_pool_user_project}
+            additional_headers = {
+                metrics.API_CLIENT_HEADER: metrics.byoid_metrics_header(
+                    self._metrics_options
+                )
+            }
+            response_data = self._sts_client.exchange_token(
+                request=request,
+                grant_type=_STS_GRANT_TYPE,
+                subject_token=self.retrieve_subject_token(request),
+                subject_token_type=self._subject_token_type,
+                audience=self._audience,
+                scopes=scopes,
+                requested_token_type=_STS_REQUESTED_TOKEN_TYPE,
+                additional_options=additional_options,
+                additional_headers=additional_headers,
+            )
+            self.token = response_data.get("access_token")
+            expires_in = response_data.get("expires_in")
+            # Some services do not respect the OAUTH2.0 RFC and send expires_in as a
+            # JSON String.
+            if isinstance(expires_in, str):
+                expires_in = int(expires_in)
+
+            lifetime = datetime.timedelta(seconds=expires_in)
+
+            self.expiry = now + lifetime
+
+    def _make_copy(self):
+        kwargs = self._constructor_args()
+        new_cred = self.__class__(**kwargs)
+        new_cred._cred_file_path = self._cred_file_path
+        new_cred._metrics_options = self._metrics_options
+        return new_cred
+
+    @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
+    def with_quota_project(self, quota_project_id):
+        # Return copy of instance with the provided quota project ID.
+        cred = self._make_copy()
+        cred._quota_project_id = quota_project_id
+        return cred
+
+    @_helpers.copy_docstring(credentials.CredentialsWithTokenUri)
+    def with_token_uri(self, token_uri):
+        cred = self._make_copy()
+        cred._token_url = token_uri
+        return cred
+
+    @_helpers.copy_docstring(credentials.CredentialsWithUniverseDomain)
+    def with_universe_domain(self, universe_domain):
+        cred = self._make_copy()
+        cred._universe_domain = universe_domain
+        return cred
+
+    def _should_initialize_impersonated_credentials(self):
+        return (
+            self._service_account_impersonation_url is not None
+            and self._impersonated_credentials is None
+        )
+
+    def _initialize_impersonated_credentials(self):
+        """Generates an impersonated credentials.
+
+        For more details, see `projects.serviceAccounts.generateAccessToken`_.
+
+        .. _projects.serviceAccounts.generateAccessToken: https://cloud.google.com/iam/docs/reference/credentials/rest/v1/projects.serviceAccounts/generateAccessToken
+
+        Returns:
+            impersonated_credentials.Credential: The impersonated credentials
+                object.
+
+        Raises:
+            google.auth.exceptions.RefreshError: If the generateAccessToken
+                endpoint returned an error.
+        """
+        # Return copy of instance with no service account impersonation.
+        kwargs = self._constructor_args()
+        kwargs.update(
+            service_account_impersonation_url=None,
+            service_account_impersonation_options={},
+        )
+        source_credentials = self.__class__(**kwargs)
+        source_credentials._metrics_options = self._metrics_options
+
+        # Determine target_principal.
+        target_principal = self.service_account_email
+        if not target_principal:
+            raise exceptions.RefreshError(
+                "Unable to determine target principal from service account impersonation URL."
+            )
+
+        scopes = self._scopes if self._scopes is not None else self._default_scopes
+        # Initialize and return impersonated credentials.
+        return impersonated_credentials.Credentials(
+            source_credentials=source_credentials,
+            target_principal=target_principal,
+            target_scopes=scopes,
+            quota_project_id=self._quota_project_id,
+            iam_endpoint_override=self._service_account_impersonation_url,
+            lifetime=self._service_account_impersonation_options.get(
+                "token_lifetime_seconds"
+            ),
+        )
+
+    def _create_default_metrics_options(self):
+        metrics_options = {}
+        if self._service_account_impersonation_url:
+            metrics_options["sa-impersonation"] = "true"
+        else:
+            metrics_options["sa-impersonation"] = "false"
+        if self._service_account_impersonation_options.get("token_lifetime_seconds"):
+            metrics_options["config-lifetime"] = "true"
+        else:
+            metrics_options["config-lifetime"] = "false"
+
+        return metrics_options
+
+    def _mtls_required(self):
+        """Returns a boolean representing whether the current credential is configured
+        for mTLS and should add a certificate to the outgoing calls to the sts and service
+        account impersonation endpoint.
+
+        Returns:
+            bool: True if the credential is configured for mTLS, False if it is not.
+        """
+        return False
+
+    def _get_mtls_cert_and_key_paths(self):
+        """Gets the file locations for a certificate and private key file
+        to be used for configuring mTLS for the sts and service account
+        impersonation calls. Currently only expected to return a value when using
+        X509 workload identity federation.
+
+        Returns:
+            Tuple[str, str]: The cert and key file locations as strings in a tuple.
+
+        Raises:
+            NotImplementedError: When the current credential is not configured for
+                mTLS.
+        """
+        raise NotImplementedError(
+            "_get_mtls_cert_and_key_location must be implemented."
+        )
+
+    @classmethod
+    def from_info(cls, info, **kwargs):
+        """Creates a Credentials instance from parsed external account info.
+
+        Args:
+            info (Mapping[str, str]): The external account info in Google
+                format.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.identity_pool.Credentials: The constructed
+                credentials.
+
+        Raises:
+            InvalidValue: For invalid parameters.
+        """
+        return cls(
+            audience=info.get("audience"),
+            subject_token_type=info.get("subject_token_type"),
+            token_url=info.get("token_url"),
+            token_info_url=info.get("token_info_url"),
+            service_account_impersonation_url=info.get(
+                "service_account_impersonation_url"
+            ),
+            service_account_impersonation_options=info.get(
+                "service_account_impersonation"
+            )
+            or {},
+            client_id=info.get("client_id"),
+            client_secret=info.get("client_secret"),
+            credential_source=info.get("credential_source"),
+            quota_project_id=info.get("quota_project_id"),
+            workforce_pool_user_project=info.get("workforce_pool_user_project"),
+            universe_domain=info.get(
+                "universe_domain", credentials.DEFAULT_UNIVERSE_DOMAIN
+            ),
+            **kwargs
+        )
+
+    @classmethod
+    def from_file(cls, filename, **kwargs):
+        """Creates a Credentials instance from an external account json file.
+
+        Args:
+            filename (str): The path to the external account json file.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.identity_pool.Credentials: The constructed
+                credentials.
+        """
+        with io.open(filename, "r", encoding="utf-8") as json_file:
+            data = json.load(json_file)
+            return cls.from_info(data, **kwargs)
diff --git a/.venv/lib/python3.12/site-packages/google/auth/external_account_authorized_user.py b/.venv/lib/python3.12/site-packages/google/auth/external_account_authorized_user.py
new file mode 100644
index 00000000..4d0c3c68
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/external_account_authorized_user.py
@@ -0,0 +1,380 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""External Account Authorized User Credentials.
+This module provides credentials based on OAuth 2.0 access and refresh tokens.
+These credentials usually access resources on behalf of a user (resource
+owner).
+
+Specifically, these are sourced using external identities via Workforce Identity Federation.
+
+Obtaining the initial access and refresh token can be done through the Google Cloud CLI.
+
+Example credential:
+{
+  "type": "external_account_authorized_user",
+  "audience": "//iam.googleapis.com/locations/global/workforcePools/$WORKFORCE_POOL_ID/providers/$PROVIDER_ID",
+  "refresh_token": "refreshToken",
+  "token_url": "https://sts.googleapis.com/v1/oauth/token",
+  "token_info_url": "https://sts.googleapis.com/v1/instrospect",
+  "client_id": "clientId",
+  "client_secret": "clientSecret"
+}
+"""
+
+import datetime
+import io
+import json
+
+from google.auth import _helpers
+from google.auth import credentials
+from google.auth import exceptions
+from google.oauth2 import sts
+from google.oauth2 import utils
+
+_EXTERNAL_ACCOUNT_AUTHORIZED_USER_JSON_TYPE = "external_account_authorized_user"
+
+
+class Credentials(
+    credentials.CredentialsWithQuotaProject,
+    credentials.ReadOnlyScoped,
+    credentials.CredentialsWithTokenUri,
+):
+    """Credentials for External Account Authorized Users.
+
+    This is used to instantiate Credentials for exchanging refresh tokens from
+    authorized users for Google access token and authorizing requests to Google
+    APIs.
+
+    The credentials are considered immutable. If you want to modify the
+    quota project, use `with_quota_project` and if you want to modify the token
+    uri, use `with_token_uri`.
+    """
+
+    def __init__(
+        self,
+        token=None,
+        expiry=None,
+        refresh_token=None,
+        audience=None,
+        client_id=None,
+        client_secret=None,
+        token_url=None,
+        token_info_url=None,
+        revoke_url=None,
+        scopes=None,
+        quota_project_id=None,
+        universe_domain=credentials.DEFAULT_UNIVERSE_DOMAIN,
+    ):
+        """Instantiates a external account authorized user credentials object.
+
+        Args:
+        token (str): The OAuth 2.0 access token. Can be None if refresh information
+            is provided.
+        expiry (datetime.datetime): The optional expiration datetime of the OAuth 2.0 access
+            token.
+        refresh_token (str): The optional OAuth 2.0 refresh token. If specified,
+            credentials can be refreshed.
+        audience (str): The optional STS audience which contains the resource name for the workforce
+            pool and the provider identifier in that pool.
+        client_id (str): The OAuth 2.0 client ID. Must be specified for refresh, can be left as
+            None if the token can not be refreshed.
+        client_secret (str): The OAuth 2.0 client secret. Must be specified for refresh, can be
+            left as None if the token can not be refreshed.
+        token_url (str): The optional STS token exchange endpoint for refresh. Must be specified for
+            refresh, can be left as None if the token can not be refreshed.
+        token_info_url (str): The optional STS endpoint URL for token introspection.
+        revoke_url (str): The optional STS endpoint URL for revoking tokens.
+        quota_project_id (str): The optional project ID used for quota and billing.
+            This project may be different from the project used to
+            create the credentials.
+        universe_domain (Optional[str]): The universe domain. The default value
+            is googleapis.com.
+
+        Returns:
+            google.auth.external_account_authorized_user.Credentials: The
+                constructed credentials.
+        """
+        super(Credentials, self).__init__()
+
+        self.token = token
+        self.expiry = expiry
+        self._audience = audience
+        self._refresh_token = refresh_token
+        self._token_url = token_url
+        self._token_info_url = token_info_url
+        self._client_id = client_id
+        self._client_secret = client_secret
+        self._revoke_url = revoke_url
+        self._quota_project_id = quota_project_id
+        self._scopes = scopes
+        self._universe_domain = universe_domain or credentials.DEFAULT_UNIVERSE_DOMAIN
+        self._cred_file_path = None
+
+        if not self.valid and not self.can_refresh:
+            raise exceptions.InvalidOperation(
+                "Token should be created with fields to make it valid (`token` and "
+                "`expiry`), or fields to allow it to refresh (`refresh_token`, "
+                "`token_url`, `client_id`, `client_secret`)."
+            )
+
+        self._client_auth = None
+        if self._client_id:
+            self._client_auth = utils.ClientAuthentication(
+                utils.ClientAuthType.basic, self._client_id, self._client_secret
+            )
+        self._sts_client = sts.Client(self._token_url, self._client_auth)
+
+    @property
+    def info(self):
+        """Generates the serializable dictionary representation of the current
+        credentials.
+
+        Returns:
+            Mapping: The dictionary representation of the credentials. This is the
+                reverse of the "from_info" method defined in this class. It is
+                useful for serializing the current credentials so it can deserialized
+                later.
+        """
+        config_info = self.constructor_args()
+        config_info.update(type=_EXTERNAL_ACCOUNT_AUTHORIZED_USER_JSON_TYPE)
+        if config_info["expiry"]:
+            config_info["expiry"] = config_info["expiry"].isoformat() + "Z"
+
+        return {key: value for key, value in config_info.items() if value is not None}
+
+    def constructor_args(self):
+        return {
+            "audience": self._audience,
+            "refresh_token": self._refresh_token,
+            "token_url": self._token_url,
+            "token_info_url": self._token_info_url,
+            "client_id": self._client_id,
+            "client_secret": self._client_secret,
+            "token": self.token,
+            "expiry": self.expiry,
+            "revoke_url": self._revoke_url,
+            "scopes": self._scopes,
+            "quota_project_id": self._quota_project_id,
+            "universe_domain": self._universe_domain,
+        }
+
+    @property
+    def scopes(self):
+        """Optional[str]: The OAuth 2.0 permission scopes."""
+        return self._scopes
+
+    @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
+
+    @property
+    def client_id(self):
+        """Optional[str]: The OAuth 2.0 client ID."""
+        return self._client_id
+
+    @property
+    def client_secret(self):
+        """Optional[str]: The OAuth 2.0 client secret."""
+        return self._client_secret
+
+    @property
+    def audience(self):
+        """Optional[str]: The STS audience which contains the resource name for the
+            workforce pool and the provider identifier in that pool."""
+        return self._audience
+
+    @property
+    def refresh_token(self):
+        """Optional[str]: The OAuth 2.0 refresh token."""
+        return self._refresh_token
+
+    @property
+    def token_url(self):
+        """Optional[str]: The STS token exchange endpoint for refresh."""
+        return self._token_url
+
+    @property
+    def token_info_url(self):
+        """Optional[str]: The STS endpoint for token info."""
+        return self._token_info_url
+
+    @property
+    def revoke_url(self):
+        """Optional[str]: The STS endpoint for token revocation."""
+        return self._revoke_url
+
+    @property
+    def is_user(self):
+        """ True: This credential always represents a user."""
+        return True
+
+    @property
+    def can_refresh(self):
+        return all(
+            (self._refresh_token, self._token_url, self._client_id, self._client_secret)
+        )
+
+    def get_project_id(self, request=None):
+        """Retrieves the project ID corresponding to the workload identity or workforce pool.
+        For workforce pool credentials, it returns the project ID corresponding to
+        the workforce_pool_user_project.
+
+        When not determinable, None is returned.
+
+        Args:
+            request (google.auth.transport.requests.Request): Request object.
+                Unused here, but passed from _default.default().
+
+        Return:
+          str: project ID is not determinable for this credential type so it returns None
+        """
+
+        return None
+
+    def to_json(self, strip=None):
+        """Utility function that creates a JSON representation of this
+        credential.
+        Args:
+            strip (Sequence[str]): Optional list of members to exclude from the
+                                   generated JSON.
+        Returns:
+            str: A JSON representation of this instance. When converted into
+            a dictionary, it can be passed to from_info()
+            to create a new instance.
+        """
+        strip = strip if strip else []
+        return json.dumps({k: v for (k, v) in self.info.items() if k not in strip})
+
+    def refresh(self, request):
+        """Refreshes the access token.
+
+        Args:
+            request (google.auth.transport.Request): The object used to make
+                HTTP requests.
+
+        Raises:
+            google.auth.exceptions.RefreshError: If the credentials could
+                not be refreshed.
+        """
+        if not self.can_refresh:
+            raise exceptions.RefreshError(
+                "The credentials do not contain the necessary fields need to "
+                "refresh the access token. You must specify refresh_token, "
+                "token_url, client_id, and client_secret."
+            )
+
+        now = _helpers.utcnow()
+        response_data = self._make_sts_request(request)
+
+        self.token = response_data.get("access_token")
+
+        lifetime = datetime.timedelta(seconds=response_data.get("expires_in"))
+        self.expiry = now + lifetime
+
+        if "refresh_token" in response_data:
+            self._refresh_token = response_data["refresh_token"]
+
+    def _make_sts_request(self, request):
+        return self._sts_client.refresh_token(request, self._refresh_token)
+
+    @_helpers.copy_docstring(credentials.Credentials)
+    def get_cred_info(self):
+        if self._cred_file_path:
+            return {
+                "credential_source": self._cred_file_path,
+                "credential_type": "external account authorized user credentials",
+            }
+        return None
+
+    def _make_copy(self):
+        kwargs = self.constructor_args()
+        cred = self.__class__(**kwargs)
+        cred._cred_file_path = self._cred_file_path
+        return cred
+
+    @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
+    def with_quota_project(self, quota_project_id):
+        cred = self._make_copy()
+        cred._quota_project_id = quota_project_id
+        return cred
+
+    @_helpers.copy_docstring(credentials.CredentialsWithTokenUri)
+    def with_token_uri(self, token_uri):
+        cred = self._make_copy()
+        cred._token_url = token_uri
+        return cred
+
+    @_helpers.copy_docstring(credentials.CredentialsWithUniverseDomain)
+    def with_universe_domain(self, universe_domain):
+        cred = self._make_copy()
+        cred._universe_domain = universe_domain
+        return cred
+
+    @classmethod
+    def from_info(cls, info, **kwargs):
+        """Creates a Credentials instance from parsed external account info.
+
+        Args:
+            info (Mapping[str, str]): The external account info in Google
+                format.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.external_account_authorized_user.Credentials: The
+                constructed credentials.
+
+        Raises:
+            ValueError: For invalid parameters.
+        """
+        expiry = info.get("expiry")
+        if expiry:
+            expiry = datetime.datetime.strptime(
+                expiry.rstrip("Z").split(".")[0], "%Y-%m-%dT%H:%M:%S"
+            )
+        return cls(
+            audience=info.get("audience"),
+            refresh_token=info.get("refresh_token"),
+            token_url=info.get("token_url"),
+            token_info_url=info.get("token_info_url"),
+            client_id=info.get("client_id"),
+            client_secret=info.get("client_secret"),
+            token=info.get("token"),
+            expiry=expiry,
+            revoke_url=info.get("revoke_url"),
+            quota_project_id=info.get("quota_project_id"),
+            scopes=info.get("scopes"),
+            universe_domain=info.get(
+                "universe_domain", credentials.DEFAULT_UNIVERSE_DOMAIN
+            ),
+            **kwargs
+        )
+
+    @classmethod
+    def from_file(cls, filename, **kwargs):
+        """Creates a Credentials instance from an external account json file.
+
+        Args:
+            filename (str): The path to the external account json file.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.external_account_authorized_user.Credentials: The
+                constructed credentials.
+        """
+        with io.open(filename, "r", encoding="utf-8") as json_file:
+            data = json.load(json_file)
+            return cls.from_info(data, **kwargs)
diff --git a/.venv/lib/python3.12/site-packages/google/auth/iam.py b/.venv/lib/python3.12/site-packages/google/auth/iam.py
new file mode 100644
index 00000000..1e4cdffe
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/iam.py
@@ -0,0 +1,136 @@
+# Copyright 2017 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Tools for using the Google `Cloud Identity and Access Management (IAM)
+API`_'s auth-related functionality.
+
+.. _Cloud Identity and Access Management (IAM) API:
+    https://cloud.google.com/iam/docs/
+"""
+
+import base64
+import http.client as http_client
+import json
+
+from google.auth import _exponential_backoff
+from google.auth import _helpers
+from google.auth import credentials
+from google.auth import crypt
+from google.auth import exceptions
+
+IAM_RETRY_CODES = {
+    http_client.INTERNAL_SERVER_ERROR,
+    http_client.BAD_GATEWAY,
+    http_client.SERVICE_UNAVAILABLE,
+    http_client.GATEWAY_TIMEOUT,
+}
+
+_IAM_SCOPE = ["https://www.googleapis.com/auth/iam"]
+
+_IAM_ENDPOINT = (
+    "https://iamcredentials.googleapis.com/v1/projects/-"
+    + "/serviceAccounts/{}:generateAccessToken"
+)
+
+_IAM_SIGN_ENDPOINT = (
+    "https://iamcredentials.googleapis.com/v1/projects/-"
+    + "/serviceAccounts/{}:signBlob"
+)
+
+_IAM_SIGNJWT_ENDPOINT = (
+    "https://iamcredentials.googleapis.com/v1/projects/-"
+    + "/serviceAccounts/{}:signJwt"
+)
+
+_IAM_IDTOKEN_ENDPOINT = (
+    "https://iamcredentials.googleapis.com/v1/"
+    + "projects/-/serviceAccounts/{}:generateIdToken"
+)
+
+
+class Signer(crypt.Signer):
+    """Signs messages using the IAM `signBlob API`_.
+
+    This is useful when you need to sign bytes but do not have access to the
+    credential's private key file.
+
+    .. _signBlob API:
+        https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts
+        /signBlob
+    """
+
+    def __init__(self, request, credentials, service_account_email):
+        """
+        Args:
+            request (google.auth.transport.Request): The object used to make
+                HTTP requests.
+            credentials (google.auth.credentials.Credentials): The credentials
+                that will be used to authenticate the request to the IAM API.
+                The credentials must have of one the following scopes:
+
+                - https://www.googleapis.com/auth/iam
+                - https://www.googleapis.com/auth/cloud-platform
+            service_account_email (str): The service account email identifying
+                which service account to use to sign bytes. Often, this can
+                be the same as the service account email in the given
+                credentials.
+        """
+        self._request = request
+        self._credentials = credentials
+        self._service_account_email = service_account_email
+
+    def _make_signing_request(self, message):
+        """Makes a request to the API signBlob API."""
+        message = _helpers.to_bytes(message)
+
+        method = "POST"
+        url = _IAM_SIGN_ENDPOINT.replace(
+            credentials.DEFAULT_UNIVERSE_DOMAIN, self._credentials.universe_domain
+        ).format(self._service_account_email)
+        headers = {"Content-Type": "application/json"}
+        body = json.dumps(
+            {"payload": base64.b64encode(message).decode("utf-8")}
+        ).encode("utf-8")
+
+        retries = _exponential_backoff.ExponentialBackoff()
+        for _ in retries:
+            self._credentials.before_request(self._request, method, url, headers)
+
+            response = self._request(url=url, method=method, body=body, headers=headers)
+
+            if response.status in IAM_RETRY_CODES:
+                continue
+
+            if response.status != http_client.OK:
+                raise exceptions.TransportError(
+                    "Error calling the IAM signBlob API: {}".format(response.data)
+                )
+
+            return json.loads(response.data.decode("utf-8"))
+        raise exceptions.TransportError("exhausted signBlob endpoint retries")
+
+    @property
+    def key_id(self):
+        """Optional[str]: The key ID used to identify this private key.
+
+        .. warning::
+           This is always ``None``. The key ID used by IAM can not
+           be reliably determined ahead of time.
+        """
+        return None
+
+    @_helpers.copy_docstring(crypt.Signer)
+    def sign(self, message):
+        response = self._make_signing_request(message)
+        return base64.b64decode(response["signedBlob"])
diff --git a/.venv/lib/python3.12/site-packages/google/auth/identity_pool.py b/.venv/lib/python3.12/site-packages/google/auth/identity_pool.py
new file mode 100644
index 00000000..47f9a557
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/identity_pool.py
@@ -0,0 +1,439 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Identity Pool Credentials.
+
+This module provides credentials to access Google Cloud resources from on-prem
+or non-Google Cloud platforms which support external credentials (e.g. OIDC ID
+tokens) retrieved from local file locations or local servers. This includes
+Microsoft Azure and OIDC identity providers (e.g. K8s workloads registered with
+Hub with Hub workload identity enabled).
+
+These credentials are recommended over the use of service account credentials
+in on-prem/non-Google Cloud platforms as they do not involve the management of
+long-live service account private keys.
+
+Identity Pool Credentials are initialized using external_account
+arguments which are typically loaded from an external credentials file or
+an external credentials URL.
+
+This module also provides a definition for an abstract subject token supplier.
+This supplier can be implemented to return a valid OIDC or SAML2.0 subject token
+and used to create Identity Pool credentials. The credentials will then call the
+supplier instead of using pre-defined methods such as reading a local file or
+calling a URL.
+"""
+
+try:
+    from collections.abc import Mapping
+# Python 2.7 compatibility
+except ImportError:  # pragma: NO COVER
+    from collections import Mapping  # type: ignore
+import abc
+import json
+import os
+from typing import NamedTuple
+
+from google.auth import _helpers
+from google.auth import exceptions
+from google.auth import external_account
+from google.auth.transport import _mtls_helper
+
+
+class SubjectTokenSupplier(metaclass=abc.ABCMeta):
+    """Base class for subject token suppliers. This can be implemented with custom logic to retrieve
+    a subject token to exchange for a Google Cloud access token when using Workload or
+    Workforce Identity Federation. The identity pool credential does not cache the subject token,
+    so caching logic should be added in the implementation.
+    """
+
+    @abc.abstractmethod
+    def get_subject_token(self, context, request):
+        """Returns the requested subject token. The subject token must be valid.
+
+        .. warning: This is not cached by the calling Google credential, so caching logic should be implemented in the supplier.
+
+        Args:
+            context (google.auth.externalaccount.SupplierContext): The context object
+                containing information about the requested audience and subject token type.
+            request (google.auth.transport.Request): The object used to make
+                HTTP requests.
+
+        Raises:
+            google.auth.exceptions.RefreshError: If an error is encountered during
+                subject token retrieval logic.
+
+        Returns:
+            str: The requested subject token string.
+        """
+        raise NotImplementedError("")
+
+
+class _TokenContent(NamedTuple):
+    """Models the token content response from file and url internal suppliers.
+        Attributes:
+            content (str): The string content of the file or URL response.
+            location (str): The location the content was retrieved from. This will either be a file location or a URL.
+    """
+
+    content: str
+    location: str
+
+
+class _FileSupplier(SubjectTokenSupplier):
+    """ Internal implementation of subject token supplier which supports reading a subject token from a file."""
+
+    def __init__(self, path, format_type, subject_token_field_name):
+        self._path = path
+        self._format_type = format_type
+        self._subject_token_field_name = subject_token_field_name
+
+    @_helpers.copy_docstring(SubjectTokenSupplier)
+    def get_subject_token(self, context, request):
+        if not os.path.exists(self._path):
+            raise exceptions.RefreshError("File '{}' was not found.".format(self._path))
+
+        with open(self._path, "r", encoding="utf-8") as file_obj:
+            token_content = _TokenContent(file_obj.read(), self._path)
+
+        return _parse_token_data(
+            token_content, self._format_type, self._subject_token_field_name
+        )
+
+
+class _UrlSupplier(SubjectTokenSupplier):
+    """ Internal implementation of subject token supplier which supports retrieving a subject token by calling a URL endpoint."""
+
+    def __init__(self, url, format_type, subject_token_field_name, headers):
+        self._url = url
+        self._format_type = format_type
+        self._subject_token_field_name = subject_token_field_name
+        self._headers = headers
+
+    @_helpers.copy_docstring(SubjectTokenSupplier)
+    def get_subject_token(self, context, request):
+        response = request(url=self._url, method="GET", headers=self._headers)
+
+        # support both string and bytes type response.data
+        response_body = (
+            response.data.decode("utf-8")
+            if hasattr(response.data, "decode")
+            else response.data
+        )
+
+        if response.status != 200:
+            raise exceptions.RefreshError(
+                "Unable to retrieve Identity Pool subject token", response_body
+            )
+        token_content = _TokenContent(response_body, self._url)
+        return _parse_token_data(
+            token_content, self._format_type, self._subject_token_field_name
+        )
+
+
+class _X509Supplier(SubjectTokenSupplier):
+    """Internal supplier for X509 workload credentials. This class is used internally and always returns an empty string as the subject token."""
+
+    @_helpers.copy_docstring(SubjectTokenSupplier)
+    def get_subject_token(self, context, request):
+        return ""
+
+
+def _parse_token_data(token_content, format_type="text", subject_token_field_name=None):
+    if format_type == "text":
+        token = token_content.content
+    else:
+        try:
+            # Parse file content as JSON.
+            response_data = json.loads(token_content.content)
+            # Get the subject_token.
+            token = response_data[subject_token_field_name]
+        except (KeyError, ValueError):
+            raise exceptions.RefreshError(
+                "Unable to parse subject_token from JSON file '{}' using key '{}'".format(
+                    token_content.location, subject_token_field_name
+                )
+            )
+    if not token:
+        raise exceptions.RefreshError(
+            "Missing subject_token in the credential_source file"
+        )
+    return token
+
+
+class Credentials(external_account.Credentials):
+    """External account credentials sourced from files and URLs."""
+
+    def __init__(
+        self,
+        audience,
+        subject_token_type,
+        token_url=external_account._DEFAULT_TOKEN_URL,
+        credential_source=None,
+        subject_token_supplier=None,
+        *args,
+        **kwargs
+    ):
+        """Instantiates an external account credentials object from a file/URL.
+
+        Args:
+            audience (str): The STS audience field.
+            subject_token_type (str): The subject token type based on the Oauth2.0 token exchange spec.
+                Expected values include::
+
+                    “urn:ietf:params:oauth:token-type:jwt”
+                    “urn:ietf:params:oauth:token-type:id-token”
+                    “urn:ietf:params:oauth:token-type:saml2”
+
+            token_url (Optional [str]): The STS endpoint URL. If not provided, will default to "https://sts.googleapis.com/v1/token".
+            credential_source (Optional [Mapping]): The credential source dictionary used to
+                provide instructions on how to retrieve external credential to be
+                exchanged for Google access tokens. Either a credential source or
+                a subject token supplier must be provided.
+
+                Example credential_source for url-sourced credential::
+
+                    {
+                        "url": "http://www.example.com",
+                        "format": {
+                            "type": "json",
+                            "subject_token_field_name": "access_token",
+                        },
+                        "headers": {"foo": "bar"},
+                    }
+
+                Example credential_source for file-sourced credential::
+
+                    {
+                        "file": "/path/to/token/file.txt"
+                    }
+            subject_token_supplier (Optional [SubjectTokenSupplier]): Optional subject token supplier.
+                This will be called to supply a valid subject token which will then
+                be exchanged for Google access tokens. Either a subject token  supplier
+                or a credential source must be provided.
+            args (List): Optional positional arguments passed into the underlying :meth:`~external_account.Credentials.__init__` method.
+            kwargs (Mapping): Optional keyword arguments passed into the underlying :meth:`~external_account.Credentials.__init__` method.
+
+        Raises:
+            google.auth.exceptions.RefreshError: If an error is encountered during
+                access token retrieval logic.
+            ValueError: For invalid parameters.
+
+        .. note:: Typically one of the helper constructors
+            :meth:`from_file` or
+            :meth:`from_info` are used instead of calling the constructor directly.
+        """
+
+        super(Credentials, self).__init__(
+            audience=audience,
+            subject_token_type=subject_token_type,
+            token_url=token_url,
+            credential_source=credential_source,
+            *args,
+            **kwargs
+        )
+        if credential_source is None and subject_token_supplier is None:
+            raise exceptions.InvalidValue(
+                "A valid credential source or a subject token supplier must be provided."
+            )
+        if credential_source is not None and subject_token_supplier is not None:
+            raise exceptions.InvalidValue(
+                "Identity pool credential cannot have both a credential source and a subject token supplier."
+            )
+
+        if subject_token_supplier is not None:
+            self._subject_token_supplier = subject_token_supplier
+            self._credential_source_file = None
+            self._credential_source_url = None
+            self._credential_source_certificate = None
+        else:
+            if not isinstance(credential_source, Mapping):
+                self._credential_source_executable = None
+                raise exceptions.MalformedError(
+                    "Invalid credential_source. The credential_source is not a dict."
+                )
+            self._credential_source_file = credential_source.get("file")
+            self._credential_source_url = credential_source.get("url")
+            self._credential_source_certificate = credential_source.get("certificate")
+
+            # environment_id is only supported in AWS or dedicated future external
+            # account credentials.
+            if "environment_id" in credential_source:
+                raise exceptions.MalformedError(
+                    "Invalid Identity Pool credential_source field 'environment_id'"
+                )
+
+            # check that only one of file, url, or certificate are provided.
+            self._validate_single_source()
+
+            if self._credential_source_certificate:
+                self._validate_certificate_config()
+            else:
+                self._validate_file_or_url_config(credential_source)
+
+            if self._credential_source_file:
+                self._subject_token_supplier = _FileSupplier(
+                    self._credential_source_file,
+                    self._credential_source_format_type,
+                    self._credential_source_field_name,
+                )
+            elif self._credential_source_url:
+                self._subject_token_supplier = _UrlSupplier(
+                    self._credential_source_url,
+                    self._credential_source_format_type,
+                    self._credential_source_field_name,
+                    self._credential_source_headers,
+                )
+            else:  # self._credential_source_certificate
+                self._subject_token_supplier = _X509Supplier()
+
+    @_helpers.copy_docstring(external_account.Credentials)
+    def retrieve_subject_token(self, request):
+        return self._subject_token_supplier.get_subject_token(
+            self._supplier_context, request
+        )
+
+    def _get_mtls_cert_and_key_paths(self):
+        if self._credential_source_certificate is None:
+            raise exceptions.RefreshError(
+                'The credential is not configured to use mtls requests. The credential should include a "certificate" section in the credential source.'
+            )
+        else:
+            return _mtls_helper._get_workload_cert_and_key_paths(
+                self._certificate_config_location
+            )
+
+    def _mtls_required(self):
+        return self._credential_source_certificate is not None
+
+    def _create_default_metrics_options(self):
+        metrics_options = super(Credentials, self)._create_default_metrics_options()
+        # Check that credential source is a dict before checking for credential type. This check needs to be done
+        # here because the external_account credential constructor needs to pass the metrics options to the
+        # impersonated credential object before the identity_pool credentials are validated.
+        if isinstance(self._credential_source, Mapping):
+            if self._credential_source.get("file"):
+                metrics_options["source"] = "file"
+            elif self._credential_source.get("url"):
+                metrics_options["source"] = "url"
+            else:
+                metrics_options["source"] = "x509"
+        else:
+            metrics_options["source"] = "programmatic"
+        return metrics_options
+
+    def _has_custom_supplier(self):
+        return self._credential_source is None
+
+    def _constructor_args(self):
+        args = super(Credentials, self)._constructor_args()
+        # If a custom supplier was used, append it to the args dict.
+        if self._has_custom_supplier():
+            args.update({"subject_token_supplier": self._subject_token_supplier})
+        return args
+
+    def _validate_certificate_config(self):
+        self._certificate_config_location = self._credential_source_certificate.get(
+            "certificate_config_location"
+        )
+        use_default = self._credential_source_certificate.get(
+            "use_default_certificate_config"
+        )
+        if self._certificate_config_location and use_default:
+            raise exceptions.MalformedError(
+                "Invalid certificate configuration, certificate_config_location cannot be specified when use_default_certificate_config = true."
+            )
+        if not self._certificate_config_location and not use_default:
+            raise exceptions.MalformedError(
+                "Invalid certificate configuration, use_default_certificate_config should be true if no certificate_config_location is provided."
+            )
+
+    def _validate_file_or_url_config(self, credential_source):
+        self._credential_source_headers = credential_source.get("headers")
+        credential_source_format = credential_source.get("format", {})
+        # Get credential_source format type. When not provided, this
+        # defaults to text.
+        self._credential_source_format_type = (
+            credential_source_format.get("type") or "text"
+        )
+        if self._credential_source_format_type not in ["text", "json"]:
+            raise exceptions.MalformedError(
+                "Invalid credential_source format '{}'".format(
+                    self._credential_source_format_type
+                )
+            )
+        # For JSON types, get the required subject_token field name.
+        if self._credential_source_format_type == "json":
+            self._credential_source_field_name = credential_source_format.get(
+                "subject_token_field_name"
+            )
+            if self._credential_source_field_name is None:
+                raise exceptions.MalformedError(
+                    "Missing subject_token_field_name for JSON credential_source format"
+                )
+        else:
+            self._credential_source_field_name = None
+
+    def _validate_single_source(self):
+        credential_sources = [
+            self._credential_source_file,
+            self._credential_source_url,
+            self._credential_source_certificate,
+        ]
+        valid_credential_sources = list(
+            filter(lambda source: source is not None, credential_sources)
+        )
+
+        if len(valid_credential_sources) > 1:
+            raise exceptions.MalformedError(
+                "Ambiguous credential_source. 'file', 'url', and 'certificate' are mutually exclusive.."
+            )
+        if len(valid_credential_sources) != 1:
+            raise exceptions.MalformedError(
+                "Missing credential_source. A 'file', 'url', or 'certificate' must be provided."
+            )
+
+    @classmethod
+    def from_info(cls, info, **kwargs):
+        """Creates an Identity Pool Credentials instance from parsed external account info.
+
+        Args:
+            info (Mapping[str, str]): The Identity Pool external account info in Google
+                format.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.identity_pool.Credentials: The constructed
+                credentials.
+
+        Raises:
+            ValueError: For invalid parameters.
+        """
+        subject_token_supplier = info.get("subject_token_supplier")
+        kwargs.update({"subject_token_supplier": subject_token_supplier})
+        return super(Credentials, cls).from_info(info, **kwargs)
+
+    @classmethod
+    def from_file(cls, filename, **kwargs):
+        """Creates an IdentityPool Credentials instance from an external account json file.
+
+        Args:
+            filename (str): The path to the IdentityPool external account json file.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.identity_pool.Credentials: The constructed
+                credentials.
+        """
+        return super(Credentials, cls).from_file(filename, **kwargs)
diff --git a/.venv/lib/python3.12/site-packages/google/auth/impersonated_credentials.py b/.venv/lib/python3.12/site-packages/google/auth/impersonated_credentials.py
new file mode 100644
index 00000000..ed7e3f00
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/impersonated_credentials.py
@@ -0,0 +1,579 @@
+# Copyright 2018 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Google Cloud Impersonated credentials.
+
+This module provides authentication for applications where local credentials
+impersonates a remote service account using `IAM Credentials API`_.
+
+This class can be used to impersonate a service account as long as the original
+Credential object has the "Service Account Token Creator" role on the target
+service account.
+
+    .. _IAM Credentials API:
+        https://cloud.google.com/iam/credentials/reference/rest/
+"""
+
+import base64
+import copy
+from datetime import datetime
+import http.client as http_client
+import json
+
+from google.auth import _exponential_backoff
+from google.auth import _helpers
+from google.auth import credentials
+from google.auth import exceptions
+from google.auth import iam
+from google.auth import jwt
+from google.auth import metrics
+from google.oauth2 import _client
+
+
+_REFRESH_ERROR = "Unable to acquire impersonated credentials"
+
+_DEFAULT_TOKEN_LIFETIME_SECS = 3600  # 1 hour in seconds
+
+_GOOGLE_OAUTH2_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token"
+
+
+def _make_iam_token_request(
+    request,
+    principal,
+    headers,
+    body,
+    universe_domain=credentials.DEFAULT_UNIVERSE_DOMAIN,
+    iam_endpoint_override=None,
+):
+    """Makes a request to the Google Cloud IAM service for an access token.
+    Args:
+        request (Request): The Request object to use.
+        principal (str): The principal to request an access token for.
+        headers (Mapping[str, str]): Map of headers to transmit.
+        body (Mapping[str, str]): JSON Payload body for the iamcredentials
+            API call.
+        iam_endpoint_override (Optiona[str]): The full IAM endpoint override
+            with the target_principal embedded. This is useful when supporting
+            impersonation with regional endpoints.
+
+    Raises:
+        google.auth.exceptions.TransportError: Raised if there is an underlying
+            HTTP connection error
+        google.auth.exceptions.RefreshError: Raised if the impersonated
+            credentials are not available.  Common reasons are
+            `iamcredentials.googleapis.com` is not enabled or the
+            `Service Account Token Creator` is not assigned
+    """
+    iam_endpoint = iam_endpoint_override or iam._IAM_ENDPOINT.replace(
+        credentials.DEFAULT_UNIVERSE_DOMAIN, universe_domain
+    ).format(principal)
+
+    body = json.dumps(body).encode("utf-8")
+
+    response = request(url=iam_endpoint, method="POST", headers=headers, body=body)
+
+    # support both string and bytes type response.data
+    response_body = (
+        response.data.decode("utf-8")
+        if hasattr(response.data, "decode")
+        else response.data
+    )
+
+    if response.status != http_client.OK:
+        raise exceptions.RefreshError(_REFRESH_ERROR, response_body)
+
+    try:
+        token_response = json.loads(response_body)
+        token = token_response["accessToken"]
+        expiry = datetime.strptime(token_response["expireTime"], "%Y-%m-%dT%H:%M:%SZ")
+
+        return token, expiry
+
+    except (KeyError, ValueError) as caught_exc:
+        new_exc = exceptions.RefreshError(
+            "{}: No access token or invalid expiration in response.".format(
+                _REFRESH_ERROR
+            ),
+            response_body,
+        )
+        raise new_exc from caught_exc
+
+
+class Credentials(
+    credentials.Scoped, credentials.CredentialsWithQuotaProject, credentials.Signing
+):
+    """This module defines impersonated credentials which are essentially
+    impersonated identities.
+
+    Impersonated Credentials allows credentials issued to a user or
+    service account to impersonate another. The target service account must
+    grant the originating credential principal the
+    `Service Account Token Creator`_ IAM role:
+
+    For more information about Token Creator IAM role and
+    IAMCredentials API, see
+    `Creating Short-Lived Service Account Credentials`_.
+
+    .. _Service Account Token Creator:
+        https://cloud.google.com/iam/docs/service-accounts#the_service_account_token_creator_role
+
+    .. _Creating Short-Lived Service Account Credentials:
+        https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials
+
+    Usage:
+
+    First grant source_credentials the `Service Account Token Creator`
+    role on the target account to impersonate.   In this example, the
+    service account represented by svc_account.json has the
+    token creator role on
+    `impersonated-account@_project_.iam.gserviceaccount.com`.
+
+    Enable the IAMCredentials API on the source project:
+    `gcloud services enable iamcredentials.googleapis.com`.
+
+    Initialize a source credential which does not have access to
+    list bucket::
+
+        from google.oauth2 import service_account
+
+        target_scopes = [
+            'https://www.googleapis.com/auth/devstorage.read_only']
+
+        source_credentials = (
+            service_account.Credentials.from_service_account_file(
+                '/path/to/svc_account.json',
+                scopes=target_scopes))
+
+    Now use the source credentials to acquire credentials to impersonate
+    another service account::
+
+        from google.auth import impersonated_credentials
+
+        target_credentials = impersonated_credentials.Credentials(
+          source_credentials=source_credentials,
+          target_principal='impersonated-account@_project_.iam.gserviceaccount.com',
+          target_scopes = target_scopes,
+          lifetime=500)
+
+    Resource access is granted::
+
+        client = storage.Client(credentials=target_credentials)
+        buckets = client.list_buckets(project='your_project')
+        for bucket in buckets:
+          print(bucket.name)
+    """
+
+    def __init__(
+        self,
+        source_credentials,
+        target_principal,
+        target_scopes,
+        delegates=None,
+        subject=None,
+        lifetime=_DEFAULT_TOKEN_LIFETIME_SECS,
+        quota_project_id=None,
+        iam_endpoint_override=None,
+    ):
+        """
+        Args:
+            source_credentials (google.auth.Credentials): The source credential
+                used as to acquire the impersonated credentials.
+            target_principal (str): The service account to impersonate.
+            target_scopes (Sequence[str]): Scopes to request during the
+                authorization grant.
+            delegates (Sequence[str]): The chained list of delegates required
+                to grant the final access_token.  If set, the sequence of
+                identities must have "Service Account Token Creator" capability
+                granted to the prceeding identity.  For example, if set to
+                [serviceAccountB, serviceAccountC], the source_credential
+                must have the Token Creator role on serviceAccountB.
+                serviceAccountB must have the Token Creator on
+                serviceAccountC.
+                Finally, C must have Token Creator on target_principal.
+                If left unset, source_credential must have that role on
+                target_principal.
+            lifetime (int): Number of seconds the delegated credential should
+                be valid for (upto 3600).
+            quota_project_id (Optional[str]): The project ID used for quota and billing.
+                This project may be different from the project used to
+                create the credentials.
+            iam_endpoint_override (Optional[str]): The full IAM endpoint override
+                with the target_principal embedded. This is useful when supporting
+                impersonation with regional endpoints.
+            subject (Optional[str]): sub field of a JWT. This field should only be set
+                if you wish to impersonate as a user. This feature is useful when
+                using domain wide delegation.
+        """
+
+        super(Credentials, self).__init__()
+
+        self._source_credentials = copy.copy(source_credentials)
+        # Service account source credentials must have the _IAM_SCOPE
+        # added to refresh correctly. User credentials cannot have
+        # their original scopes modified.
+        if isinstance(self._source_credentials, credentials.Scoped):
+            self._source_credentials = self._source_credentials.with_scopes(
+                iam._IAM_SCOPE
+            )
+            # If the source credential is service account and self signed jwt
+            # is needed, we need to create a jwt credential inside it
+            if (
+                hasattr(self._source_credentials, "_create_self_signed_jwt")
+                and self._source_credentials._always_use_jwt_access
+            ):
+                self._source_credentials._create_self_signed_jwt(None)
+
+        self._universe_domain = source_credentials.universe_domain
+        self._target_principal = target_principal
+        self._target_scopes = target_scopes
+        self._delegates = delegates
+        self._subject = subject
+        self._lifetime = lifetime or _DEFAULT_TOKEN_LIFETIME_SECS
+        self.token = None
+        self.expiry = _helpers.utcnow()
+        self._quota_project_id = quota_project_id
+        self._iam_endpoint_override = iam_endpoint_override
+        self._cred_file_path = None
+
+    def _metric_header_for_usage(self):
+        return metrics.CRED_TYPE_SA_IMPERSONATE
+
+    @_helpers.copy_docstring(credentials.Credentials)
+    def refresh(self, request):
+        self._update_token(request)
+
+    def _update_token(self, request):
+        """Updates credentials with a new access_token representing
+        the impersonated account.
+
+        Args:
+            request (google.auth.transport.requests.Request): Request object
+                to use for refreshing credentials.
+        """
+
+        # Refresh our source credentials if it is not valid.
+        if (
+            self._source_credentials.token_state == credentials.TokenState.STALE
+            or self._source_credentials.token_state == credentials.TokenState.INVALID
+        ):
+            self._source_credentials.refresh(request)
+
+        body = {
+            "delegates": self._delegates,
+            "scope": self._target_scopes,
+            "lifetime": str(self._lifetime) + "s",
+        }
+
+        headers = {
+            "Content-Type": "application/json",
+            metrics.API_CLIENT_HEADER: metrics.token_request_access_token_impersonate(),
+        }
+
+        # Apply the source credentials authentication info.
+        self._source_credentials.apply(headers)
+
+        #  If a subject is specified a domain-wide delegation auth-flow is initiated
+        #  to impersonate as the provided subject (user).
+        if self._subject:
+            if self.universe_domain != credentials.DEFAULT_UNIVERSE_DOMAIN:
+                raise exceptions.GoogleAuthError(
+                    "Domain-wide delegation is not supported in universes other "
+                    + "than googleapis.com"
+                )
+
+            now = _helpers.utcnow()
+            payload = {
+                "iss": self._target_principal,
+                "scope": _helpers.scopes_to_string(self._target_scopes or ()),
+                "sub": self._subject,
+                "aud": _GOOGLE_OAUTH2_TOKEN_ENDPOINT,
+                "iat": _helpers.datetime_to_secs(now),
+                "exp": _helpers.datetime_to_secs(now) + _DEFAULT_TOKEN_LIFETIME_SECS,
+            }
+
+            assertion = _sign_jwt_request(
+                request=request,
+                principal=self._target_principal,
+                headers=headers,
+                payload=payload,
+                delegates=self._delegates,
+            )
+
+            self.token, self.expiry, _ = _client.jwt_grant(
+                request, _GOOGLE_OAUTH2_TOKEN_ENDPOINT, assertion
+            )
+
+            return
+
+        self.token, self.expiry = _make_iam_token_request(
+            request=request,
+            principal=self._target_principal,
+            headers=headers,
+            body=body,
+            universe_domain=self.universe_domain,
+            iam_endpoint_override=self._iam_endpoint_override,
+        )
+
+    def sign_bytes(self, message):
+        from google.auth.transport.requests import AuthorizedSession
+
+        iam_sign_endpoint = iam._IAM_SIGN_ENDPOINT.replace(
+            credentials.DEFAULT_UNIVERSE_DOMAIN, self.universe_domain
+        ).format(self._target_principal)
+
+        body = {
+            "payload": base64.b64encode(message).decode("utf-8"),
+            "delegates": self._delegates,
+        }
+
+        headers = {"Content-Type": "application/json"}
+
+        authed_session = AuthorizedSession(self._source_credentials)
+
+        try:
+            retries = _exponential_backoff.ExponentialBackoff()
+            for _ in retries:
+                response = authed_session.post(
+                    url=iam_sign_endpoint, headers=headers, json=body
+                )
+                if response.status_code in iam.IAM_RETRY_CODES:
+                    continue
+                if response.status_code != http_client.OK:
+                    raise exceptions.TransportError(
+                        "Error calling sign_bytes: {}".format(response.json())
+                    )
+
+                return base64.b64decode(response.json()["signedBlob"])
+        finally:
+            authed_session.close()
+        raise exceptions.TransportError("exhausted signBlob endpoint retries")
+
+    @property
+    def signer_email(self):
+        return self._target_principal
+
+    @property
+    def service_account_email(self):
+        return self._target_principal
+
+    @property
+    def signer(self):
+        return self
+
+    @property
+    def requires_scopes(self):
+        return not self._target_scopes
+
+    @_helpers.copy_docstring(credentials.Credentials)
+    def get_cred_info(self):
+        if self._cred_file_path:
+            return {
+                "credential_source": self._cred_file_path,
+                "credential_type": "impersonated credentials",
+                "principal": self._target_principal,
+            }
+        return None
+
+    def _make_copy(self):
+        cred = self.__class__(
+            self._source_credentials,
+            target_principal=self._target_principal,
+            target_scopes=self._target_scopes,
+            delegates=self._delegates,
+            lifetime=self._lifetime,
+            quota_project_id=self._quota_project_id,
+            iam_endpoint_override=self._iam_endpoint_override,
+        )
+        cred._cred_file_path = self._cred_file_path
+        return cred
+
+    @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
+    def with_quota_project(self, quota_project_id):
+        cred = self._make_copy()
+        cred._quota_project_id = quota_project_id
+        return cred
+
+    @_helpers.copy_docstring(credentials.Scoped)
+    def with_scopes(self, scopes, default_scopes=None):
+        cred = self._make_copy()
+        cred._target_scopes = scopes or default_scopes
+        return cred
+
+
+class IDTokenCredentials(credentials.CredentialsWithQuotaProject):
+    """Open ID Connect ID Token-based service account credentials.
+
+    """
+
+    def __init__(
+        self,
+        target_credentials,
+        target_audience=None,
+        include_email=False,
+        quota_project_id=None,
+    ):
+        """
+        Args:
+            target_credentials (google.auth.Credentials): The target
+                credential used as to acquire the id tokens for.
+            target_audience (string): Audience to issue the token for.
+            include_email (bool): Include email in IdToken
+            quota_project_id (Optional[str]):  The project ID used for
+                quota and billing.
+        """
+        super(IDTokenCredentials, self).__init__()
+
+        if not isinstance(target_credentials, Credentials):
+            raise exceptions.GoogleAuthError(
+                "Provided Credential must be " "impersonated_credentials"
+            )
+        self._target_credentials = target_credentials
+        self._target_audience = target_audience
+        self._include_email = include_email
+        self._quota_project_id = quota_project_id
+
+    def from_credentials(self, target_credentials, target_audience=None):
+        return self.__class__(
+            target_credentials=target_credentials,
+            target_audience=target_audience,
+            include_email=self._include_email,
+            quota_project_id=self._quota_project_id,
+        )
+
+    def with_target_audience(self, target_audience):
+        return self.__class__(
+            target_credentials=self._target_credentials,
+            target_audience=target_audience,
+            include_email=self._include_email,
+            quota_project_id=self._quota_project_id,
+        )
+
+    def with_include_email(self, include_email):
+        return self.__class__(
+            target_credentials=self._target_credentials,
+            target_audience=self._target_audience,
+            include_email=include_email,
+            quota_project_id=self._quota_project_id,
+        )
+
+    @_helpers.copy_docstring(credentials.CredentialsWithQuotaProject)
+    def with_quota_project(self, quota_project_id):
+        return self.__class__(
+            target_credentials=self._target_credentials,
+            target_audience=self._target_audience,
+            include_email=self._include_email,
+            quota_project_id=quota_project_id,
+        )
+
+    @_helpers.copy_docstring(credentials.Credentials)
+    def refresh(self, request):
+        from google.auth.transport.requests import AuthorizedSession
+
+        iam_sign_endpoint = iam._IAM_IDTOKEN_ENDPOINT.replace(
+            credentials.DEFAULT_UNIVERSE_DOMAIN,
+            self._target_credentials.universe_domain,
+        ).format(self._target_credentials.signer_email)
+
+        body = {
+            "audience": self._target_audience,
+            "delegates": self._target_credentials._delegates,
+            "includeEmail": self._include_email,
+        }
+
+        headers = {
+            "Content-Type": "application/json",
+            metrics.API_CLIENT_HEADER: metrics.token_request_id_token_impersonate(),
+        }
+
+        authed_session = AuthorizedSession(
+            self._target_credentials._source_credentials, auth_request=request
+        )
+
+        try:
+            response = authed_session.post(
+                url=iam_sign_endpoint,
+                headers=headers,
+                data=json.dumps(body).encode("utf-8"),
+            )
+        finally:
+            authed_session.close()
+
+        if response.status_code != http_client.OK:
+            raise exceptions.RefreshError(
+                "Error getting ID token: {}".format(response.json())
+            )
+
+        id_token = response.json()["token"]
+        self.token = id_token
+        self.expiry = datetime.utcfromtimestamp(
+            jwt.decode(id_token, verify=False)["exp"]
+        )
+
+
+def _sign_jwt_request(request, principal, headers, payload, delegates=[]):
+    """Makes a request to the Google Cloud IAM service to sign a JWT using a
+    service account's system-managed private key.
+    Args:
+        request (Request): The Request object to use.
+        principal (str): The principal to request an access token for.
+        headers (Mapping[str, str]): Map of headers to transmit.
+        payload (Mapping[str, str]): The JWT payload to sign. Must be a
+            serialized JSON object that contains a JWT Claims Set.
+        delegates (Sequence[str]): The chained list of delegates required
+            to grant the final access_token.  If set, the sequence of
+            identities must have "Service Account Token Creator" capability
+            granted to the prceeding identity.  For example, if set to
+            [serviceAccountB, serviceAccountC], the source_credential
+            must have the Token Creator role on serviceAccountB.
+            serviceAccountB must have the Token Creator on
+            serviceAccountC.
+            Finally, C must have Token Creator on target_principal.
+            If left unset, source_credential must have that role on
+            target_principal.
+
+    Raises:
+        google.auth.exceptions.TransportError: Raised if there is an underlying
+            HTTP connection error
+        google.auth.exceptions.RefreshError: Raised if the impersonated
+            credentials are not available.  Common reasons are
+            `iamcredentials.googleapis.com` is not enabled or the
+            `Service Account Token Creator` is not assigned
+    """
+    iam_endpoint = iam._IAM_SIGNJWT_ENDPOINT.format(principal)
+
+    body = {"delegates": delegates, "payload": json.dumps(payload)}
+    body = json.dumps(body).encode("utf-8")
+
+    response = request(url=iam_endpoint, method="POST", headers=headers, body=body)
+
+    # support both string and bytes type response.data
+    response_body = (
+        response.data.decode("utf-8")
+        if hasattr(response.data, "decode")
+        else response.data
+    )
+
+    if response.status != http_client.OK:
+        raise exceptions.RefreshError(_REFRESH_ERROR, response_body)
+
+    try:
+        jwt_response = json.loads(response_body)
+        signed_jwt = jwt_response["signedJwt"]
+        return signed_jwt
+
+    except (KeyError, ValueError) as caught_exc:
+        new_exc = exceptions.RefreshError(
+            "{}: No signed JWT in response.".format(_REFRESH_ERROR), response_body
+        )
+        raise new_exc from caught_exc
diff --git a/.venv/lib/python3.12/site-packages/google/auth/jwt.py b/.venv/lib/python3.12/site-packages/google/auth/jwt.py
new file mode 100644
index 00000000..1ebd565d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/jwt.py
@@ -0,0 +1,878 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""JSON Web Tokens
+
+Provides support for creating (encoding) and verifying (decoding) JWTs,
+especially JWTs generated and consumed by Google infrastructure.
+
+See `rfc7519`_ for more details on JWTs.
+
+To encode a JWT use :func:`encode`::
+
+    from google.auth import crypt
+    from google.auth import jwt
+
+    signer = crypt.Signer(private_key)
+    payload = {'some': 'payload'}
+    encoded = jwt.encode(signer, payload)
+
+To decode a JWT and verify claims use :func:`decode`::
+
+    claims = jwt.decode(encoded, certs=public_certs)
+
+You can also skip verification::
+
+    claims = jwt.decode(encoded, verify=False)
+
+.. _rfc7519: https://tools.ietf.org/html/rfc7519
+
+"""
+
+try:
+    from collections.abc import Mapping
+# Python 2.7 compatibility
+except ImportError:  # pragma: NO COVER
+    from collections import Mapping  # type: ignore
+import copy
+import datetime
+import json
+import urllib
+
+import cachetools
+
+from google.auth import _helpers
+from google.auth import _service_account_info
+from google.auth import crypt
+from google.auth import exceptions
+import google.auth.credentials
+
+try:
+    from google.auth.crypt import es256
+except ImportError:  # pragma: NO COVER
+    es256 = None  # type: ignore
+
+_DEFAULT_TOKEN_LIFETIME_SECS = 3600  # 1 hour in seconds
+_DEFAULT_MAX_CACHE_SIZE = 10
+_ALGORITHM_TO_VERIFIER_CLASS = {"RS256": crypt.RSAVerifier}
+_CRYPTOGRAPHY_BASED_ALGORITHMS = frozenset(["ES256"])
+
+if es256 is not None:  # pragma: NO COVER
+    _ALGORITHM_TO_VERIFIER_CLASS["ES256"] = es256.ES256Verifier  # type: ignore
+
+
+def encode(signer, payload, header=None, key_id=None):
+    """Make a signed JWT.
+
+    Args:
+        signer (google.auth.crypt.Signer): The signer used to sign the JWT.
+        payload (Mapping[str, str]): The JWT payload.
+        header (Mapping[str, str]): Additional JWT header payload.
+        key_id (str): The key id to add to the JWT header. If the
+            signer has a key id it will be used as the default. If this is
+            specified it will override the signer's key id.
+
+    Returns:
+        bytes: The encoded JWT.
+    """
+    if header is None:
+        header = {}
+
+    if key_id is None:
+        key_id = signer.key_id
+
+    header.update({"typ": "JWT"})
+
+    if "alg" not in header:
+        if es256 is not None and isinstance(signer, es256.ES256Signer):
+            header.update({"alg": "ES256"})
+        else:
+            header.update({"alg": "RS256"})
+
+    if key_id is not None:
+        header["kid"] = key_id
+
+    segments = [
+        _helpers.unpadded_urlsafe_b64encode(json.dumps(header).encode("utf-8")),
+        _helpers.unpadded_urlsafe_b64encode(json.dumps(payload).encode("utf-8")),
+    ]
+
+    signing_input = b".".join(segments)
+    signature = signer.sign(signing_input)
+    segments.append(_helpers.unpadded_urlsafe_b64encode(signature))
+
+    return b".".join(segments)
+
+
+def _decode_jwt_segment(encoded_section):
+    """Decodes a single JWT segment."""
+    section_bytes = _helpers.padded_urlsafe_b64decode(encoded_section)
+    try:
+        return json.loads(section_bytes.decode("utf-8"))
+    except ValueError as caught_exc:
+        new_exc = exceptions.MalformedError(
+            "Can't parse segment: {0}".format(section_bytes)
+        )
+        raise new_exc from caught_exc
+
+
+def _unverified_decode(token):
+    """Decodes a token and does no verification.
+
+    Args:
+        token (Union[str, bytes]): The encoded JWT.
+
+    Returns:
+        Tuple[Mapping, Mapping, str, str]: header, payload, signed_section, and
+            signature.
+
+    Raises:
+        google.auth.exceptions.MalformedError: if there are an incorrect amount of segments in the token or segments of the wrong type.
+    """
+    token = _helpers.to_bytes(token)
+
+    if token.count(b".") != 2:
+        raise exceptions.MalformedError(
+            "Wrong number of segments in token: {0}".format(token)
+        )
+
+    encoded_header, encoded_payload, signature = token.split(b".")
+    signed_section = encoded_header + b"." + encoded_payload
+    signature = _helpers.padded_urlsafe_b64decode(signature)
+
+    # Parse segments
+    header = _decode_jwt_segment(encoded_header)
+    payload = _decode_jwt_segment(encoded_payload)
+
+    if not isinstance(header, Mapping):
+        raise exceptions.MalformedError(
+            "Header segment should be a JSON object: {0}".format(encoded_header)
+        )
+
+    if not isinstance(payload, Mapping):
+        raise exceptions.MalformedError(
+            "Payload segment should be a JSON object: {0}".format(encoded_payload)
+        )
+
+    return header, payload, signed_section, signature
+
+
+def decode_header(token):
+    """Return the decoded header of a token.
+
+    No verification is done. This is useful to extract the key id from
+    the header in order to acquire the appropriate certificate to verify
+    the token.
+
+    Args:
+        token (Union[str, bytes]): the encoded JWT.
+
+    Returns:
+        Mapping: The decoded JWT header.
+    """
+    header, _, _, _ = _unverified_decode(token)
+    return header
+
+
+def _verify_iat_and_exp(payload, clock_skew_in_seconds=0):
+    """Verifies the ``iat`` (Issued At) and ``exp`` (Expires) claims in a token
+    payload.
+
+    Args:
+        payload (Mapping[str, str]): The JWT payload.
+        clock_skew_in_seconds (int): The clock skew used for `iat` and `exp`
+            validation.
+
+    Raises:
+        google.auth.exceptions.InvalidValue: if value validation failed.
+        google.auth.exceptions.MalformedError: if schema validation failed.
+    """
+    now = _helpers.datetime_to_secs(_helpers.utcnow())
+
+    # Make sure the iat and exp claims are present.
+    for key in ("iat", "exp"):
+        if key not in payload:
+            raise exceptions.MalformedError(
+                "Token does not contain required claim {}".format(key)
+            )
+
+    # Make sure the token wasn't issued in the future.
+    iat = payload["iat"]
+    # Err on the side of accepting a token that is slightly early to account
+    # for clock skew.
+    earliest = iat - clock_skew_in_seconds
+    if now < earliest:
+        raise exceptions.InvalidValue(
+            "Token used too early, {} < {}. Check that your computer's clock is set correctly.".format(
+                now, iat
+            )
+        )
+
+    # Make sure the token wasn't issued in the past.
+    exp = payload["exp"]
+    # Err on the side of accepting a token that is slightly out of date
+    # to account for clow skew.
+    latest = exp + clock_skew_in_seconds
+    if latest < now:
+        raise exceptions.InvalidValue("Token expired, {} < {}".format(latest, now))
+
+
+def decode(token, certs=None, verify=True, audience=None, clock_skew_in_seconds=0):
+    """Decode and verify a JWT.
+
+    Args:
+        token (str): The encoded JWT.
+        certs (Union[str, bytes, Mapping[str, Union[str, bytes]]]): The
+            certificate used to validate the JWT signature. If bytes or string,
+            it must the the public key certificate in PEM format. If a mapping,
+            it must be a mapping of key IDs to public key certificates in PEM
+            format. The mapping must contain the same key ID that's specified
+            in the token's header.
+        verify (bool): Whether to perform signature and claim validation.
+            Verification is done by default.
+        audience (str or list): The audience claim, 'aud', that this JWT should
+            contain. Or a list of audience claims. If None then the JWT's 'aud'
+            parameter is not verified.
+        clock_skew_in_seconds (int): The clock skew used for `iat` and `exp`
+            validation.
+
+    Returns:
+        Mapping[str, str]: The deserialized JSON payload in the JWT.
+
+    Raises:
+        google.auth.exceptions.InvalidValue: if value validation failed.
+        google.auth.exceptions.MalformedError: if schema validation failed.
+    """
+    header, payload, signed_section, signature = _unverified_decode(token)
+
+    if not verify:
+        return payload
+
+    # Pluck the key id and algorithm from the header and make sure we have
+    # a verifier that can support it.
+    key_alg = header.get("alg")
+    key_id = header.get("kid")
+
+    try:
+        verifier_cls = _ALGORITHM_TO_VERIFIER_CLASS[key_alg]
+    except KeyError as exc:
+        if key_alg in _CRYPTOGRAPHY_BASED_ALGORITHMS:
+            raise exceptions.InvalidValue(
+                "The key algorithm {} requires the cryptography package to be installed.".format(
+                    key_alg
+                )
+            ) from exc
+        else:
+            raise exceptions.InvalidValue(
+                "Unsupported signature algorithm {}".format(key_alg)
+            ) from exc
+    # If certs is specified as a dictionary of key IDs to certificates, then
+    # use the certificate identified by the key ID in the token header.
+    if isinstance(certs, Mapping):
+        if key_id:
+            if key_id not in certs:
+                raise exceptions.MalformedError(
+                    "Certificate for key id {} not found.".format(key_id)
+                )
+            certs_to_check = [certs[key_id]]
+        # If there's no key id in the header, check against all of the certs.
+        else:
+            certs_to_check = certs.values()
+    else:
+        certs_to_check = certs
+
+    # Verify that the signature matches the message.
+    if not crypt.verify_signature(
+        signed_section, signature, certs_to_check, verifier_cls
+    ):
+        raise exceptions.MalformedError("Could not verify token signature.")
+
+    # Verify the issued at and created times in the payload.
+    _verify_iat_and_exp(payload, clock_skew_in_seconds)
+
+    # Check audience.
+    if audience is not None:
+        claim_audience = payload.get("aud")
+        if isinstance(audience, str):
+            audience = [audience]
+        if claim_audience not in audience:
+            raise exceptions.InvalidValue(
+                "Token has wrong audience {}, expected one of {}".format(
+                    claim_audience, audience
+                )
+            )
+
+    return payload
+
+
+class Credentials(
+    google.auth.credentials.Signing, google.auth.credentials.CredentialsWithQuotaProject
+):
+    """Credentials that use a JWT as the bearer token.
+
+    These credentials require an "audience" claim. This claim identifies the
+    intended recipient of the bearer token.
+
+    The constructor arguments determine the claims for the JWT that is
+    sent with requests. Usually, you'll construct these credentials with
+    one of the helper constructors as shown in the next section.
+
+    To create JWT credentials using a Google service account private key
+    JSON file::
+
+        audience = 'https://pubsub.googleapis.com/google.pubsub.v1.Publisher'
+        credentials = jwt.Credentials.from_service_account_file(
+            'service-account.json',
+            audience=audience)
+
+    If you already have the service account file loaded and parsed::
+
+        service_account_info = json.load(open('service_account.json'))
+        credentials = jwt.Credentials.from_service_account_info(
+            service_account_info,
+            audience=audience)
+
+    Both helper methods pass on arguments to the constructor, so you can
+    specify the JWT claims::
+
+        credentials = jwt.Credentials.from_service_account_file(
+            'service-account.json',
+            audience=audience,
+            additional_claims={'meta': 'data'})
+
+    You can also construct the credentials directly if you have a
+    :class:`~google.auth.crypt.Signer` instance::
+
+        credentials = jwt.Credentials(
+            signer,
+            issuer='your-issuer',
+            subject='your-subject',
+            audience=audience)
+
+    The claims are considered immutable. If you want to modify the claims,
+    you can easily create another instance using :meth:`with_claims`::
+
+        new_audience = (
+            'https://pubsub.googleapis.com/google.pubsub.v1.Subscriber')
+        new_credentials = credentials.with_claims(audience=new_audience)
+    """
+
+    def __init__(
+        self,
+        signer,
+        issuer,
+        subject,
+        audience,
+        additional_claims=None,
+        token_lifetime=_DEFAULT_TOKEN_LIFETIME_SECS,
+        quota_project_id=None,
+    ):
+        """
+        Args:
+            signer (google.auth.crypt.Signer): The signer used to sign JWTs.
+            issuer (str): The `iss` claim.
+            subject (str): The `sub` claim.
+            audience (str): the `aud` claim. The intended audience for the
+                credentials.
+            additional_claims (Mapping[str, str]): Any additional claims for
+                the JWT payload.
+            token_lifetime (int): The amount of time in seconds for
+                which the token is valid. Defaults to 1 hour.
+            quota_project_id (Optional[str]): The project ID used for quota
+                and billing.
+        """
+        super(Credentials, self).__init__()
+        self._signer = signer
+        self._issuer = issuer
+        self._subject = subject
+        self._audience = audience
+        self._token_lifetime = token_lifetime
+        self._quota_project_id = quota_project_id
+
+        if additional_claims is None:
+            additional_claims = {}
+
+        self._additional_claims = additional_claims
+
+    @classmethod
+    def _from_signer_and_info(cls, signer, info, **kwargs):
+        """Creates a Credentials instance from a signer and service account
+        info.
+
+        Args:
+            signer (google.auth.crypt.Signer): The signer used to sign JWTs.
+            info (Mapping[str, str]): The service account info.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.jwt.Credentials: The constructed credentials.
+
+        Raises:
+            google.auth.exceptions.MalformedError: If the info is not in the expected format.
+        """
+        kwargs.setdefault("subject", info["client_email"])
+        kwargs.setdefault("issuer", info["client_email"])
+        return cls(signer, **kwargs)
+
+    @classmethod
+    def from_service_account_info(cls, info, **kwargs):
+        """Creates an Credentials instance from a dictionary.
+
+        Args:
+            info (Mapping[str, str]): The service account info in Google
+                format.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.jwt.Credentials: The constructed credentials.
+
+        Raises:
+            google.auth.exceptions.MalformedError: If the info is not in the expected format.
+        """
+        signer = _service_account_info.from_dict(info, require=["client_email"])
+        return cls._from_signer_and_info(signer, info, **kwargs)
+
+    @classmethod
+    def from_service_account_file(cls, filename, **kwargs):
+        """Creates a Credentials instance from a service account .json file
+        in Google format.
+
+        Args:
+            filename (str): The path to the service account .json file.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.jwt.Credentials: The constructed credentials.
+        """
+        info, signer = _service_account_info.from_filename(
+            filename, require=["client_email"]
+        )
+        return cls._from_signer_and_info(signer, info, **kwargs)
+
+    @classmethod
+    def from_signing_credentials(cls, credentials, audience, **kwargs):
+        """Creates a new :class:`google.auth.jwt.Credentials` instance from an
+        existing :class:`google.auth.credentials.Signing` instance.
+
+        The new instance will use the same signer as the existing instance and
+        will use the existing instance's signer email as the issuer and
+        subject by default.
+
+        Example::
+
+            svc_creds = service_account.Credentials.from_service_account_file(
+                'service_account.json')
+            audience = (
+                'https://pubsub.googleapis.com/google.pubsub.v1.Publisher')
+            jwt_creds = jwt.Credentials.from_signing_credentials(
+                svc_creds, audience=audience)
+
+        Args:
+            credentials (google.auth.credentials.Signing): The credentials to
+                use to construct the new credentials.
+            audience (str): the `aud` claim. The intended audience for the
+                credentials.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.jwt.Credentials: A new Credentials instance.
+        """
+        kwargs.setdefault("issuer", credentials.signer_email)
+        kwargs.setdefault("subject", credentials.signer_email)
+        return cls(credentials.signer, audience=audience, **kwargs)
+
+    def with_claims(
+        self, issuer=None, subject=None, audience=None, additional_claims=None
+    ):
+        """Returns a copy of these credentials with modified claims.
+
+        Args:
+            issuer (str): The `iss` claim. If unspecified the current issuer
+                claim will be used.
+            subject (str): The `sub` claim. If unspecified the current subject
+                claim will be used.
+            audience (str): the `aud` claim. If unspecified the current
+                audience claim will be used.
+            additional_claims (Mapping[str, str]): Any additional claims for
+                the JWT payload. This will be merged with the current
+                additional claims.
+
+        Returns:
+            google.auth.jwt.Credentials: A new credentials instance.
+        """
+        new_additional_claims = copy.deepcopy(self._additional_claims)
+        new_additional_claims.update(additional_claims or {})
+
+        return self.__class__(
+            self._signer,
+            issuer=issuer if issuer is not None else self._issuer,
+            subject=subject if subject is not None else self._subject,
+            audience=audience if audience is not None else self._audience,
+            additional_claims=new_additional_claims,
+            quota_project_id=self._quota_project_id,
+        )
+
+    @_helpers.copy_docstring(google.auth.credentials.CredentialsWithQuotaProject)
+    def with_quota_project(self, quota_project_id):
+        return self.__class__(
+            self._signer,
+            issuer=self._issuer,
+            subject=self._subject,
+            audience=self._audience,
+            additional_claims=self._additional_claims,
+            quota_project_id=quota_project_id,
+        )
+
+    def _make_jwt(self):
+        """Make a signed JWT.
+
+        Returns:
+            Tuple[bytes, datetime]: The encoded JWT and the expiration.
+        """
+        now = _helpers.utcnow()
+        lifetime = datetime.timedelta(seconds=self._token_lifetime)
+        expiry = now + lifetime
+
+        payload = {
+            "iss": self._issuer,
+            "sub": self._subject,
+            "iat": _helpers.datetime_to_secs(now),
+            "exp": _helpers.datetime_to_secs(expiry),
+        }
+        if self._audience:
+            payload["aud"] = self._audience
+
+        payload.update(self._additional_claims)
+
+        jwt = encode(self._signer, payload)
+
+        return jwt, expiry
+
+    def refresh(self, request):
+        """Refreshes the access token.
+
+        Args:
+            request (Any): Unused.
+        """
+        # pylint: disable=unused-argument
+        # (pylint doesn't correctly recognize overridden methods.)
+        self.token, self.expiry = self._make_jwt()
+
+    @_helpers.copy_docstring(google.auth.credentials.Signing)
+    def sign_bytes(self, message):
+        return self._signer.sign(message)
+
+    @property  # type: ignore
+    @_helpers.copy_docstring(google.auth.credentials.Signing)
+    def signer_email(self):
+        return self._issuer
+
+    @property  # type: ignore
+    @_helpers.copy_docstring(google.auth.credentials.Signing)
+    def signer(self):
+        return self._signer
+
+    @property  # type: ignore
+    def additional_claims(self):
+        """ Additional claims the JWT object was created with."""
+        return self._additional_claims
+
+
+class OnDemandCredentials(
+    google.auth.credentials.Signing, google.auth.credentials.CredentialsWithQuotaProject
+):
+    """On-demand JWT credentials.
+
+    Like :class:`Credentials`, this class uses a JWT as the bearer token for
+    authentication. However, this class does not require the audience at
+    construction time. Instead, it will generate a new token on-demand for
+    each request using the request URI as the audience. It caches tokens
+    so that multiple requests to the same URI do not incur the overhead
+    of generating a new token every time.
+
+    This behavior is especially useful for `gRPC`_ clients. A gRPC service may
+    have multiple audience and gRPC clients may not know all of the audiences
+    required for accessing a particular service. With these credentials,
+    no knowledge of the audiences is required ahead of time.
+
+    .. _grpc: http://www.grpc.io/
+    """
+
+    def __init__(
+        self,
+        signer,
+        issuer,
+        subject,
+        additional_claims=None,
+        token_lifetime=_DEFAULT_TOKEN_LIFETIME_SECS,
+        max_cache_size=_DEFAULT_MAX_CACHE_SIZE,
+        quota_project_id=None,
+    ):
+        """
+        Args:
+            signer (google.auth.crypt.Signer): The signer used to sign JWTs.
+            issuer (str): The `iss` claim.
+            subject (str): The `sub` claim.
+            additional_claims (Mapping[str, str]): Any additional claims for
+                the JWT payload.
+            token_lifetime (int): The amount of time in seconds for
+                which the token is valid. Defaults to 1 hour.
+            max_cache_size (int): The maximum number of JWT tokens to keep in
+                cache. Tokens are cached using :class:`cachetools.LRUCache`.
+            quota_project_id (Optional[str]): The project ID used for quota
+                and billing.
+
+        """
+        super(OnDemandCredentials, self).__init__()
+        self._signer = signer
+        self._issuer = issuer
+        self._subject = subject
+        self._token_lifetime = token_lifetime
+        self._quota_project_id = quota_project_id
+
+        if additional_claims is None:
+            additional_claims = {}
+
+        self._additional_claims = additional_claims
+        self._cache = cachetools.LRUCache(maxsize=max_cache_size)
+
+    @classmethod
+    def _from_signer_and_info(cls, signer, info, **kwargs):
+        """Creates an OnDemandCredentials instance from a signer and service
+        account info.
+
+        Args:
+            signer (google.auth.crypt.Signer): The signer used to sign JWTs.
+            info (Mapping[str, str]): The service account info.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.jwt.OnDemandCredentials: The constructed credentials.
+
+        Raises:
+            google.auth.exceptions.MalformedError: If the info is not in the expected format.
+        """
+        kwargs.setdefault("subject", info["client_email"])
+        kwargs.setdefault("issuer", info["client_email"])
+        return cls(signer, **kwargs)
+
+    @classmethod
+    def from_service_account_info(cls, info, **kwargs):
+        """Creates an OnDemandCredentials instance from a dictionary.
+
+        Args:
+            info (Mapping[str, str]): The service account info in Google
+                format.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.jwt.OnDemandCredentials: The constructed credentials.
+
+        Raises:
+            google.auth.exceptions.MalformedError: If the info is not in the expected format.
+        """
+        signer = _service_account_info.from_dict(info, require=["client_email"])
+        return cls._from_signer_and_info(signer, info, **kwargs)
+
+    @classmethod
+    def from_service_account_file(cls, filename, **kwargs):
+        """Creates an OnDemandCredentials instance from a service account .json
+        file in Google format.
+
+        Args:
+            filename (str): The path to the service account .json file.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.jwt.OnDemandCredentials: The constructed credentials.
+        """
+        info, signer = _service_account_info.from_filename(
+            filename, require=["client_email"]
+        )
+        return cls._from_signer_and_info(signer, info, **kwargs)
+
+    @classmethod
+    def from_signing_credentials(cls, credentials, **kwargs):
+        """Creates a new :class:`google.auth.jwt.OnDemandCredentials` instance
+        from an existing :class:`google.auth.credentials.Signing` instance.
+
+        The new instance will use the same signer as the existing instance and
+        will use the existing instance's signer email as the issuer and
+        subject by default.
+
+        Example::
+
+            svc_creds = service_account.Credentials.from_service_account_file(
+                'service_account.json')
+            jwt_creds = jwt.OnDemandCredentials.from_signing_credentials(
+                svc_creds)
+
+        Args:
+            credentials (google.auth.credentials.Signing): The credentials to
+                use to construct the new credentials.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.jwt.Credentials: A new Credentials instance.
+        """
+        kwargs.setdefault("issuer", credentials.signer_email)
+        kwargs.setdefault("subject", credentials.signer_email)
+        return cls(credentials.signer, **kwargs)
+
+    def with_claims(self, issuer=None, subject=None, additional_claims=None):
+        """Returns a copy of these credentials with modified claims.
+
+        Args:
+            issuer (str): The `iss` claim. If unspecified the current issuer
+                claim will be used.
+            subject (str): The `sub` claim. If unspecified the current subject
+                claim will be used.
+            additional_claims (Mapping[str, str]): Any additional claims for
+                the JWT payload. This will be merged with the current
+                additional claims.
+
+        Returns:
+            google.auth.jwt.OnDemandCredentials: A new credentials instance.
+        """
+        new_additional_claims = copy.deepcopy(self._additional_claims)
+        new_additional_claims.update(additional_claims or {})
+
+        return self.__class__(
+            self._signer,
+            issuer=issuer if issuer is not None else self._issuer,
+            subject=subject if subject is not None else self._subject,
+            additional_claims=new_additional_claims,
+            max_cache_size=self._cache.maxsize,
+            quota_project_id=self._quota_project_id,
+        )
+
+    @_helpers.copy_docstring(google.auth.credentials.CredentialsWithQuotaProject)
+    def with_quota_project(self, quota_project_id):
+
+        return self.__class__(
+            self._signer,
+            issuer=self._issuer,
+            subject=self._subject,
+            additional_claims=self._additional_claims,
+            max_cache_size=self._cache.maxsize,
+            quota_project_id=quota_project_id,
+        )
+
+    @property
+    def valid(self):
+        """Checks the validity of the credentials.
+
+        These credentials are always valid because it generates tokens on
+        demand.
+        """
+        return True
+
+    def _make_jwt_for_audience(self, audience):
+        """Make a new JWT for the given audience.
+
+        Args:
+            audience (str): The intended audience.
+
+        Returns:
+            Tuple[bytes, datetime]: The encoded JWT and the expiration.
+        """
+        now = _helpers.utcnow()
+        lifetime = datetime.timedelta(seconds=self._token_lifetime)
+        expiry = now + lifetime
+
+        payload = {
+            "iss": self._issuer,
+            "sub": self._subject,
+            "iat": _helpers.datetime_to_secs(now),
+            "exp": _helpers.datetime_to_secs(expiry),
+            "aud": audience,
+        }
+
+        payload.update(self._additional_claims)
+
+        jwt = encode(self._signer, payload)
+
+        return jwt, expiry
+
+    def _get_jwt_for_audience(self, audience):
+        """Get a JWT For a given audience.
+
+        If there is already an existing, non-expired token in the cache for
+        the audience, that token is used. Otherwise, a new token will be
+        created.
+
+        Args:
+            audience (str): The intended audience.
+
+        Returns:
+            bytes: The encoded JWT.
+        """
+        token, expiry = self._cache.get(audience, (None, None))
+
+        if token is None or expiry < _helpers.utcnow():
+            token, expiry = self._make_jwt_for_audience(audience)
+            self._cache[audience] = token, expiry
+
+        return token
+
+    def refresh(self, request):
+        """Raises an exception, these credentials can not be directly
+        refreshed.
+
+        Args:
+            request (Any): Unused.
+
+        Raises:
+            google.auth.RefreshError
+        """
+        # pylint: disable=unused-argument
+        # (pylint doesn't correctly recognize overridden methods.)
+        raise exceptions.RefreshError(
+            "OnDemandCredentials can not be directly refreshed."
+        )
+
+    def before_request(self, request, method, url, headers):
+        """Performs credential-specific before request logic.
+
+        Args:
+            request (Any): Unused. JWT credentials do not need to make an
+                HTTP request to refresh.
+            method (str): The request's HTTP method.
+            url (str): The request's URI. This is used as the audience claim
+                when generating the JWT.
+            headers (Mapping): The request's headers.
+        """
+        # pylint: disable=unused-argument
+        # (pylint doesn't correctly recognize overridden methods.)
+        parts = urllib.parse.urlsplit(url)
+        # Strip query string and fragment
+        audience = urllib.parse.urlunsplit(
+            (parts.scheme, parts.netloc, parts.path, "", "")
+        )
+        token = self._get_jwt_for_audience(audience)
+        self.apply(headers, token=token)
+
+    @_helpers.copy_docstring(google.auth.credentials.Signing)
+    def sign_bytes(self, message):
+        return self._signer.sign(message)
+
+    @property  # type: ignore
+    @_helpers.copy_docstring(google.auth.credentials.Signing)
+    def signer_email(self):
+        return self._issuer
+
+    @property  # type: ignore
+    @_helpers.copy_docstring(google.auth.credentials.Signing)
+    def signer(self):
+        return self._signer
diff --git a/.venv/lib/python3.12/site-packages/google/auth/metrics.py b/.venv/lib/python3.12/site-packages/google/auth/metrics.py
new file mode 100644
index 00000000..11e4b077
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/metrics.py
@@ -0,0 +1,154 @@
+# Copyright 2023 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+""" We use x-goog-api-client header to report metrics. This module provides
+the constants and helper methods to construct x-goog-api-client header.
+"""
+
+import platform
+
+from google.auth import version
+
+
+API_CLIENT_HEADER = "x-goog-api-client"
+
+# BYOID Specific consts
+BYOID_HEADER_SECTION = "google-byoid-sdk"
+
+# Auth request type
+REQUEST_TYPE_ACCESS_TOKEN = "auth-request-type/at"
+REQUEST_TYPE_ID_TOKEN = "auth-request-type/it"
+REQUEST_TYPE_MDS_PING = "auth-request-type/mds"
+REQUEST_TYPE_REAUTH_START = "auth-request-type/re-start"
+REQUEST_TYPE_REAUTH_CONTINUE = "auth-request-type/re-cont"
+
+# Credential type
+CRED_TYPE_USER = "cred-type/u"
+CRED_TYPE_SA_ASSERTION = "cred-type/sa"
+CRED_TYPE_SA_JWT = "cred-type/jwt"
+CRED_TYPE_SA_MDS = "cred-type/mds"
+CRED_TYPE_SA_IMPERSONATE = "cred-type/imp"
+
+
+# Versions
+def python_and_auth_lib_version():
+    return "gl-python/{} auth/{}".format(platform.python_version(), version.__version__)
+
+
+# Token request metric header values
+
+# x-goog-api-client header value for access token request via metadata server.
+# Example: "gl-python/3.7 auth/1.1 auth-request-type/at cred-type/mds"
+def token_request_access_token_mds():
+    return "{} {} {}".format(
+        python_and_auth_lib_version(), REQUEST_TYPE_ACCESS_TOKEN, CRED_TYPE_SA_MDS
+    )
+
+
+# x-goog-api-client header value for ID token request via metadata server.
+# Example: "gl-python/3.7 auth/1.1 auth-request-type/it cred-type/mds"
+def token_request_id_token_mds():
+    return "{} {} {}".format(
+        python_and_auth_lib_version(), REQUEST_TYPE_ID_TOKEN, CRED_TYPE_SA_MDS
+    )
+
+
+# x-goog-api-client header value for impersonated credentials access token request.
+# Example: "gl-python/3.7 auth/1.1 auth-request-type/at cred-type/imp"
+def token_request_access_token_impersonate():
+    return "{} {} {}".format(
+        python_and_auth_lib_version(),
+        REQUEST_TYPE_ACCESS_TOKEN,
+        CRED_TYPE_SA_IMPERSONATE,
+    )
+
+
+# x-goog-api-client header value for impersonated credentials ID token request.
+# Example: "gl-python/3.7 auth/1.1 auth-request-type/it cred-type/imp"
+def token_request_id_token_impersonate():
+    return "{} {} {}".format(
+        python_and_auth_lib_version(), REQUEST_TYPE_ID_TOKEN, CRED_TYPE_SA_IMPERSONATE
+    )
+
+
+# x-goog-api-client header value for service account credentials access token
+# request (assertion flow).
+# Example: "gl-python/3.7 auth/1.1 auth-request-type/at cred-type/sa"
+def token_request_access_token_sa_assertion():
+    return "{} {} {}".format(
+        python_and_auth_lib_version(), REQUEST_TYPE_ACCESS_TOKEN, CRED_TYPE_SA_ASSERTION
+    )
+
+
+# x-goog-api-client header value for service account credentials ID token
+# request (assertion flow).
+# Example: "gl-python/3.7 auth/1.1 auth-request-type/it cred-type/sa"
+def token_request_id_token_sa_assertion():
+    return "{} {} {}".format(
+        python_and_auth_lib_version(), REQUEST_TYPE_ID_TOKEN, CRED_TYPE_SA_ASSERTION
+    )
+
+
+# x-goog-api-client header value for user credentials token request.
+# Example: "gl-python/3.7 auth/1.1 cred-type/u"
+def token_request_user():
+    return "{} {}".format(python_and_auth_lib_version(), CRED_TYPE_USER)
+
+
+# Miscellenous metrics
+
+# x-goog-api-client header value for metadata server ping.
+# Example: "gl-python/3.7 auth/1.1 auth-request-type/mds"
+def mds_ping():
+    return "{} {}".format(python_and_auth_lib_version(), REQUEST_TYPE_MDS_PING)
+
+
+# x-goog-api-client header value for reauth start endpoint calls.
+# Example: "gl-python/3.7 auth/1.1 auth-request-type/re-start"
+def reauth_start():
+    return "{} {}".format(python_and_auth_lib_version(), REQUEST_TYPE_REAUTH_START)
+
+
+# x-goog-api-client header value for reauth continue endpoint calls.
+# Example: "gl-python/3.7 auth/1.1 cred-type/re-cont"
+def reauth_continue():
+    return "{} {}".format(python_and_auth_lib_version(), REQUEST_TYPE_REAUTH_CONTINUE)
+
+
+# x-goog-api-client header value for BYOID calls to the Security Token Service exchange token endpoint.
+# Example: "gl-python/3.7 auth/1.1 google-byoid-sdk source/aws sa-impersonation/true sa-impersonation/true"
+def byoid_metrics_header(metrics_options):
+    header = "{} {}".format(python_and_auth_lib_version(), BYOID_HEADER_SECTION)
+    for key, value in metrics_options.items():
+        header = "{} {}/{}".format(header, key, value)
+    return header
+
+
+def add_metric_header(headers, metric_header_value):
+    """Add x-goog-api-client header with the given value.
+
+    Args:
+        headers (Mapping[str, str]): The headers to which we will add the
+            metric header.
+        metric_header_value (Optional[str]): If value is None, do nothing;
+            if headers already has a x-goog-api-client header, append the value
+            to the existing header; otherwise add a new x-goog-api-client
+            header with the given value.
+    """
+    if not metric_header_value:
+        return
+    if API_CLIENT_HEADER not in headers:
+        headers[API_CLIENT_HEADER] = metric_header_value
+    else:
+        headers[API_CLIENT_HEADER] += " " + metric_header_value
diff --git a/.venv/lib/python3.12/site-packages/google/auth/pluggable.py b/.venv/lib/python3.12/site-packages/google/auth/pluggable.py
new file mode 100644
index 00000000..d725188f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/pluggable.py
@@ -0,0 +1,429 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Pluggable Credentials.
+Pluggable Credentials are initialized using external_account arguments which
+are typically loaded from third-party executables. Unlike other
+credentials that can be initialized with a list of explicit arguments, secrets
+or credentials, external account clients use the environment and hints/guidelines
+provided by the external_account JSON file to retrieve credentials and exchange
+them for Google access tokens.
+
+Example credential_source for pluggable credential:
+{
+    "executable": {
+        "command": "/path/to/get/credentials.sh --arg1=value1 --arg2=value2",
+        "timeout_millis": 5000,
+        "output_file": "/path/to/generated/cached/credentials"
+    }
+}
+"""
+
+try:
+    from collections.abc import Mapping
+# Python 2.7 compatibility
+except ImportError:  # pragma: NO COVER
+    from collections import Mapping  # type: ignore
+import json
+import os
+import subprocess
+import sys
+import time
+
+from google.auth import _helpers
+from google.auth import exceptions
+from google.auth import external_account
+
+# The max supported executable spec version.
+EXECUTABLE_SUPPORTED_MAX_VERSION = 1
+
+EXECUTABLE_TIMEOUT_MILLIS_DEFAULT = 30 * 1000  # 30 seconds
+EXECUTABLE_TIMEOUT_MILLIS_LOWER_BOUND = 5 * 1000  # 5 seconds
+EXECUTABLE_TIMEOUT_MILLIS_UPPER_BOUND = 120 * 1000  # 2 minutes
+
+EXECUTABLE_INTERACTIVE_TIMEOUT_MILLIS_LOWER_BOUND = 30 * 1000  # 30 seconds
+EXECUTABLE_INTERACTIVE_TIMEOUT_MILLIS_UPPER_BOUND = 30 * 60 * 1000  # 30 minutes
+
+
+class Credentials(external_account.Credentials):
+    """External account credentials sourced from executables."""
+
+    def __init__(
+        self,
+        audience,
+        subject_token_type,
+        token_url,
+        credential_source,
+        *args,
+        **kwargs
+    ):
+        """Instantiates an external account credentials object from a executables.
+
+        Args:
+            audience (str): The STS audience field.
+            subject_token_type (str): The subject token type.
+            token_url (str): The STS endpoint URL.
+            credential_source (Mapping): The credential source dictionary used to
+                provide instructions on how to retrieve external credential to be
+                exchanged for Google access tokens.
+
+                Example credential_source for pluggable credential:
+
+                    {
+                        "executable": {
+                            "command": "/path/to/get/credentials.sh --arg1=value1 --arg2=value2",
+                            "timeout_millis": 5000,
+                            "output_file": "/path/to/generated/cached/credentials"
+                        }
+                    }
+            args (List): Optional positional arguments passed into the underlying :meth:`~external_account.Credentials.__init__` method.
+            kwargs (Mapping): Optional keyword arguments passed into the underlying :meth:`~external_account.Credentials.__init__` method.
+
+        Raises:
+            google.auth.exceptions.RefreshError: If an error is encountered during
+                access token retrieval logic.
+            google.auth.exceptions.InvalidValue: For invalid parameters.
+            google.auth.exceptions.MalformedError: For invalid parameters.
+
+        .. note:: Typically one of the helper constructors
+            :meth:`from_file` or
+            :meth:`from_info` are used instead of calling the constructor directly.
+        """
+
+        self.interactive = kwargs.pop("interactive", False)
+        super(Credentials, self).__init__(
+            audience=audience,
+            subject_token_type=subject_token_type,
+            token_url=token_url,
+            credential_source=credential_source,
+            *args,
+            **kwargs
+        )
+        if not isinstance(credential_source, Mapping):
+            self._credential_source_executable = None
+            raise exceptions.MalformedError(
+                "Missing credential_source. The credential_source is not a dict."
+            )
+        self._credential_source_executable = credential_source.get("executable")
+        if not self._credential_source_executable:
+            raise exceptions.MalformedError(
+                "Missing credential_source. An 'executable' must be provided."
+            )
+        self._credential_source_executable_command = self._credential_source_executable.get(
+            "command"
+        )
+        self._credential_source_executable_timeout_millis = self._credential_source_executable.get(
+            "timeout_millis"
+        )
+        self._credential_source_executable_interactive_timeout_millis = self._credential_source_executable.get(
+            "interactive_timeout_millis"
+        )
+        self._credential_source_executable_output_file = self._credential_source_executable.get(
+            "output_file"
+        )
+
+        # Dummy value. This variable is only used via injection, not exposed to ctor
+        self._tokeninfo_username = ""
+
+        if not self._credential_source_executable_command:
+            raise exceptions.MalformedError(
+                "Missing command field. Executable command must be provided."
+            )
+        if not self._credential_source_executable_timeout_millis:
+            self._credential_source_executable_timeout_millis = (
+                EXECUTABLE_TIMEOUT_MILLIS_DEFAULT
+            )
+        elif (
+            self._credential_source_executable_timeout_millis
+            < EXECUTABLE_TIMEOUT_MILLIS_LOWER_BOUND
+            or self._credential_source_executable_timeout_millis
+            > EXECUTABLE_TIMEOUT_MILLIS_UPPER_BOUND
+        ):
+            raise exceptions.InvalidValue("Timeout must be between 5 and 120 seconds.")
+
+        if self._credential_source_executable_interactive_timeout_millis:
+            if (
+                self._credential_source_executable_interactive_timeout_millis
+                < EXECUTABLE_INTERACTIVE_TIMEOUT_MILLIS_LOWER_BOUND
+                or self._credential_source_executable_interactive_timeout_millis
+                > EXECUTABLE_INTERACTIVE_TIMEOUT_MILLIS_UPPER_BOUND
+            ):
+                raise exceptions.InvalidValue(
+                    "Interactive timeout must be between 30 seconds and 30 minutes."
+                )
+
+    @_helpers.copy_docstring(external_account.Credentials)
+    def retrieve_subject_token(self, request):
+        self._validate_running_mode()
+
+        # Check output file.
+        if self._credential_source_executable_output_file is not None:
+            try:
+                with open(
+                    self._credential_source_executable_output_file, encoding="utf-8"
+                ) as output_file:
+                    response = json.load(output_file)
+            except Exception:
+                pass
+            else:
+                try:
+                    # If the cached response is expired, _parse_subject_token will raise an error which will be ignored and we will call the executable again.
+                    subject_token = self._parse_subject_token(response)
+                    if (
+                        "expiration_time" not in response
+                    ):  # Always treat missing expiration_time as expired and proceed to executable run.
+                        raise exceptions.RefreshError
+                except (exceptions.MalformedError, exceptions.InvalidValue):
+                    raise
+                except exceptions.RefreshError:
+                    pass
+                else:
+                    return subject_token
+
+        if not _helpers.is_python_3():
+            raise exceptions.RefreshError(
+                "Pluggable auth is only supported for python 3.7+"
+            )
+
+        # Inject env vars.
+        env = os.environ.copy()
+        self._inject_env_variables(env)
+        env["GOOGLE_EXTERNAL_ACCOUNT_REVOKE"] = "0"
+
+        # Run executable.
+        exe_timeout = (
+            self._credential_source_executable_interactive_timeout_millis / 1000
+            if self.interactive
+            else self._credential_source_executable_timeout_millis / 1000
+        )
+        exe_stdin = sys.stdin if self.interactive else None
+        exe_stdout = sys.stdout if self.interactive else subprocess.PIPE
+        exe_stderr = sys.stdout if self.interactive else subprocess.STDOUT
+
+        result = subprocess.run(
+            self._credential_source_executable_command.split(),
+            timeout=exe_timeout,
+            stdin=exe_stdin,
+            stdout=exe_stdout,
+            stderr=exe_stderr,
+            env=env,
+        )
+        if result.returncode != 0:
+            raise exceptions.RefreshError(
+                "Executable exited with non-zero return code {}. Error: {}".format(
+                    result.returncode, result.stdout
+                )
+            )
+
+        # Handle executable output.
+        response = json.loads(result.stdout.decode("utf-8")) if result.stdout else None
+        if not response and self._credential_source_executable_output_file is not None:
+            response = json.load(
+                open(self._credential_source_executable_output_file, encoding="utf-8")
+            )
+
+        subject_token = self._parse_subject_token(response)
+        return subject_token
+
+    def revoke(self, request):
+        """Revokes the subject token using the credential_source object.
+
+        Args:
+            request (google.auth.transport.Request): A callable used to make
+                HTTP requests.
+        Raises:
+            google.auth.exceptions.RefreshError: If the executable revocation
+                not properly executed.
+
+        """
+        if not self.interactive:
+            raise exceptions.InvalidValue(
+                "Revoke is only enabled under interactive mode."
+            )
+        self._validate_running_mode()
+
+        if not _helpers.is_python_3():
+            raise exceptions.RefreshError(
+                "Pluggable auth is only supported for python 3.7+"
+            )
+
+        # Inject variables
+        env = os.environ.copy()
+        self._inject_env_variables(env)
+        env["GOOGLE_EXTERNAL_ACCOUNT_REVOKE"] = "1"
+
+        # Run executable
+        result = subprocess.run(
+            self._credential_source_executable_command.split(),
+            timeout=self._credential_source_executable_interactive_timeout_millis
+            / 1000,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.STDOUT,
+            env=env,
+        )
+
+        if result.returncode != 0:
+            raise exceptions.RefreshError(
+                "Auth revoke failed on executable. Exit with non-zero return code {}. Error: {}".format(
+                    result.returncode, result.stdout
+                )
+            )
+
+        response = json.loads(result.stdout.decode("utf-8"))
+        self._validate_revoke_response(response)
+
+    @property
+    def external_account_id(self):
+        """Returns the external account identifier.
+
+        When service account impersonation is used the identifier is the service
+        account email.
+
+        Without service account impersonation, this returns None, unless it is
+        being used by the Google Cloud CLI which populates this field.
+        """
+
+        return self.service_account_email or self._tokeninfo_username
+
+    @classmethod
+    def from_info(cls, info, **kwargs):
+        """Creates a Pluggable Credentials instance from parsed external account info.
+
+        Args:
+            info (Mapping[str, str]): The Pluggable external account info in Google
+                format.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.pluggable.Credentials: The constructed
+                credentials.
+
+        Raises:
+            google.auth.exceptions.InvalidValue: For invalid parameters.
+            google.auth.exceptions.MalformedError: For invalid parameters.
+        """
+        return super(Credentials, cls).from_info(info, **kwargs)
+
+    @classmethod
+    def from_file(cls, filename, **kwargs):
+        """Creates an Pluggable Credentials instance from an external account json file.
+
+        Args:
+            filename (str): The path to the Pluggable external account json file.
+            kwargs: Additional arguments to pass to the constructor.
+
+        Returns:
+            google.auth.pluggable.Credentials: The constructed
+                credentials.
+        """
+        return super(Credentials, cls).from_file(filename, **kwargs)
+
+    def _inject_env_variables(self, env):
+        env["GOOGLE_EXTERNAL_ACCOUNT_AUDIENCE"] = self._audience
+        env["GOOGLE_EXTERNAL_ACCOUNT_TOKEN_TYPE"] = self._subject_token_type
+        env["GOOGLE_EXTERNAL_ACCOUNT_ID"] = self.external_account_id
+        env["GOOGLE_EXTERNAL_ACCOUNT_INTERACTIVE"] = "1" if self.interactive else "0"
+
+        if self._service_account_impersonation_url is not None:
+            env[
+                "GOOGLE_EXTERNAL_ACCOUNT_IMPERSONATED_EMAIL"
+            ] = self.service_account_email
+        if self._credential_source_executable_output_file is not None:
+            env[
+                "GOOGLE_EXTERNAL_ACCOUNT_OUTPUT_FILE"
+            ] = self._credential_source_executable_output_file
+
+    def _parse_subject_token(self, response):
+        self._validate_response_schema(response)
+        if not response["success"]:
+            if "code" not in response or "message" not in response:
+                raise exceptions.MalformedError(
+                    "Error code and message fields are required in the response."
+                )
+            raise exceptions.RefreshError(
+                "Executable returned unsuccessful response: code: {}, message: {}.".format(
+                    response["code"], response["message"]
+                )
+            )
+        if "expiration_time" in response and response["expiration_time"] < time.time():
+            raise exceptions.RefreshError(
+                "The token returned by the executable is expired."
+            )
+        if "token_type" not in response:
+            raise exceptions.MalformedError(
+                "The executable response is missing the token_type field."
+            )
+        if (
+            response["token_type"] == "urn:ietf:params:oauth:token-type:jwt"
+            or response["token_type"] == "urn:ietf:params:oauth:token-type:id_token"
+        ):  # OIDC
+            return response["id_token"]
+        elif response["token_type"] == "urn:ietf:params:oauth:token-type:saml2":  # SAML
+            return response["saml_response"]
+        else:
+            raise exceptions.RefreshError("Executable returned unsupported token type.")
+
+    def _validate_revoke_response(self, response):
+        self._validate_response_schema(response)
+        if not response["success"]:
+            raise exceptions.RefreshError("Revoke failed with unsuccessful response.")
+
+    def _validate_response_schema(self, response):
+        if "version" not in response:
+            raise exceptions.MalformedError(
+                "The executable response is missing the version field."
+            )
+        if response["version"] > EXECUTABLE_SUPPORTED_MAX_VERSION:
+            raise exceptions.RefreshError(
+                "Executable returned unsupported version {}.".format(
+                    response["version"]
+                )
+            )
+
+        if "success" not in response:
+            raise exceptions.MalformedError(
+                "The executable response is missing the success field."
+            )
+
+    def _validate_running_mode(self):
+        env_allow_executables = os.environ.get(
+            "GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES"
+        )
+        if env_allow_executables != "1":
+            raise exceptions.MalformedError(
+                "Executables need to be explicitly allowed (set GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES to '1') to run."
+            )
+
+        if self.interactive and not self._credential_source_executable_output_file:
+            raise exceptions.MalformedError(
+                "An output_file must be specified in the credential configuration for interactive mode."
+            )
+
+        if (
+            self.interactive
+            and not self._credential_source_executable_interactive_timeout_millis
+        ):
+            raise exceptions.InvalidOperation(
+                "Interactive mode cannot run without an interactive timeout."
+            )
+
+        if self.interactive and not self.is_workforce_pool:
+            raise exceptions.InvalidValue(
+                "Interactive mode is only enabled for workforce pool."
+            )
+
+    def _create_default_metrics_options(self):
+        metrics_options = super(Credentials, self)._create_default_metrics_options()
+        metrics_options["source"] = "executable"
+        return metrics_options
diff --git a/.venv/lib/python3.12/site-packages/google/auth/py.typed b/.venv/lib/python3.12/site-packages/google/auth/py.typed
new file mode 100644
index 00000000..aa7b6892
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/py.typed
@@ -0,0 +1,2 @@
+# Marker file for PEP 561.

+# The google-auth package uses inline types.

diff --git a/.venv/lib/python3.12/site-packages/google/auth/transport/__init__.py b/.venv/lib/python3.12/site-packages/google/auth/transport/__init__.py
new file mode 100644
index 00000000..724568e5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/transport/__init__.py
@@ -0,0 +1,103 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Transport - HTTP client library support.
+
+:mod:`google.auth` is designed to work with various HTTP client libraries such
+as urllib3 and requests. In order to work across these libraries with different
+interfaces some abstraction is needed.
+
+This module provides two interfaces that are implemented by transport adapters
+to support HTTP libraries. :class:`Request` defines the interface expected by
+:mod:`google.auth` to make requests. :class:`Response` defines the interface
+for the return value of :class:`Request`.
+"""
+
+import abc
+import http.client as http_client
+
+DEFAULT_RETRYABLE_STATUS_CODES = (
+    http_client.INTERNAL_SERVER_ERROR,
+    http_client.SERVICE_UNAVAILABLE,
+    http_client.REQUEST_TIMEOUT,
+    http_client.TOO_MANY_REQUESTS,
+)
+"""Sequence[int]:  HTTP status codes indicating a request can be retried.
+"""
+
+
+DEFAULT_REFRESH_STATUS_CODES = (http_client.UNAUTHORIZED,)
+"""Sequence[int]:  Which HTTP status code indicate that credentials should be
+refreshed.
+"""
+
+DEFAULT_MAX_REFRESH_ATTEMPTS = 2
+"""int: How many times to refresh the credentials and retry a request."""
+
+
+class Response(metaclass=abc.ABCMeta):
+    """HTTP Response data."""
+
+    @abc.abstractproperty
+    def status(self):
+        """int: The HTTP status code."""
+        raise NotImplementedError("status must be implemented.")
+
+    @abc.abstractproperty
+    def headers(self):
+        """Mapping[str, str]: The HTTP response headers."""
+        raise NotImplementedError("headers must be implemented.")
+
+    @abc.abstractproperty
+    def data(self):
+        """bytes: The response body."""
+        raise NotImplementedError("data must be implemented.")
+
+
+class Request(metaclass=abc.ABCMeta):
+    """Interface for a callable that makes HTTP requests.
+
+    Specific transport implementations should provide an implementation of
+    this that adapts their specific request / response API.
+
+    .. automethod:: __call__
+    """
+
+    @abc.abstractmethod
+    def __call__(
+        self, url, method="GET", body=None, headers=None, timeout=None, **kwargs
+    ):
+        """Make an HTTP request.
+
+        Args:
+            url (str): The URI to be requested.
+            method (str): The HTTP method to use for the request. Defaults
+                to 'GET'.
+            body (bytes): The payload / body in HTTP request.
+            headers (Mapping[str, str]): Request headers.
+            timeout (Optional[int]): The number of seconds to wait for a
+                response from the server. If not specified or if None, the
+                transport-specific default timeout will be used.
+            kwargs: Additionally arguments passed on to the transport's
+                request method.
+
+        Returns:
+            Response: The HTTP response.
+
+        Raises:
+            google.auth.exceptions.TransportError: If any exception occurred.
+        """
+        # pylint: disable=redundant-returns-doc, missing-raises-doc
+        # (pylint doesn't play well with abstract docstrings.)
+        raise NotImplementedError("__call__ must be implemented.")
diff --git a/.venv/lib/python3.12/site-packages/google/auth/transport/_aiohttp_requests.py b/.venv/lib/python3.12/site-packages/google/auth/transport/_aiohttp_requests.py
new file mode 100644
index 00000000..bc4d9dc6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/transport/_aiohttp_requests.py
@@ -0,0 +1,391 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Transport adapter for Async HTTP (aiohttp).
+
+NOTE: This async support is experimental and marked internal. This surface may
+change in minor releases.
+"""
+
+from __future__ import absolute_import
+
+import asyncio
+import functools
+
+import aiohttp  # type: ignore
+import urllib3  # type: ignore
+
+from google.auth import exceptions
+from google.auth import transport
+from google.auth.transport import requests
+
+# Timeout can be re-defined depending on async requirement. Currently made 60s more than
+# sync timeout.
+_DEFAULT_TIMEOUT = 180  # in seconds
+
+
+class _CombinedResponse(transport.Response):
+    """
+    In order to more closely resemble the `requests` interface, where a raw
+    and deflated content could be accessed at once, this class lazily reads the
+    stream in `transport.Response` so both return forms can be used.
+
+    The gzip and deflate transfer-encodings are automatically decoded for you
+    because the default parameter for autodecompress into the ClientSession is set
+    to False, and therefore we add this class to act as a wrapper for a user to be
+    able to access both the raw and decoded response bodies - mirroring the sync
+    implementation.
+    """
+
+    def __init__(self, response):
+        self._response = response
+        self._raw_content = None
+
+    def _is_compressed(self):
+        headers = self._response.headers
+        return "Content-Encoding" in headers and (
+            headers["Content-Encoding"] == "gzip"
+            or headers["Content-Encoding"] == "deflate"
+        )
+
+    @property
+    def status(self):
+        return self._response.status
+
+    @property
+    def headers(self):
+        return self._response.headers
+
+    @property
+    def data(self):
+        return self._response.content
+
+    async def raw_content(self):
+        if self._raw_content is None:
+            self._raw_content = await self._response.content.read()
+        return self._raw_content
+
+    async def content(self):
+        # Load raw_content if necessary
+        await self.raw_content()
+        if self._is_compressed():
+            decoder = urllib3.response.MultiDecoder(
+                self._response.headers["Content-Encoding"]
+            )
+            decompressed = decoder.decompress(self._raw_content)
+            return decompressed
+
+        return self._raw_content
+
+
+class _Response(transport.Response):
+    """
+    Requests transport response adapter.
+
+    Args:
+        response (requests.Response): The raw Requests response.
+    """
+
+    def __init__(self, response):
+        self._response = response
+
+    @property
+    def status(self):
+        return self._response.status
+
+    @property
+    def headers(self):
+        return self._response.headers
+
+    @property
+    def data(self):
+        return self._response.content
+
+
+class Request(transport.Request):
+    """Requests request adapter.
+
+    This class is used internally for making requests using asyncio transports
+    in a consistent way. If you use :class:`AuthorizedSession` you do not need
+    to construct or use this class directly.
+
+    This class can be useful if you want to manually refresh a
+    :class:`~google.auth.credentials.Credentials` instance::
+
+        import google.auth.transport.aiohttp_requests
+
+        request = google.auth.transport.aiohttp_requests.Request()
+
+        credentials.refresh(request)
+
+    Args:
+        session (aiohttp.ClientSession): An instance :class:`aiohttp.ClientSession` used
+            to make HTTP requests. If not specified, a session will be created.
+
+    .. automethod:: __call__
+    """
+
+    def __init__(self, session=None):
+        # TODO: Use auto_decompress property for aiohttp 3.7+
+        if session is not None and session._auto_decompress:
+            raise exceptions.InvalidOperation(
+                "Client sessions with auto_decompress=True are not supported."
+            )
+        self.session = session
+
+    async def __call__(
+        self,
+        url,
+        method="GET",
+        body=None,
+        headers=None,
+        timeout=_DEFAULT_TIMEOUT,
+        **kwargs,
+    ):
+        """
+        Make an HTTP request using aiohttp.
+
+        Args:
+            url (str): The URL to be requested.
+            method (Optional[str]):
+                The HTTP method to use for the request. Defaults to 'GET'.
+            body (Optional[bytes]):
+                The payload or body in HTTP request.
+            headers (Optional[Mapping[str, str]]):
+                Request headers.
+            timeout (Optional[int]): The number of seconds to wait for a
+                response from the server. If not specified or if None, the
+                requests default timeout will be used.
+            kwargs: Additional arguments passed through to the underlying
+                requests :meth:`requests.Session.request` method.
+
+        Returns:
+            google.auth.transport.Response: The HTTP response.
+
+        Raises:
+            google.auth.exceptions.TransportError: If any exception occurred.
+        """
+
+        try:
+            if self.session is None:  # pragma: NO COVER
+                self.session = aiohttp.ClientSession(
+                    auto_decompress=False
+                )  # pragma: NO COVER
+            requests._LOGGER.debug("Making request: %s %s", method, url)
+            response = await self.session.request(
+                method, url, data=body, headers=headers, timeout=timeout, **kwargs
+            )
+            return _CombinedResponse(response)
+
+        except aiohttp.ClientError as caught_exc:
+            new_exc = exceptions.TransportError(caught_exc)
+            raise new_exc from caught_exc
+
+        except asyncio.TimeoutError as caught_exc:
+            new_exc = exceptions.TransportError(caught_exc)
+            raise new_exc from caught_exc
+
+
+class AuthorizedSession(aiohttp.ClientSession):
+    """This is an async implementation of the Authorized Session class. We utilize an
+    aiohttp transport instance, and the interface mirrors the google.auth.transport.requests
+    Authorized Session class, except for the change in the transport used in the async use case.
+
+    A Requests Session class with credentials.
+
+    This class is used to perform requests to API endpoints that require
+    authorization::
+
+        from google.auth.transport import aiohttp_requests
+
+        async with aiohttp_requests.AuthorizedSession(credentials) as authed_session:
+            response = await authed_session.request(
+                'GET', 'https://www.googleapis.com/storage/v1/b')
+
+    The underlying :meth:`request` implementation handles adding the
+    credentials' headers to the request and refreshing credentials as needed.
+
+    Args:
+        credentials (google.auth._credentials_async.Credentials):
+            The credentials to add to the request.
+        refresh_status_codes (Sequence[int]): Which HTTP status codes indicate
+            that credentials should be refreshed and the request should be
+            retried.
+        max_refresh_attempts (int): The maximum number of times to attempt to
+            refresh the credentials and retry the request.
+        refresh_timeout (Optional[int]): The timeout value in seconds for
+            credential refresh HTTP requests.
+        auth_request (google.auth.transport.aiohttp_requests.Request):
+            (Optional) An instance of
+            :class:`~google.auth.transport.aiohttp_requests.Request` used when
+            refreshing credentials. If not passed,
+            an instance of :class:`~google.auth.transport.aiohttp_requests.Request`
+            is created.
+        kwargs: Additional arguments passed through to the underlying
+            ClientSession :meth:`aiohttp.ClientSession` object.
+    """
+
+    def __init__(
+        self,
+        credentials,
+        refresh_status_codes=transport.DEFAULT_REFRESH_STATUS_CODES,
+        max_refresh_attempts=transport.DEFAULT_MAX_REFRESH_ATTEMPTS,
+        refresh_timeout=None,
+        auth_request=None,
+        auto_decompress=False,
+        **kwargs,
+    ):
+        super(AuthorizedSession, self).__init__(**kwargs)
+        self.credentials = credentials
+        self._refresh_status_codes = refresh_status_codes
+        self._max_refresh_attempts = max_refresh_attempts
+        self._refresh_timeout = refresh_timeout
+        self._is_mtls = False
+        self._auth_request = auth_request
+        self._auth_request_session = None
+        self._loop = asyncio.get_event_loop()
+        self._refresh_lock = asyncio.Lock()
+        self._auto_decompress = auto_decompress
+
+    async def request(
+        self,
+        method,
+        url,
+        data=None,
+        headers=None,
+        max_allowed_time=None,
+        timeout=_DEFAULT_TIMEOUT,
+        auto_decompress=False,
+        **kwargs,
+    ):
+
+        """Implementation of Authorized Session aiohttp request.
+
+        Args:
+            method (str):
+                The http request method used (e.g. GET, PUT, DELETE)
+            url (str):
+                The url at which the http request is sent.
+            data (Optional[dict]): Dictionary, list of tuples, bytes, or file-like
+                object to send in the body of the Request.
+            headers (Optional[dict]): Dictionary of HTTP Headers to send with the
+                Request.
+            timeout (Optional[Union[float, aiohttp.ClientTimeout]]):
+                The amount of time in seconds to wait for the server response
+                with each individual request. Can also be passed as an
+                ``aiohttp.ClientTimeout`` object.
+            max_allowed_time (Optional[float]):
+                If the method runs longer than this, a ``Timeout`` exception is
+                automatically raised. Unlike the ``timeout`` parameter, this
+                value applies to the total method execution time, even if
+                multiple requests are made under the hood.
+
+                Mind that it is not guaranteed that the timeout error is raised
+                at ``max_allowed_time``. It might take longer, for example, if
+                an underlying request takes a lot of time, but the request
+                itself does not timeout, e.g. if a large file is being
+                transmitted. The timout error will be raised after such
+                request completes.
+        """
+        # Headers come in as bytes which isn't expected behavior, the resumable
+        # media libraries in some cases expect a str type for the header values,
+        # but sometimes the operations return these in bytes types.
+        if headers:
+            for key in headers.keys():
+                if type(headers[key]) is bytes:
+                    headers[key] = headers[key].decode("utf-8")
+
+        async with aiohttp.ClientSession(
+            auto_decompress=self._auto_decompress,
+            trust_env=kwargs.get("trust_env", False),
+        ) as self._auth_request_session:
+            auth_request = Request(self._auth_request_session)
+            self._auth_request = auth_request
+
+            # Use a kwarg for this instead of an attribute to maintain
+            # thread-safety.
+            _credential_refresh_attempt = kwargs.pop("_credential_refresh_attempt", 0)
+            # Make a copy of the headers. They will be modified by the credentials
+            # and we want to pass the original headers if we recurse.
+            request_headers = headers.copy() if headers is not None else {}
+
+            # Do not apply the timeout unconditionally in order to not override the
+            # _auth_request's default timeout.
+            auth_request = (
+                self._auth_request
+                if timeout is None
+                else functools.partial(self._auth_request, timeout=timeout)
+            )
+
+            remaining_time = max_allowed_time
+
+            with requests.TimeoutGuard(remaining_time, asyncio.TimeoutError) as guard:
+                await self.credentials.before_request(
+                    auth_request, method, url, request_headers
+                )
+
+            with requests.TimeoutGuard(remaining_time, asyncio.TimeoutError) as guard:
+                response = await super(AuthorizedSession, self).request(
+                    method,
+                    url,
+                    data=data,
+                    headers=request_headers,
+                    timeout=timeout,
+                    **kwargs,
+                )
+
+            remaining_time = guard.remaining_timeout
+
+            if (
+                response.status in self._refresh_status_codes
+                and _credential_refresh_attempt < self._max_refresh_attempts
+            ):
+
+                requests._LOGGER.info(
+                    "Refreshing credentials due to a %s response. Attempt %s/%s.",
+                    response.status,
+                    _credential_refresh_attempt + 1,
+                    self._max_refresh_attempts,
+                )
+
+                # Do not apply the timeout unconditionally in order to not override the
+                # _auth_request's default timeout.
+                auth_request = (
+                    self._auth_request
+                    if timeout is None
+                    else functools.partial(self._auth_request, timeout=timeout)
+                )
+
+                with requests.TimeoutGuard(
+                    remaining_time, asyncio.TimeoutError
+                ) as guard:
+                    async with self._refresh_lock:
+                        await self._loop.run_in_executor(
+                            None, self.credentials.refresh, auth_request
+                        )
+
+                remaining_time = guard.remaining_timeout
+
+                return await self.request(
+                    method,
+                    url,
+                    data=data,
+                    headers=headers,
+                    max_allowed_time=remaining_time,
+                    timeout=timeout,
+                    _credential_refresh_attempt=_credential_refresh_attempt + 1,
+                    **kwargs,
+                )
+
+        return response
diff --git a/.venv/lib/python3.12/site-packages/google/auth/transport/_custom_tls_signer.py b/.venv/lib/python3.12/site-packages/google/auth/transport/_custom_tls_signer.py
new file mode 100644
index 00000000..9279158d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/transport/_custom_tls_signer.py
@@ -0,0 +1,283 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Code for configuring client side TLS to offload the signing operation to
+signing libraries.
+"""
+
+import ctypes
+import json
+import logging
+import os
+import sys
+
+import cffi  # type: ignore
+
+from google.auth import exceptions
+
+_LOGGER = logging.getLogger(__name__)
+
+# C++ offload lib requires google-auth lib to provide the following callback:
+#     using SignFunc = int (*)(unsigned char *sig, size_t *sig_len,
+#             const unsigned char *tbs, size_t tbs_len)
+# The bytes to be signed and the length are provided via `tbs` and `tbs_len`,
+# the callback computes the signature, and write the signature and its length
+# into `sig` and `sig_len`.
+# If the signing is successful, the callback returns 1, otherwise it returns 0.
+SIGN_CALLBACK_CTYPE = ctypes.CFUNCTYPE(
+    ctypes.c_int,  # return type
+    ctypes.POINTER(ctypes.c_ubyte),  # sig
+    ctypes.POINTER(ctypes.c_size_t),  # sig_len
+    ctypes.POINTER(ctypes.c_ubyte),  # tbs
+    ctypes.c_size_t,  # tbs_len
+)
+
+
+# Cast SSL_CTX* to void*
+def _cast_ssl_ctx_to_void_p_pyopenssl(ssl_ctx):
+    return ctypes.cast(int(cffi.FFI().cast("intptr_t", ssl_ctx)), ctypes.c_void_p)
+
+
+# Cast SSL_CTX* to void*
+def _cast_ssl_ctx_to_void_p_stdlib(context):
+    return ctypes.c_void_p.from_address(
+        id(context) + ctypes.sizeof(ctypes.c_void_p) * 2
+    )
+
+
+# Load offload library and set up the function types.
+def load_offload_lib(offload_lib_path):
+    _LOGGER.debug("loading offload library from %s", offload_lib_path)
+
+    # winmode parameter is only available for python 3.8+.
+    lib = (
+        ctypes.CDLL(offload_lib_path, winmode=0)
+        if sys.version_info >= (3, 8) and os.name == "nt"
+        else ctypes.CDLL(offload_lib_path)
+    )
+
+    # Set up types for:
+    # int ConfigureSslContext(SignFunc sign_func, const char *cert, SSL_CTX *ctx)
+    lib.ConfigureSslContext.argtypes = [
+        SIGN_CALLBACK_CTYPE,
+        ctypes.c_char_p,
+        ctypes.c_void_p,
+    ]
+    lib.ConfigureSslContext.restype = ctypes.c_int
+
+    return lib
+
+
+# Load signer library and set up the function types.
+# See: https://github.com/googleapis/enterprise-certificate-proxy/blob/main/cshared/main.go
+def load_signer_lib(signer_lib_path):
+    _LOGGER.debug("loading signer library from %s", signer_lib_path)
+
+    # winmode parameter is only available for python 3.8+.
+    lib = (
+        ctypes.CDLL(signer_lib_path, winmode=0)
+        if sys.version_info >= (3, 8) and os.name == "nt"
+        else ctypes.CDLL(signer_lib_path)
+    )
+
+    # Set up types for:
+    # func GetCertPemForPython(configFilePath *C.char, certHolder *byte, certHolderLen int)
+    lib.GetCertPemForPython.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int]
+    # Returns: certLen
+    lib.GetCertPemForPython.restype = ctypes.c_int
+
+    # Set up types for:
+    # func SignForPython(configFilePath *C.char, digest *byte, digestLen int,
+    #     sigHolder *byte, sigHolderLen int)
+    lib.SignForPython.argtypes = [
+        ctypes.c_char_p,
+        ctypes.c_char_p,
+        ctypes.c_int,
+        ctypes.c_char_p,
+        ctypes.c_int,
+    ]
+    # Returns: the signature length
+    lib.SignForPython.restype = ctypes.c_int
+
+    return lib
+
+
+def load_provider_lib(provider_lib_path):
+    _LOGGER.debug("loading provider library from %s", provider_lib_path)
+
+    # winmode parameter is only available for python 3.8+.
+    lib = (
+        ctypes.CDLL(provider_lib_path, winmode=0)
+        if sys.version_info >= (3, 8) and os.name == "nt"
+        else ctypes.CDLL(provider_lib_path)
+    )
+
+    lib.ECP_attach_to_ctx.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
+    lib.ECP_attach_to_ctx.restype = ctypes.c_int
+
+    return lib
+
+
+# Computes SHA256 hash.
+def _compute_sha256_digest(to_be_signed, to_be_signed_len):
+    from cryptography.hazmat.primitives import hashes
+
+    data = ctypes.string_at(to_be_signed, to_be_signed_len)
+    hash = hashes.Hash(hashes.SHA256())
+    hash.update(data)
+    return hash.finalize()
+
+
+# Create the signing callback. The actual signing work is done by the
+# `SignForPython` method from the signer lib.
+def get_sign_callback(signer_lib, config_file_path):
+    def sign_callback(sig, sig_len, tbs, tbs_len):
+        _LOGGER.debug("calling sign callback...")
+
+        digest = _compute_sha256_digest(tbs, tbs_len)
+        digestArray = ctypes.c_char * len(digest)
+
+        # reserve 2000 bytes for the signature, shoud be more then enough.
+        # RSA signature is 256 bytes, EC signature is 70~72.
+        sig_holder_len = 2000
+        sig_holder = ctypes.create_string_buffer(sig_holder_len)
+
+        signature_len = signer_lib.SignForPython(
+            config_file_path.encode(),  # configFilePath
+            digestArray.from_buffer(bytearray(digest)),  # digest
+            len(digest),  # digestLen
+            sig_holder,  # sigHolder
+            sig_holder_len,  # sigHolderLen
+        )
+
+        if signature_len == 0:
+            # signing failed, return 0
+            return 0
+
+        sig_len[0] = signature_len
+        bs = bytearray(sig_holder)
+        for i in range(signature_len):
+            sig[i] = bs[i]
+
+        return 1
+
+    return SIGN_CALLBACK_CTYPE(sign_callback)
+
+
+# Obtain the certificate bytes by calling the `GetCertPemForPython` method from
+# the signer lib. The method is called twice, the first time is to compute the
+# cert length, then we create a buffer to hold the cert, and call it again to
+# fill the buffer.
+def get_cert(signer_lib, config_file_path):
+    # First call to calculate the cert length
+    cert_len = signer_lib.GetCertPemForPython(
+        config_file_path.encode(),  # configFilePath
+        None,  # certHolder
+        0,  # certHolderLen
+    )
+    if cert_len == 0:
+        raise exceptions.MutualTLSChannelError("failed to get certificate")
+
+    # Then we create an array to hold the cert, and call again to fill the cert
+    cert_holder = ctypes.create_string_buffer(cert_len)
+    signer_lib.GetCertPemForPython(
+        config_file_path.encode(),  # configFilePath
+        cert_holder,  # certHolder
+        cert_len,  # certHolderLen
+    )
+    return bytes(cert_holder)
+
+
+class CustomTlsSigner(object):
+    def __init__(self, enterprise_cert_file_path):
+        """
+        This class loads the offload and signer library, and calls APIs from
+        these libraries to obtain the cert and a signing callback, and attach
+        them to SSL context. The cert and the signing callback will be used
+        for client authentication in TLS handshake.
+
+        Args:
+            enterprise_cert_file_path (str): the path to a enterprise cert JSON
+                file. The file should contain the following field:
+
+                    {
+                        "libs": {
+                            "ecp_client": "...",
+                            "tls_offload": "..."
+                        }
+                    }
+        """
+        self._enterprise_cert_file_path = enterprise_cert_file_path
+        self._cert = None
+        self._sign_callback = None
+        self._provider_lib = None
+
+    def load_libraries(self):
+        with open(self._enterprise_cert_file_path, "r") as f:
+            enterprise_cert_json = json.load(f)
+            libs = enterprise_cert_json.get("libs", {})
+
+            signer_library = libs.get("ecp_client", None)
+            offload_library = libs.get("tls_offload", None)
+            provider_library = libs.get("ecp_provider", None)
+
+        # Using newer provider implementation. This is mutually exclusive to the
+        # offload implementation.
+        if provider_library:
+            self._provider_lib = load_provider_lib(provider_library)
+            return
+
+        # Using old offload implementation
+        if offload_library and signer_library:
+            self._offload_lib = load_offload_lib(offload_library)
+            self._signer_lib = load_signer_lib(signer_library)
+            self.set_up_custom_key()
+            return
+
+        raise exceptions.MutualTLSChannelError("enterprise cert file is invalid")
+
+    def set_up_custom_key(self):
+        # We need to keep a reference of the cert and sign callback so it won't
+        # be garbage collected, otherwise it will crash when used by signer lib.
+        self._cert = get_cert(self._signer_lib, self._enterprise_cert_file_path)
+        self._sign_callback = get_sign_callback(
+            self._signer_lib, self._enterprise_cert_file_path
+        )
+
+    def should_use_provider(self):
+        if self._provider_lib:
+            return True
+        return False
+
+    def attach_to_ssl_context(self, ctx):
+        if self.should_use_provider():
+            if not self._provider_lib.ECP_attach_to_ctx(
+                _cast_ssl_ctx_to_void_p_stdlib(ctx),
+                self._enterprise_cert_file_path.encode("ascii"),
+            ):
+                raise exceptions.MutualTLSChannelError(
+                    "failed to configure ECP Provider SSL context"
+                )
+        elif self._offload_lib and self._signer_lib:
+            if not self._offload_lib.ConfigureSslContext(
+                self._sign_callback,
+                ctypes.c_char_p(self._cert),
+                _cast_ssl_ctx_to_void_p_pyopenssl(ctx._ctx._context),
+            ):
+                raise exceptions.MutualTLSChannelError(
+                    "failed to configure ECP Offload SSL context"
+                )
+        else:
+            raise exceptions.MutualTLSChannelError("Invalid ECP configuration.")
diff --git a/.venv/lib/python3.12/site-packages/google/auth/transport/_http_client.py b/.venv/lib/python3.12/site-packages/google/auth/transport/_http_client.py
new file mode 100644
index 00000000..cec0ab73
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/transport/_http_client.py
@@ -0,0 +1,113 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Transport adapter for http.client, for internal use only."""
+
+import http.client as http_client
+import logging
+import socket
+import urllib
+
+from google.auth import exceptions
+from google.auth import transport
+
+_LOGGER = logging.getLogger(__name__)
+
+
+class Response(transport.Response):
+    """http.client transport response adapter.
+
+    Args:
+        response (http.client.HTTPResponse): The raw http client response.
+    """
+
+    def __init__(self, response):
+        self._status = response.status
+        self._headers = {key.lower(): value for key, value in response.getheaders()}
+        self._data = response.read()
+
+    @property
+    def status(self):
+        return self._status
+
+    @property
+    def headers(self):
+        return self._headers
+
+    @property
+    def data(self):
+        return self._data
+
+
+class Request(transport.Request):
+    """http.client transport request adapter."""
+
+    def __call__(
+        self, url, method="GET", body=None, headers=None, timeout=None, **kwargs
+    ):
+        """Make an HTTP request using http.client.
+
+        Args:
+            url (str): The URI to be requested.
+            method (str): The HTTP method to use for the request. Defaults
+                to 'GET'.
+            body (bytes): The payload / body in HTTP request.
+            headers (Mapping): Request headers.
+            timeout (Optional(int)): The number of seconds to wait for a
+                response from the server. If not specified or if None, the
+                socket global default timeout will be used.
+            kwargs: Additional arguments passed throught to the underlying
+                :meth:`~http.client.HTTPConnection.request` method.
+
+        Returns:
+            Response: The HTTP response.
+
+        Raises:
+            google.auth.exceptions.TransportError: If any exception occurred.
+        """
+        # socket._GLOBAL_DEFAULT_TIMEOUT is the default in http.client.
+        if timeout is None:
+            timeout = socket._GLOBAL_DEFAULT_TIMEOUT
+
+        # http.client doesn't allow None as the headers argument.
+        if headers is None:
+            headers = {}
+
+        # http.client needs the host and path parts specified separately.
+        parts = urllib.parse.urlsplit(url)
+        path = urllib.parse.urlunsplit(
+            ("", "", parts.path, parts.query, parts.fragment)
+        )
+
+        if parts.scheme != "http":
+            raise exceptions.TransportError(
+                "http.client transport only supports the http scheme, {}"
+                "was specified".format(parts.scheme)
+            )
+
+        connection = http_client.HTTPConnection(parts.netloc, timeout=timeout)
+
+        try:
+            _LOGGER.debug("Making request: %s %s", method, url)
+
+            connection.request(method, path, body=body, headers=headers, **kwargs)
+            response = connection.getresponse()
+            return Response(response)
+
+        except (http_client.HTTPException, socket.error) as caught_exc:
+            new_exc = exceptions.TransportError(caught_exc)
+            raise new_exc from caught_exc
+
+        finally:
+            connection.close()
diff --git a/.venv/lib/python3.12/site-packages/google/auth/transport/_mtls_helper.py b/.venv/lib/python3.12/site-packages/google/auth/transport/_mtls_helper.py
new file mode 100644
index 00000000..68568dd6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/transport/_mtls_helper.py
@@ -0,0 +1,407 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Helper functions for getting mTLS cert and key."""
+
+import json
+import logging
+from os import environ, path
+import re
+import subprocess
+
+from google.auth import exceptions
+
+CONTEXT_AWARE_METADATA_PATH = "~/.secureConnect/context_aware_metadata.json"
+CERTIFICATE_CONFIGURATION_DEFAULT_PATH = "~/.config/gcloud/certificate_config.json"
+_CERTIFICATE_CONFIGURATION_ENV = "GOOGLE_API_CERTIFICATE_CONFIG"
+_CERT_PROVIDER_COMMAND = "cert_provider_command"
+_CERT_REGEX = re.compile(
+    b"-----BEGIN CERTIFICATE-----.+-----END CERTIFICATE-----\r?\n?", re.DOTALL
+)
+
+# support various format of key files, e.g.
+# "-----BEGIN PRIVATE KEY-----...",
+# "-----BEGIN EC PRIVATE KEY-----...",
+# "-----BEGIN RSA PRIVATE KEY-----..."
+# "-----BEGIN ENCRYPTED PRIVATE KEY-----"
+_KEY_REGEX = re.compile(
+    b"-----BEGIN [A-Z ]*PRIVATE KEY-----.+-----END [A-Z ]*PRIVATE KEY-----\r?\n?",
+    re.DOTALL,
+)
+
+_LOGGER = logging.getLogger(__name__)
+
+
+_PASSPHRASE_REGEX = re.compile(
+    b"-----BEGIN PASSPHRASE-----(.+)-----END PASSPHRASE-----", re.DOTALL
+)
+
+
+def _check_config_path(config_path):
+    """Checks for config file path. If it exists, returns the absolute path with user expansion;
+    otherwise returns None.
+
+    Args:
+        config_path (str): The config file path for either context_aware_metadata.json or certificate_config.json for example
+
+    Returns:
+        str: absolute path if exists and None otherwise.
+    """
+    config_path = path.expanduser(config_path)
+    if not path.exists(config_path):
+        _LOGGER.debug("%s is not found.", config_path)
+        return None
+    return config_path
+
+
+def _load_json_file(path):
+    """Reads and loads JSON from the given path. Used to read both X509 workload certificate and
+    secure connect configurations.
+
+    Args:
+        path (str): the path to read from.
+
+    Returns:
+        Dict[str, str]: The JSON stored at the file.
+
+    Raises:
+        google.auth.exceptions.ClientCertError: If failed to parse the file as JSON.
+    """
+    try:
+        with open(path) as f:
+            json_data = json.load(f)
+    except ValueError as caught_exc:
+        new_exc = exceptions.ClientCertError(caught_exc)
+        raise new_exc from caught_exc
+
+    return json_data
+
+
+def _get_workload_cert_and_key(certificate_config_path=None):
+    """Read the workload identity cert and key files specified in the certificate config provided.
+    If no config path is provided, check the environment variable: "GOOGLE_API_CERTIFICATE_CONFIG"
+    first, then the well known gcloud location: "~/.config/gcloud/certificate_config.json".
+
+    Args:
+        certificate_config_path (string): The certificate config path. If no path is provided,
+        the environment variable will be checked first, then the well known gcloud location.
+
+    Returns:
+        Tuple[Optional[bytes], Optional[bytes]]: client certificate bytes in PEM format and key
+            bytes in PEM format.
+
+    Raises:
+        google.auth.exceptions.ClientCertError: if problems occurs when retrieving
+        the certificate or key information.
+    """
+
+    cert_path, key_path = _get_workload_cert_and_key_paths(certificate_config_path)
+
+    if cert_path is None and key_path is None:
+        return None, None
+
+    return _read_cert_and_key_files(cert_path, key_path)
+
+
+def _get_cert_config_path(certificate_config_path=None):
+    """Get the certificate configuration path based on the following order:
+
+    1: Explicit override, if set
+    2: Environment variable, if set
+    3: Well-known location
+
+    Returns "None" if the selected config file does not exist.
+
+    Args:
+        certificate_config_path (string): The certificate config path. If provided, the well known
+        location and environment variable will be ignored.
+
+    Returns:
+        The absolute path of the certificate config file, and None if the file does not exist.
+    """
+
+    if certificate_config_path is None:
+        env_path = environ.get(_CERTIFICATE_CONFIGURATION_ENV, None)
+        if env_path is not None and env_path != "":
+            certificate_config_path = env_path
+        else:
+            certificate_config_path = CERTIFICATE_CONFIGURATION_DEFAULT_PATH
+
+    certificate_config_path = path.expanduser(certificate_config_path)
+    if not path.exists(certificate_config_path):
+        return None
+    return certificate_config_path
+
+
+def _get_workload_cert_and_key_paths(config_path):
+    absolute_path = _get_cert_config_path(config_path)
+    if absolute_path is None:
+        return None, None
+
+    data = _load_json_file(absolute_path)
+
+    if "cert_configs" not in data:
+        raise exceptions.ClientCertError(
+            'Certificate config file {} is in an invalid format, a "cert configs" object is expected'.format(
+                absolute_path
+            )
+        )
+    cert_configs = data["cert_configs"]
+
+    if "workload" not in cert_configs:
+        raise exceptions.ClientCertError(
+            'Certificate config file {} is in an invalid format, a "workload" cert config is expected'.format(
+                absolute_path
+            )
+        )
+    workload = cert_configs["workload"]
+
+    if "cert_path" not in workload:
+        raise exceptions.ClientCertError(
+            'Certificate config file {} is in an invalid format, a "cert_path" is expected in the workload cert config'.format(
+                absolute_path
+            )
+        )
+    cert_path = workload["cert_path"]
+
+    if "key_path" not in workload:
+        raise exceptions.ClientCertError(
+            'Certificate config file {} is in an invalid format, a "key_path" is expected in the workload cert config'.format(
+                absolute_path
+            )
+        )
+    key_path = workload["key_path"]
+
+    return cert_path, key_path
+
+
+def _read_cert_and_key_files(cert_path, key_path):
+    cert_data = _read_cert_file(cert_path)
+    key_data = _read_key_file(key_path)
+
+    return cert_data, key_data
+
+
+def _read_cert_file(cert_path):
+    with open(cert_path, "rb") as cert_file:
+        cert_data = cert_file.read()
+
+    cert_match = re.findall(_CERT_REGEX, cert_data)
+    if len(cert_match) != 1:
+        raise exceptions.ClientCertError(
+            "Certificate file {} is in an invalid format, a single PEM formatted certificate is expected".format(
+                cert_path
+            )
+        )
+    return cert_match[0]
+
+
+def _read_key_file(key_path):
+    with open(key_path, "rb") as key_file:
+        key_data = key_file.read()
+
+    key_match = re.findall(_KEY_REGEX, key_data)
+    if len(key_match) != 1:
+        raise exceptions.ClientCertError(
+            "Private key file {} is in an invalid format, a single PEM formatted private key is expected".format(
+                key_path
+            )
+        )
+
+    return key_match[0]
+
+
+def _run_cert_provider_command(command, expect_encrypted_key=False):
+    """Run the provided command, and return client side mTLS cert, key and
+    passphrase.
+
+    Args:
+        command (List[str]): cert provider command.
+        expect_encrypted_key (bool): If encrypted private key is expected.
+
+    Returns:
+        Tuple[bytes, bytes, bytes]: client certificate bytes in PEM format, key
+            bytes in PEM format and passphrase bytes.
+
+    Raises:
+        google.auth.exceptions.ClientCertError: if problems occurs when running
+            the cert provider command or generating cert, key and passphrase.
+    """
+    try:
+        process = subprocess.Popen(
+            command, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+        )
+        stdout, stderr = process.communicate()
+    except OSError as caught_exc:
+        new_exc = exceptions.ClientCertError(caught_exc)
+        raise new_exc from caught_exc
+
+    # Check cert provider command execution error.
+    if process.returncode != 0:
+        raise exceptions.ClientCertError(
+            "Cert provider command returns non-zero status code %s" % process.returncode
+        )
+
+    # Extract certificate (chain), key and passphrase.
+    cert_match = re.findall(_CERT_REGEX, stdout)
+    if len(cert_match) != 1:
+        raise exceptions.ClientCertError("Client SSL certificate is missing or invalid")
+    key_match = re.findall(_KEY_REGEX, stdout)
+    if len(key_match) != 1:
+        raise exceptions.ClientCertError("Client SSL key is missing or invalid")
+    passphrase_match = re.findall(_PASSPHRASE_REGEX, stdout)
+
+    if expect_encrypted_key:
+        if len(passphrase_match) != 1:
+            raise exceptions.ClientCertError("Passphrase is missing or invalid")
+        if b"ENCRYPTED" not in key_match[0]:
+            raise exceptions.ClientCertError("Encrypted private key is expected")
+        return cert_match[0], key_match[0], passphrase_match[0].strip()
+
+    if b"ENCRYPTED" in key_match[0]:
+        raise exceptions.ClientCertError("Encrypted private key is not expected")
+    if len(passphrase_match) > 0:
+        raise exceptions.ClientCertError("Passphrase is not expected")
+    return cert_match[0], key_match[0], None
+
+
+def get_client_ssl_credentials(
+    generate_encrypted_key=False,
+    context_aware_metadata_path=CONTEXT_AWARE_METADATA_PATH,
+    certificate_config_path=CERTIFICATE_CONFIGURATION_DEFAULT_PATH,
+):
+    """Returns the client side certificate, private key and passphrase.
+
+    We look for certificates and keys with the following order of priority:
+        1. Certificate and key specified by certificate_config.json.
+               Currently, only X.509 workload certificates are supported.
+        2. Certificate and key specified by context aware metadata (i.e. SecureConnect).
+
+    Args:
+        generate_encrypted_key (bool): If set to True, encrypted private key
+            and passphrase will be generated; otherwise, unencrypted private key
+            will be generated and passphrase will be None. This option only
+            affects keys obtained via context_aware_metadata.json.
+        context_aware_metadata_path (str): The context_aware_metadata.json file path.
+        certificate_config_path (str): The certificate_config.json file path.
+
+    Returns:
+        Tuple[bool, bytes, bytes, bytes]:
+            A boolean indicating if cert, key and passphrase are obtained, the
+            cert bytes and key bytes both in PEM format, and passphrase bytes.
+
+    Raises:
+        google.auth.exceptions.ClientCertError: if problems occurs when getting
+            the cert, key and passphrase.
+    """
+
+    # 1. Check for certificate config json.
+    cert_config_path = _check_config_path(certificate_config_path)
+    if cert_config_path:
+        # Attempt to retrieve X.509 Workload cert and key.
+        cert, key = _get_workload_cert_and_key(cert_config_path)
+        if cert and key:
+            return True, cert, key, None
+
+    # 2. Check for context aware metadata json
+    metadata_path = _check_config_path(context_aware_metadata_path)
+
+    if metadata_path:
+        metadata_json = _load_json_file(metadata_path)
+
+        if _CERT_PROVIDER_COMMAND not in metadata_json:
+            raise exceptions.ClientCertError("Cert provider command is not found")
+
+        command = metadata_json[_CERT_PROVIDER_COMMAND]
+
+        if generate_encrypted_key and "--with_passphrase" not in command:
+            command.append("--with_passphrase")
+
+        # Execute the command.
+        cert, key, passphrase = _run_cert_provider_command(
+            command, expect_encrypted_key=generate_encrypted_key
+        )
+        return True, cert, key, passphrase
+
+    return False, None, None, None
+
+
+def get_client_cert_and_key(client_cert_callback=None):
+    """Returns the client side certificate and private key. The function first
+    tries to get certificate and key from client_cert_callback; if the callback
+    is None or doesn't provide certificate and key, the function tries application
+    default SSL credentials.
+
+    Args:
+        client_cert_callback (Optional[Callable[[], (bytes, bytes)]]): An
+            optional callback which returns client certificate bytes and private
+            key bytes both in PEM format.
+
+    Returns:
+        Tuple[bool, bytes, bytes]:
+            A boolean indicating if cert and key are obtained, the cert bytes
+            and key bytes both in PEM format.
+
+    Raises:
+        google.auth.exceptions.ClientCertError: if problems occurs when getting
+            the cert and key.
+    """
+    if client_cert_callback:
+        cert, key = client_cert_callback()
+        return True, cert, key
+
+    has_cert, cert, key, _ = get_client_ssl_credentials(generate_encrypted_key=False)
+    return has_cert, cert, key
+
+
+def decrypt_private_key(key, passphrase):
+    """A helper function to decrypt the private key with the given passphrase.
+    google-auth library doesn't support passphrase protected private key for
+    mutual TLS channel. This helper function can be used to decrypt the
+    passphrase protected private key in order to estalish mutual TLS channel.
+
+    For example, if you have a function which produces client cert, passphrase
+    protected private key and passphrase, you can convert it to a client cert
+    callback function accepted by google-auth::
+
+        from google.auth.transport import _mtls_helper
+
+        def your_client_cert_function():
+            return cert, encrypted_key, passphrase
+
+        # callback accepted by google-auth for mutual TLS channel.
+        def client_cert_callback():
+            cert, encrypted_key, passphrase = your_client_cert_function()
+            decrypted_key = _mtls_helper.decrypt_private_key(encrypted_key,
+                passphrase)
+            return cert, decrypted_key
+
+    Args:
+        key (bytes): The private key bytes in PEM format.
+        passphrase (bytes): The passphrase bytes.
+
+    Returns:
+        bytes: The decrypted private key in PEM format.
+
+    Raises:
+        ImportError: If pyOpenSSL is not installed.
+        OpenSSL.crypto.Error: If there is any problem decrypting the private key.
+    """
+    from OpenSSL import crypto
+
+    # First convert encrypted_key_bytes to PKey object
+    pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, key, passphrase=passphrase)
+
+    # Then dump the decrypted key bytes
+    return crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey)
diff --git a/.venv/lib/python3.12/site-packages/google/auth/transport/_requests_base.py b/.venv/lib/python3.12/site-packages/google/auth/transport/_requests_base.py
new file mode 100644
index 00000000..0608223d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/transport/_requests_base.py
@@ -0,0 +1,53 @@
+# Copyright 2024 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Transport adapter for Base Requests."""
+# NOTE: The coverage for this file is temporarily disabled in `.coveragerc`
+# since it is currently unused.
+
+import abc
+
+
+_DEFAULT_TIMEOUT = 120  # in second
+
+
+class _BaseAuthorizedSession(metaclass=abc.ABCMeta):
+    """Base class for a Request Session with credentials. This class is intended to capture
+    the common logic between synchronous and asynchronous request sessions and is not intended to
+    be instantiated directly.
+
+    Args:
+        credentials (google.auth._credentials_base.BaseCredentials): The credentials to
+            add to the request.
+    """
+
+    def __init__(self, credentials):
+        self.credentials = credentials
+
+    @abc.abstractmethod
+    def request(
+        self,
+        method,
+        url,
+        data=None,
+        headers=None,
+        max_allowed_time=None,
+        timeout=_DEFAULT_TIMEOUT,
+        **kwargs
+    ):
+        raise NotImplementedError("Request must be implemented")
+
+    @abc.abstractmethod
+    def close(self):
+        raise NotImplementedError("Close must be implemented")
diff --git a/.venv/lib/python3.12/site-packages/google/auth/transport/grpc.py b/.venv/lib/python3.12/site-packages/google/auth/transport/grpc.py
new file mode 100644
index 00000000..1ebe1379
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/transport/grpc.py
@@ -0,0 +1,343 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Authorization support for gRPC."""
+
+from __future__ import absolute_import
+
+import logging
+import os
+
+from google.auth import environment_vars
+from google.auth import exceptions
+from google.auth.transport import _mtls_helper
+from google.oauth2 import service_account
+
+try:
+    import grpc  # type: ignore
+except ImportError as caught_exc:  # pragma: NO COVER
+    raise ImportError(
+        "gRPC is not installed from please install the grpcio package to use the gRPC transport."
+    ) from caught_exc
+
+_LOGGER = logging.getLogger(__name__)
+
+
+class AuthMetadataPlugin(grpc.AuthMetadataPlugin):
+    """A `gRPC AuthMetadataPlugin`_ that inserts the credentials into each
+    request.
+
+    .. _gRPC AuthMetadataPlugin:
+        http://www.grpc.io/grpc/python/grpc.html#grpc.AuthMetadataPlugin
+
+    Args:
+        credentials (google.auth.credentials.Credentials): The credentials to
+            add to requests.
+        request (google.auth.transport.Request): A HTTP transport request
+            object used to refresh credentials as needed.
+        default_host (Optional[str]): A host like "pubsub.googleapis.com".
+            This is used when a self-signed JWT is created from service
+            account credentials.
+    """
+
+    def __init__(self, credentials, request, default_host=None):
+        # pylint: disable=no-value-for-parameter
+        # pylint doesn't realize that the super method takes no arguments
+        # because this class is the same name as the superclass.
+        super(AuthMetadataPlugin, self).__init__()
+        self._credentials = credentials
+        self._request = request
+        self._default_host = default_host
+
+    def _get_authorization_headers(self, context):
+        """Gets the authorization headers for a request.
+
+        Returns:
+            Sequence[Tuple[str, str]]: A list of request headers (key, value)
+                to add to the request.
+        """
+        headers = {}
+
+        # https://google.aip.dev/auth/4111
+        # Attempt to use self-signed JWTs when a service account is used.
+        # A default host must be explicitly provided since it cannot always
+        # be determined from the context.service_url.
+        if isinstance(self._credentials, service_account.Credentials):
+            self._credentials._create_self_signed_jwt(
+                "https://{}/".format(self._default_host) if self._default_host else None
+            )
+
+        self._credentials.before_request(
+            self._request, context.method_name, context.service_url, headers
+        )
+
+        return list(headers.items())
+
+    def __call__(self, context, callback):
+        """Passes authorization metadata into the given callback.
+
+        Args:
+            context (grpc.AuthMetadataContext): The RPC context.
+            callback (grpc.AuthMetadataPluginCallback): The callback that will
+                be invoked to pass in the authorization metadata.
+        """
+        callback(self._get_authorization_headers(context), None)
+
+
+def secure_authorized_channel(
+    credentials,
+    request,
+    target,
+    ssl_credentials=None,
+    client_cert_callback=None,
+    **kwargs
+):
+    """Creates a secure authorized gRPC channel.
+
+    This creates a channel with SSL and :class:`AuthMetadataPlugin`. This
+    channel can be used to create a stub that can make authorized requests.
+    Users can configure client certificate or rely on device certificates to
+    establish a mutual TLS channel, if the `GOOGLE_API_USE_CLIENT_CERTIFICATE`
+    variable is explicitly set to `true`.
+
+    Example::
+
+        import google.auth
+        import google.auth.transport.grpc
+        import google.auth.transport.requests
+        from google.cloud.speech.v1 import cloud_speech_pb2
+
+        # Get credentials.
+        credentials, _ = google.auth.default()
+
+        # Get an HTTP request function to refresh credentials.
+        request = google.auth.transport.requests.Request()
+
+        # Create a channel.
+        channel = google.auth.transport.grpc.secure_authorized_channel(
+            credentials, regular_endpoint, request,
+            ssl_credentials=grpc.ssl_channel_credentials())
+
+        # Use the channel to create a stub.
+        cloud_speech.create_Speech_stub(channel)
+
+    Usage:
+
+    There are actually a couple of options to create a channel, depending on if
+    you want to create a regular or mutual TLS channel.
+
+    First let's list the endpoints (regular vs mutual TLS) to choose from::
+
+        regular_endpoint = 'speech.googleapis.com:443'
+        mtls_endpoint = 'speech.mtls.googleapis.com:443'
+
+    Option 1: create a regular (non-mutual) TLS channel by explicitly setting
+    the ssl_credentials::
+
+        regular_ssl_credentials = grpc.ssl_channel_credentials()
+
+        channel = google.auth.transport.grpc.secure_authorized_channel(
+            credentials, regular_endpoint, request,
+            ssl_credentials=regular_ssl_credentials)
+
+    Option 2: create a mutual TLS channel by calling a callback which returns
+    the client side certificate and the key (Note that
+    `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be explicitly
+    set to `true`)::
+
+        def my_client_cert_callback():
+            code_to_load_client_cert_and_key()
+            if loaded:
+                return (pem_cert_bytes, pem_key_bytes)
+            raise MyClientCertFailureException()
+
+        try:
+            channel = google.auth.transport.grpc.secure_authorized_channel(
+                credentials, mtls_endpoint, request,
+                client_cert_callback=my_client_cert_callback)
+        except MyClientCertFailureException:
+            # handle the exception
+
+    Option 3: use application default SSL credentials. It searches and uses
+    the command in a context aware metadata file, which is available on devices
+    with endpoint verification support (Note that
+    `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable must be explicitly
+    set to `true`).
+    See https://cloud.google.com/endpoint-verification/docs/overview::
+
+        try:
+            default_ssl_credentials = SslCredentials()
+        except:
+            # Exception can be raised if the context aware metadata is malformed.
+            # See :class:`SslCredentials` for the possible exceptions.
+
+        # Choose the endpoint based on the SSL credentials type.
+        if default_ssl_credentials.is_mtls:
+            endpoint_to_use = mtls_endpoint
+        else:
+            endpoint_to_use = regular_endpoint
+        channel = google.auth.transport.grpc.secure_authorized_channel(
+            credentials, endpoint_to_use, request,
+            ssl_credentials=default_ssl_credentials)
+
+    Option 4: not setting ssl_credentials and client_cert_callback. For devices
+    without endpoint verification support or `GOOGLE_API_USE_CLIENT_CERTIFICATE`
+    environment variable is not `true`, a regular TLS channel is created;
+    otherwise, a mutual TLS channel is created, however, the call should be
+    wrapped in a try/except block in case of malformed context aware metadata.
+
+    The following code uses regular_endpoint, it works the same no matter the
+    created channle is regular or mutual TLS. Regular endpoint ignores client
+    certificate and key::
+
+        channel = google.auth.transport.grpc.secure_authorized_channel(
+            credentials, regular_endpoint, request)
+
+    The following code uses mtls_endpoint, if the created channle is regular,
+    and API mtls_endpoint is confgured to require client SSL credentials, API
+    calls using this channel will be rejected::
+
+        channel = google.auth.transport.grpc.secure_authorized_channel(
+            credentials, mtls_endpoint, request)
+
+    Args:
+        credentials (google.auth.credentials.Credentials): The credentials to
+            add to requests.
+        request (google.auth.transport.Request): A HTTP transport request
+            object used to refresh credentials as needed. Even though gRPC
+            is a separate transport, there's no way to refresh the credentials
+            without using a standard http transport.
+        target (str): The host and port of the service.
+        ssl_credentials (grpc.ChannelCredentials): Optional SSL channel
+            credentials. This can be used to specify different certificates.
+            This argument is mutually exclusive with client_cert_callback;
+            providing both will raise an exception.
+            If ssl_credentials and client_cert_callback are None, application
+            default SSL credentials are used if `GOOGLE_API_USE_CLIENT_CERTIFICATE`
+            environment variable is explicitly set to `true`, otherwise one way TLS
+            SSL credentials are used.
+        client_cert_callback (Callable[[], (bytes, bytes)]): Optional
+            callback function to obtain client certicate and key for mutual TLS
+            connection. This argument is mutually exclusive with
+            ssl_credentials; providing both will raise an exception.
+            This argument does nothing unless `GOOGLE_API_USE_CLIENT_CERTIFICATE`
+            environment variable is explicitly set to `true`.
+        kwargs: Additional arguments to pass to :func:`grpc.secure_channel`.
+
+    Returns:
+        grpc.Channel: The created gRPC channel.
+
+    Raises:
+        google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel
+            creation failed for any reason.
+    """
+    # Create the metadata plugin for inserting the authorization header.
+    metadata_plugin = AuthMetadataPlugin(credentials, request)
+
+    # Create a set of grpc.CallCredentials using the metadata plugin.
+    google_auth_credentials = grpc.metadata_call_credentials(metadata_plugin)
+
+    if ssl_credentials and client_cert_callback:
+        raise exceptions.MalformedError(
+            "Received both ssl_credentials and client_cert_callback; "
+            "these are mutually exclusive."
+        )
+
+    # If SSL credentials are not explicitly set, try client_cert_callback and ADC.
+    if not ssl_credentials:
+        use_client_cert = os.getenv(
+            environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE, "false"
+        )
+        if use_client_cert == "true" and client_cert_callback:
+            # Use the callback if provided.
+            cert, key = client_cert_callback()
+            ssl_credentials = grpc.ssl_channel_credentials(
+                certificate_chain=cert, private_key=key
+            )
+        elif use_client_cert == "true":
+            # Use application default SSL credentials.
+            adc_ssl_credentils = SslCredentials()
+            ssl_credentials = adc_ssl_credentils.ssl_credentials
+        else:
+            ssl_credentials = grpc.ssl_channel_credentials()
+
+    # Combine the ssl credentials and the authorization credentials.
+    composite_credentials = grpc.composite_channel_credentials(
+        ssl_credentials, google_auth_credentials
+    )
+
+    return grpc.secure_channel(target, composite_credentials, **kwargs)
+
+
+class SslCredentials:
+    """Class for application default SSL credentials.
+
+    The behavior is controlled by `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment
+    variable whose default value is `false`. Client certificate will not be used
+    unless the environment variable is explicitly set to `true`. See
+    https://google.aip.dev/auth/4114
+
+    If the environment variable is `true`, then for devices with endpoint verification
+    support, a device certificate will be automatically loaded and mutual TLS will
+    be established.
+    See https://cloud.google.com/endpoint-verification/docs/overview.
+    """
+
+    def __init__(self):
+        use_client_cert = os.getenv(
+            environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE, "false"
+        )
+        if use_client_cert != "true":
+            self._is_mtls = False
+        else:
+            # Load client SSL credentials.
+            metadata_path = _mtls_helper._check_config_path(
+                _mtls_helper.CONTEXT_AWARE_METADATA_PATH
+            )
+            self._is_mtls = metadata_path is not None
+
+    @property
+    def ssl_credentials(self):
+        """Get the created SSL channel credentials.
+
+        For devices with endpoint verification support, if the device certificate
+        loading has any problems, corresponding exceptions will be raised. For
+        a device without endpoint verification support, no exceptions will be
+        raised.
+
+        Returns:
+            grpc.ChannelCredentials: The created grpc channel credentials.
+
+        Raises:
+            google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel
+                creation failed for any reason.
+        """
+        if self._is_mtls:
+            try:
+                _, cert, key, _ = _mtls_helper.get_client_ssl_credentials()
+                self._ssl_credentials = grpc.ssl_channel_credentials(
+                    certificate_chain=cert, private_key=key
+                )
+            except exceptions.ClientCertError as caught_exc:
+                new_exc = exceptions.MutualTLSChannelError(caught_exc)
+                raise new_exc from caught_exc
+        else:
+            self._ssl_credentials = grpc.ssl_channel_credentials()
+
+        return self._ssl_credentials
+
+    @property
+    def is_mtls(self):
+        """Indicates if the created SSL channel credentials is mutual TLS."""
+        return self._is_mtls
diff --git a/.venv/lib/python3.12/site-packages/google/auth/transport/mtls.py b/.venv/lib/python3.12/site-packages/google/auth/transport/mtls.py
new file mode 100644
index 00000000..e7a7304f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/transport/mtls.py
@@ -0,0 +1,112 @@
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Utilites for mutual TLS."""
+
+from google.auth import exceptions
+from google.auth.transport import _mtls_helper
+
+
+def has_default_client_cert_source():
+    """Check if default client SSL credentials exists on the device.
+
+    Returns:
+        bool: indicating if the default client cert source exists.
+    """
+    if (
+        _mtls_helper._check_config_path(_mtls_helper.CONTEXT_AWARE_METADATA_PATH)
+        is not None
+    ):
+        return True
+    if (
+        _mtls_helper._check_config_path(
+            _mtls_helper.CERTIFICATE_CONFIGURATION_DEFAULT_PATH
+        )
+        is not None
+    ):
+        return True
+    return False
+
+
+def default_client_cert_source():
+    """Get a callback which returns the default client SSL credentials.
+
+    Returns:
+        Callable[[], [bytes, bytes]]: A callback which returns the default
+            client certificate bytes and private key bytes, both in PEM format.
+
+    Raises:
+        google.auth.exceptions.DefaultClientCertSourceError: If the default
+            client SSL credentials don't exist or are malformed.
+    """
+    if not has_default_client_cert_source():
+        raise exceptions.MutualTLSChannelError(
+            "Default client cert source doesn't exist"
+        )
+
+    def callback():
+        try:
+            _, cert_bytes, key_bytes = _mtls_helper.get_client_cert_and_key()
+        except (OSError, RuntimeError, ValueError) as caught_exc:
+            new_exc = exceptions.MutualTLSChannelError(caught_exc)
+            raise new_exc from caught_exc
+
+        return cert_bytes, key_bytes
+
+    return callback
+
+
+def default_client_encrypted_cert_source(cert_path, key_path):
+    """Get a callback which returns the default encrpyted client SSL credentials.
+
+    Args:
+        cert_path (str): The cert file path. The default client certificate will
+            be written to this file when the returned callback is called.
+        key_path (str): The key file path. The default encrypted client key will
+            be written to this file when the returned callback is called.
+
+    Returns:
+        Callable[[], [str, str, bytes]]: A callback which generates the default
+            client certificate, encrpyted private key and passphrase. It writes
+            the certificate and private key into the cert_path and key_path, and
+            returns the cert_path, key_path and passphrase bytes.
+
+    Raises:
+        google.auth.exceptions.DefaultClientCertSourceError: If any problem
+            occurs when loading or saving the client certificate and key.
+    """
+    if not has_default_client_cert_source():
+        raise exceptions.MutualTLSChannelError(
+            "Default client encrypted cert source doesn't exist"
+        )
+
+    def callback():
+        try:
+            (
+                _,
+                cert_bytes,
+                key_bytes,
+                passphrase_bytes,
+            ) = _mtls_helper.get_client_ssl_credentials(generate_encrypted_key=True)
+            with open(cert_path, "wb") as cert_file:
+                cert_file.write(cert_bytes)
+            with open(key_path, "wb") as key_file:
+                key_file.write(key_bytes)
+        except (exceptions.ClientCertError, OSError) as caught_exc:
+            new_exc = exceptions.MutualTLSChannelError(caught_exc)
+            raise new_exc from caught_exc
+
+        return cert_path, key_path, passphrase_bytes
+
+    return callback
diff --git a/.venv/lib/python3.12/site-packages/google/auth/transport/requests.py b/.venv/lib/python3.12/site-packages/google/auth/transport/requests.py
new file mode 100644
index 00000000..23a69783
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/transport/requests.py
@@ -0,0 +1,599 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Transport adapter for Requests."""
+
+from __future__ import absolute_import
+
+import functools
+import logging
+import numbers
+import os
+import time
+
+try:
+    import requests
+except ImportError as caught_exc:  # pragma: NO COVER
+    raise ImportError(
+        "The requests library is not installed from please install the requests package to use the requests transport."
+    ) from caught_exc
+import requests.adapters  # pylint: disable=ungrouped-imports
+import requests.exceptions  # pylint: disable=ungrouped-imports
+from requests.packages.urllib3.util.ssl_ import (  # type: ignore
+    create_urllib3_context,
+)  # pylint: disable=ungrouped-imports
+
+from google.auth import environment_vars
+from google.auth import exceptions
+from google.auth import transport
+import google.auth.transport._mtls_helper
+from google.oauth2 import service_account
+
+_LOGGER = logging.getLogger(__name__)
+
+_DEFAULT_TIMEOUT = 120  # in seconds
+
+
+class _Response(transport.Response):
+    """Requests transport response adapter.
+
+    Args:
+        response (requests.Response): The raw Requests response.
+    """
+
+    def __init__(self, response):
+        self._response = response
+
+    @property
+    def status(self):
+        return self._response.status_code
+
+    @property
+    def headers(self):
+        return self._response.headers
+
+    @property
+    def data(self):
+        return self._response.content
+
+
+class TimeoutGuard(object):
+    """A context manager raising an error if the suite execution took too long.
+
+    Args:
+        timeout (Union[None, Union[float, Tuple[float, float]]]):
+            The maximum number of seconds a suite can run without the context
+            manager raising a timeout exception on exit. If passed as a tuple,
+            the smaller of the values is taken as a timeout. If ``None``, a
+            timeout error is never raised.
+        timeout_error_type (Optional[Exception]):
+            The type of the error to raise on timeout. Defaults to
+            :class:`requests.exceptions.Timeout`.
+    """
+
+    def __init__(self, timeout, timeout_error_type=requests.exceptions.Timeout):
+        self._timeout = timeout
+        self.remaining_timeout = timeout
+        self._timeout_error_type = timeout_error_type
+
+    def __enter__(self):
+        self._start = time.time()
+        return self
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        if exc_value:
+            return  # let the error bubble up automatically
+
+        if self._timeout is None:
+            return  # nothing to do, the timeout was not specified
+
+        elapsed = time.time() - self._start
+        deadline_hit = False
+
+        if isinstance(self._timeout, numbers.Number):
+            self.remaining_timeout = self._timeout - elapsed
+            deadline_hit = self.remaining_timeout <= 0
+        else:
+            self.remaining_timeout = tuple(x - elapsed for x in self._timeout)
+            deadline_hit = min(self.remaining_timeout) <= 0
+
+        if deadline_hit:
+            raise self._timeout_error_type()
+
+
+class Request(transport.Request):
+    """Requests request adapter.
+
+    This class is used internally for making requests using various transports
+    in a consistent way. If you use :class:`AuthorizedSession` you do not need
+    to construct or use this class directly.
+
+    This class can be useful if you want to manually refresh a
+    :class:`~google.auth.credentials.Credentials` instance::
+
+        import google.auth.transport.requests
+        import requests
+
+        request = google.auth.transport.requests.Request()
+
+        credentials.refresh(request)
+
+    Args:
+        session (requests.Session): An instance :class:`requests.Session` used
+            to make HTTP requests. If not specified, a session will be created.
+
+    .. automethod:: __call__
+    """
+
+    def __init__(self, session=None):
+        if not session:
+            session = requests.Session()
+
+        self.session = session
+
+    def __del__(self):
+        try:
+            if hasattr(self, "session") and self.session is not None:
+                self.session.close()
+        except TypeError:
+            # NOTE: For certain Python binary built, the queue.Empty exception
+            # might not be considered a normal Python exception causing
+            # TypeError.
+            pass
+
+    def __call__(
+        self,
+        url,
+        method="GET",
+        body=None,
+        headers=None,
+        timeout=_DEFAULT_TIMEOUT,
+        **kwargs
+    ):
+        """Make an HTTP request using requests.
+
+        Args:
+            url (str): The URI to be requested.
+            method (str): The HTTP method to use for the request. Defaults
+                to 'GET'.
+            body (bytes): The payload or body in HTTP request.
+            headers (Mapping[str, str]): Request headers.
+            timeout (Optional[int]): The number of seconds to wait for a
+                response from the server. If not specified or if None, the
+                requests default timeout will be used.
+            kwargs: Additional arguments passed through to the underlying
+                requests :meth:`~requests.Session.request` method.
+
+        Returns:
+            google.auth.transport.Response: The HTTP response.
+
+        Raises:
+            google.auth.exceptions.TransportError: If any exception occurred.
+        """
+        try:
+            _LOGGER.debug("Making request: %s %s", method, url)
+            response = self.session.request(
+                method, url, data=body, headers=headers, timeout=timeout, **kwargs
+            )
+            return _Response(response)
+        except requests.exceptions.RequestException as caught_exc:
+            new_exc = exceptions.TransportError(caught_exc)
+            raise new_exc from caught_exc
+
+
+class _MutualTlsAdapter(requests.adapters.HTTPAdapter):
+    """
+    A TransportAdapter that enables mutual TLS.
+
+    Args:
+        cert (bytes): client certificate in PEM format
+        key (bytes): client private key in PEM format
+
+    Raises:
+        ImportError: if certifi or pyOpenSSL is not installed
+        OpenSSL.crypto.Error: if client cert or key is invalid
+    """
+
+    def __init__(self, cert, key):
+        import certifi
+        from OpenSSL import crypto
+        import urllib3.contrib.pyopenssl  # type: ignore
+
+        urllib3.contrib.pyopenssl.inject_into_urllib3()
+
+        pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, key)
+        x509 = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
+
+        ctx_poolmanager = create_urllib3_context()
+        ctx_poolmanager.load_verify_locations(cafile=certifi.where())
+        ctx_poolmanager._ctx.use_certificate(x509)
+        ctx_poolmanager._ctx.use_privatekey(pkey)
+        self._ctx_poolmanager = ctx_poolmanager
+
+        ctx_proxymanager = create_urllib3_context()
+        ctx_proxymanager.load_verify_locations(cafile=certifi.where())
+        ctx_proxymanager._ctx.use_certificate(x509)
+        ctx_proxymanager._ctx.use_privatekey(pkey)
+        self._ctx_proxymanager = ctx_proxymanager
+
+        super(_MutualTlsAdapter, self).__init__()
+
+    def init_poolmanager(self, *args, **kwargs):
+        kwargs["ssl_context"] = self._ctx_poolmanager
+        super(_MutualTlsAdapter, self).init_poolmanager(*args, **kwargs)
+
+    def proxy_manager_for(self, *args, **kwargs):
+        kwargs["ssl_context"] = self._ctx_proxymanager
+        return super(_MutualTlsAdapter, self).proxy_manager_for(*args, **kwargs)
+
+
+class _MutualTlsOffloadAdapter(requests.adapters.HTTPAdapter):
+    """
+    A TransportAdapter that enables mutual TLS and offloads the client side
+    signing operation to the signing library.
+
+    Args:
+        enterprise_cert_file_path (str): the path to a enterprise cert JSON
+            file. The file should contain the following field:
+
+                {
+                    "libs": {
+                        "signer_library": "...",
+                        "offload_library": "..."
+                    }
+                }
+
+    Raises:
+        ImportError: if certifi or pyOpenSSL is not installed
+        google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel
+            creation failed for any reason.
+    """
+
+    def __init__(self, enterprise_cert_file_path):
+        import certifi
+        from google.auth.transport import _custom_tls_signer
+
+        self.signer = _custom_tls_signer.CustomTlsSigner(enterprise_cert_file_path)
+        self.signer.load_libraries()
+
+        import urllib3.contrib.pyopenssl
+
+        urllib3.contrib.pyopenssl.inject_into_urllib3()
+
+        poolmanager = create_urllib3_context()
+        poolmanager.load_verify_locations(cafile=certifi.where())
+        self.signer.attach_to_ssl_context(poolmanager)
+        self._ctx_poolmanager = poolmanager
+
+        proxymanager = create_urllib3_context()
+        proxymanager.load_verify_locations(cafile=certifi.where())
+        self.signer.attach_to_ssl_context(proxymanager)
+        self._ctx_proxymanager = proxymanager
+
+        super(_MutualTlsOffloadAdapter, self).__init__()
+
+    def init_poolmanager(self, *args, **kwargs):
+        kwargs["ssl_context"] = self._ctx_poolmanager
+        super(_MutualTlsOffloadAdapter, self).init_poolmanager(*args, **kwargs)
+
+    def proxy_manager_for(self, *args, **kwargs):
+        kwargs["ssl_context"] = self._ctx_proxymanager
+        return super(_MutualTlsOffloadAdapter, self).proxy_manager_for(*args, **kwargs)
+
+
+class AuthorizedSession(requests.Session):
+    """A Requests Session class with credentials.
+
+    This class is used to perform requests to API endpoints that require
+    authorization::
+
+        from google.auth.transport.requests import AuthorizedSession
+
+        authed_session = AuthorizedSession(credentials)
+
+        response = authed_session.request(
+            'GET', 'https://www.googleapis.com/storage/v1/b')
+
+
+    The underlying :meth:`request` implementation handles adding the
+    credentials' headers to the request and refreshing credentials as needed.
+
+    This class also supports mutual TLS via :meth:`configure_mtls_channel`
+    method. In order to use this method, the `GOOGLE_API_USE_CLIENT_CERTIFICATE`
+    environment variable must be explicitly set to ``true``, otherwise it does
+    nothing. Assume the environment is set to ``true``, the method behaves in the
+    following manner:
+
+    If client_cert_callback is provided, client certificate and private
+    key are loaded using the callback; if client_cert_callback is None,
+    application default SSL credentials will be used. Exceptions are raised if
+    there are problems with the certificate, private key, or the loading process,
+    so it should be called within a try/except block.
+
+    First we set the environment variable to ``true``, then create an :class:`AuthorizedSession`
+    instance and specify the endpoints::
+
+        regular_endpoint = 'https://pubsub.googleapis.com/v1/projects/{my_project_id}/topics'
+        mtls_endpoint = 'https://pubsub.mtls.googleapis.com/v1/projects/{my_project_id}/topics'
+
+        authed_session = AuthorizedSession(credentials)
+
+    Now we can pass a callback to :meth:`configure_mtls_channel`::
+
+        def my_cert_callback():
+            # some code to load client cert bytes and private key bytes, both in
+            # PEM format.
+            some_code_to_load_client_cert_and_key()
+            if loaded:
+                return cert, key
+            raise MyClientCertFailureException()
+
+        # Always call configure_mtls_channel within a try/except block.
+        try:
+            authed_session.configure_mtls_channel(my_cert_callback)
+        except:
+            # handle exceptions.
+
+        if authed_session.is_mtls:
+            response = authed_session.request('GET', mtls_endpoint)
+        else:
+            response = authed_session.request('GET', regular_endpoint)
+
+
+    You can alternatively use application default SSL credentials like this::
+
+        try:
+            authed_session.configure_mtls_channel()
+        except:
+            # handle exceptions.
+
+    Args:
+        credentials (google.auth.credentials.Credentials): The credentials to
+            add to the request.
+        refresh_status_codes (Sequence[int]): Which HTTP status codes indicate
+            that credentials should be refreshed and the request should be
+            retried.
+        max_refresh_attempts (int): The maximum number of times to attempt to
+            refresh the credentials and retry the request.
+        refresh_timeout (Optional[int]): The timeout value in seconds for
+            credential refresh HTTP requests.
+        auth_request (google.auth.transport.requests.Request):
+            (Optional) An instance of
+            :class:`~google.auth.transport.requests.Request` used when
+            refreshing credentials. If not passed,
+            an instance of :class:`~google.auth.transport.requests.Request`
+            is created.
+        default_host (Optional[str]): A host like "pubsub.googleapis.com".
+            This is used when a self-signed JWT is created from service
+            account credentials.
+    """
+
+    def __init__(
+        self,
+        credentials,
+        refresh_status_codes=transport.DEFAULT_REFRESH_STATUS_CODES,
+        max_refresh_attempts=transport.DEFAULT_MAX_REFRESH_ATTEMPTS,
+        refresh_timeout=None,
+        auth_request=None,
+        default_host=None,
+    ):
+        super(AuthorizedSession, self).__init__()
+        self.credentials = credentials
+        self._refresh_status_codes = refresh_status_codes
+        self._max_refresh_attempts = max_refresh_attempts
+        self._refresh_timeout = refresh_timeout
+        self._is_mtls = False
+        self._default_host = default_host
+
+        if auth_request is None:
+            self._auth_request_session = requests.Session()
+
+            # Using an adapter to make HTTP requests robust to network errors.
+            # This adapter retrys HTTP requests when network errors occur
+            # and the requests seems safely retryable.
+            retry_adapter = requests.adapters.HTTPAdapter(max_retries=3)
+            self._auth_request_session.mount("https://", retry_adapter)
+
+            # Do not pass `self` as the session here, as it can lead to
+            # infinite recursion.
+            auth_request = Request(self._auth_request_session)
+        else:
+            self._auth_request_session = None
+
+        # Request instance used by internal methods (for example,
+        # credentials.refresh).
+        self._auth_request = auth_request
+
+        # https://google.aip.dev/auth/4111
+        # Attempt to use self-signed JWTs when a service account is used.
+        if isinstance(self.credentials, service_account.Credentials):
+            self.credentials._create_self_signed_jwt(
+                "https://{}/".format(self._default_host) if self._default_host else None
+            )
+
+    def configure_mtls_channel(self, client_cert_callback=None):
+        """Configure the client certificate and key for SSL connection.
+
+        The function does nothing unless `GOOGLE_API_USE_CLIENT_CERTIFICATE` is
+        explicitly set to `true`. In this case if client certificate and key are
+        successfully obtained (from the given client_cert_callback or from application
+        default SSL credentials), a :class:`_MutualTlsAdapter` instance will be mounted
+        to "https://" prefix.
+
+        Args:
+            client_cert_callback (Optional[Callable[[], (bytes, bytes)]]):
+                The optional callback returns the client certificate and private
+                key bytes both in PEM format.
+                If the callback is None, application default SSL credentials
+                will be used.
+
+        Raises:
+            google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel
+                creation failed for any reason.
+        """
+        use_client_cert = os.getenv(
+            environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE, "false"
+        )
+        if use_client_cert != "true":
+            self._is_mtls = False
+            return
+
+        try:
+            import OpenSSL
+        except ImportError as caught_exc:
+            new_exc = exceptions.MutualTLSChannelError(caught_exc)
+            raise new_exc from caught_exc
+
+        try:
+            (
+                self._is_mtls,
+                cert,
+                key,
+            ) = google.auth.transport._mtls_helper.get_client_cert_and_key(
+                client_cert_callback
+            )
+
+            if self._is_mtls:
+                mtls_adapter = _MutualTlsAdapter(cert, key)
+                self.mount("https://", mtls_adapter)
+        except (
+            exceptions.ClientCertError,
+            ImportError,
+            OpenSSL.crypto.Error,
+        ) as caught_exc:
+            new_exc = exceptions.MutualTLSChannelError(caught_exc)
+            raise new_exc from caught_exc
+
+    def request(
+        self,
+        method,
+        url,
+        data=None,
+        headers=None,
+        max_allowed_time=None,
+        timeout=_DEFAULT_TIMEOUT,
+        **kwargs
+    ):
+        """Implementation of Requests' request.
+
+        Args:
+            timeout (Optional[Union[float, Tuple[float, float]]]):
+                The amount of time in seconds to wait for the server response
+                with each individual request. Can also be passed as a tuple
+                ``(connect_timeout, read_timeout)``. See :meth:`requests.Session.request`
+                documentation for details.
+            max_allowed_time (Optional[float]):
+                If the method runs longer than this, a ``Timeout`` exception is
+                automatically raised. Unlike the ``timeout`` parameter, this
+                value applies to the total method execution time, even if
+                multiple requests are made under the hood.
+
+                Mind that it is not guaranteed that the timeout error is raised
+                at ``max_allowed_time``. It might take longer, for example, if
+                an underlying request takes a lot of time, but the request
+                itself does not timeout, e.g. if a large file is being
+                transmitted. The timout error will be raised after such
+                request completes.
+        """
+        # pylint: disable=arguments-differ
+        # Requests has a ton of arguments to request, but only two
+        # (method, url) are required. We pass through all of the other
+        # arguments to super, so no need to exhaustively list them here.
+
+        # Use a kwarg for this instead of an attribute to maintain
+        # thread-safety.
+        _credential_refresh_attempt = kwargs.pop("_credential_refresh_attempt", 0)
+
+        # Make a copy of the headers. They will be modified by the credentials
+        # and we want to pass the original headers if we recurse.
+        request_headers = headers.copy() if headers is not None else {}
+
+        # Do not apply the timeout unconditionally in order to not override the
+        # _auth_request's default timeout.
+        auth_request = (
+            self._auth_request
+            if timeout is None
+            else functools.partial(self._auth_request, timeout=timeout)
+        )
+
+        remaining_time = max_allowed_time
+
+        with TimeoutGuard(remaining_time) as guard:
+            self.credentials.before_request(auth_request, method, url, request_headers)
+        remaining_time = guard.remaining_timeout
+
+        with TimeoutGuard(remaining_time) as guard:
+            response = super(AuthorizedSession, self).request(
+                method,
+                url,
+                data=data,
+                headers=request_headers,
+                timeout=timeout,
+                **kwargs
+            )
+        remaining_time = guard.remaining_timeout
+
+        # If the response indicated that the credentials needed to be
+        # refreshed, then refresh the credentials and re-attempt the
+        # request.
+        # A stored token may expire between the time it is retrieved and
+        # the time the request is made, so we may need to try twice.
+        if (
+            response.status_code in self._refresh_status_codes
+            and _credential_refresh_attempt < self._max_refresh_attempts
+        ):
+
+            _LOGGER.info(
+                "Refreshing credentials due to a %s response. Attempt %s/%s.",
+                response.status_code,
+                _credential_refresh_attempt + 1,
+                self._max_refresh_attempts,
+            )
+
+            # Do not apply the timeout unconditionally in order to not override the
+            # _auth_request's default timeout.
+            auth_request = (
+                self._auth_request
+                if timeout is None
+                else functools.partial(self._auth_request, timeout=timeout)
+            )
+
+            with TimeoutGuard(remaining_time) as guard:
+                self.credentials.refresh(auth_request)
+            remaining_time = guard.remaining_timeout
+
+            # Recurse. Pass in the original headers, not our modified set, but
+            # do pass the adjusted max allowed time (i.e. the remaining total time).
+            return self.request(
+                method,
+                url,
+                data=data,
+                headers=headers,
+                max_allowed_time=remaining_time,
+                timeout=timeout,
+                _credential_refresh_attempt=_credential_refresh_attempt + 1,
+                **kwargs
+            )
+
+        return response
+
+    @property
+    def is_mtls(self):
+        """Indicates if the created SSL channel is mutual TLS."""
+        return self._is_mtls
+
+    def close(self):
+        if self._auth_request_session is not None:
+            self._auth_request_session.close()
+        super(AuthorizedSession, self).close()
diff --git a/.venv/lib/python3.12/site-packages/google/auth/transport/urllib3.py b/.venv/lib/python3.12/site-packages/google/auth/transport/urllib3.py
new file mode 100644
index 00000000..63144f5f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/transport/urllib3.py
@@ -0,0 +1,444 @@
+# Copyright 2016 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Transport adapter for urllib3."""
+
+from __future__ import absolute_import
+
+import logging
+import os
+import warnings
+
+# Certifi is Mozilla's certificate bundle. Urllib3 needs a certificate bundle
+# to verify HTTPS requests, and certifi is the recommended and most reliable
+# way to get a root certificate bundle. See
+# http://urllib3.readthedocs.io/en/latest/user-guide.html\
+#   #certificate-verification
+# For more details.
+try:
+    import certifi
+except ImportError:  # pragma: NO COVER
+    certifi = None  # type: ignore
+
+try:
+    import urllib3  # type: ignore
+    import urllib3.exceptions  # type: ignore
+except ImportError as caught_exc:  # pragma: NO COVER
+    raise ImportError(
+        "The urllib3 library is not installed from please install the "
+        "urllib3 package to use the urllib3 transport."
+    ) from caught_exc
+
+from packaging import version  # type: ignore
+
+from google.auth import environment_vars
+from google.auth import exceptions
+from google.auth import transport
+from google.oauth2 import service_account
+
+if version.parse(urllib3.__version__) >= version.parse("2.0.0"):  # pragma: NO COVER
+    RequestMethods = urllib3._request_methods.RequestMethods  # type: ignore
+else:  # pragma: NO COVER
+    RequestMethods = urllib3.request.RequestMethods  # type: ignore
+
+_LOGGER = logging.getLogger(__name__)
+
+
+class _Response(transport.Response):
+    """urllib3 transport response adapter.
+
+    Args:
+        response (urllib3.response.HTTPResponse): The raw urllib3 response.
+    """
+
+    def __init__(self, response):
+        self._response = response
+
+    @property
+    def status(self):
+        return self._response.status
+
+    @property
+    def headers(self):
+        return self._response.headers
+
+    @property
+    def data(self):
+        return self._response.data
+
+
+class Request(transport.Request):
+    """urllib3 request adapter.
+
+    This class is used internally for making requests using various transports
+    in a consistent way. If you use :class:`AuthorizedHttp` you do not need
+    to construct or use this class directly.
+
+    This class can be useful if you want to manually refresh a
+    :class:`~google.auth.credentials.Credentials` instance::
+
+        import google.auth.transport.urllib3
+        import urllib3
+
+        http = urllib3.PoolManager()
+        request = google.auth.transport.urllib3.Request(http)
+
+        credentials.refresh(request)
+
+    Args:
+        http (urllib3.request.RequestMethods): An instance of any urllib3
+            class that implements :class:`~urllib3.request.RequestMethods`,
+            usually :class:`urllib3.PoolManager`.
+
+    .. automethod:: __call__
+    """
+
+    def __init__(self, http):
+        self.http = http
+
+    def __call__(
+        self, url, method="GET", body=None, headers=None, timeout=None, **kwargs
+    ):
+        """Make an HTTP request using urllib3.
+
+        Args:
+            url (str): The URI to be requested.
+            method (str): The HTTP method to use for the request. Defaults
+                to 'GET'.
+            body (bytes): The payload / body in HTTP request.
+            headers (Mapping[str, str]): Request headers.
+            timeout (Optional[int]): The number of seconds to wait for a
+                response from the server. If not specified or if None, the
+                urllib3 default timeout will be used.
+            kwargs: Additional arguments passed throught to the underlying
+                urllib3 :meth:`urlopen` method.
+
+        Returns:
+            google.auth.transport.Response: The HTTP response.
+
+        Raises:
+            google.auth.exceptions.TransportError: If any exception occurred.
+        """
+        # urllib3 uses a sentinel default value for timeout, so only set it if
+        # specified.
+        if timeout is not None:
+            kwargs["timeout"] = timeout
+
+        try:
+            _LOGGER.debug("Making request: %s %s", method, url)
+            response = self.http.request(
+                method, url, body=body, headers=headers, **kwargs
+            )
+            return _Response(response)
+        except urllib3.exceptions.HTTPError as caught_exc:
+            new_exc = exceptions.TransportError(caught_exc)
+            raise new_exc from caught_exc
+
+
+def _make_default_http():
+    if certifi is not None:
+        return urllib3.PoolManager(cert_reqs="CERT_REQUIRED", ca_certs=certifi.where())
+    else:
+        return urllib3.PoolManager()
+
+
+def _make_mutual_tls_http(cert, key):
+    """Create a mutual TLS HTTP connection with the given client cert and key.
+    See https://github.com/urllib3/urllib3/issues/474#issuecomment-253168415
+
+    Args:
+        cert (bytes): client certificate in PEM format
+        key (bytes): client private key in PEM format
+
+    Returns:
+        urllib3.PoolManager: Mutual TLS HTTP connection.
+
+    Raises:
+        ImportError: If certifi or pyOpenSSL is not installed.
+        OpenSSL.crypto.Error: If the cert or key is invalid.
+    """
+    import certifi
+    from OpenSSL import crypto
+    import urllib3.contrib.pyopenssl  # type: ignore
+
+    urllib3.contrib.pyopenssl.inject_into_urllib3()
+    ctx = urllib3.util.ssl_.create_urllib3_context()
+    ctx.load_verify_locations(cafile=certifi.where())
+
+    pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, key)
+    x509 = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
+
+    ctx._ctx.use_certificate(x509)
+    ctx._ctx.use_privatekey(pkey)
+
+    http = urllib3.PoolManager(ssl_context=ctx)
+    return http
+
+
+class AuthorizedHttp(RequestMethods):  # type: ignore
+    """A urllib3 HTTP class with credentials.
+
+    This class is used to perform requests to API endpoints that require
+    authorization::
+
+        from google.auth.transport.urllib3 import AuthorizedHttp
+
+        authed_http = AuthorizedHttp(credentials)
+
+        response = authed_http.request(
+            'GET', 'https://www.googleapis.com/storage/v1/b')
+
+    This class implements :class:`urllib3.request.RequestMethods` and can be
+    used just like any other :class:`urllib3.PoolManager`.
+
+    The underlying :meth:`urlopen` implementation handles adding the
+    credentials' headers to the request and refreshing credentials as needed.
+
+    This class also supports mutual TLS via :meth:`configure_mtls_channel`
+    method. In order to use this method, the `GOOGLE_API_USE_CLIENT_CERTIFICATE`
+    environment variable must be explicitly set to `true`, otherwise it does
+    nothing. Assume the environment is set to `true`, the method behaves in the
+    following manner:
+    If client_cert_callback is provided, client certificate and private
+    key are loaded using the callback; if client_cert_callback is None,
+    application default SSL credentials will be used. Exceptions are raised if
+    there are problems with the certificate, private key, or the loading process,
+    so it should be called within a try/except block.
+
+    First we set the environment variable to `true`, then create an :class:`AuthorizedHttp`
+    instance and specify the endpoints::
+
+        regular_endpoint = 'https://pubsub.googleapis.com/v1/projects/{my_project_id}/topics'
+        mtls_endpoint = 'https://pubsub.mtls.googleapis.com/v1/projects/{my_project_id}/topics'
+
+        authed_http = AuthorizedHttp(credentials)
+
+    Now we can pass a callback to :meth:`configure_mtls_channel`::
+
+        def my_cert_callback():
+            # some code to load client cert bytes and private key bytes, both in
+            # PEM format.
+            some_code_to_load_client_cert_and_key()
+            if loaded:
+                return cert, key
+            raise MyClientCertFailureException()
+
+        # Always call configure_mtls_channel within a try/except block.
+        try:
+            is_mtls = authed_http.configure_mtls_channel(my_cert_callback)
+        except:
+            # handle exceptions.
+
+        if is_mtls:
+            response = authed_http.request('GET', mtls_endpoint)
+        else:
+            response = authed_http.request('GET', regular_endpoint)
+
+    You can alternatively use application default SSL credentials like this::
+
+        try:
+            is_mtls = authed_http.configure_mtls_channel()
+        except:
+            # handle exceptions.
+
+    Args:
+        credentials (google.auth.credentials.Credentials): The credentials to
+            add to the request.
+        http (urllib3.PoolManager): The underlying HTTP object to
+            use to make requests. If not specified, a
+            :class:`urllib3.PoolManager` instance will be constructed with
+            sane defaults.
+        refresh_status_codes (Sequence[int]): Which HTTP status codes indicate
+            that credentials should be refreshed and the request should be
+            retried.
+        max_refresh_attempts (int): The maximum number of times to attempt to
+            refresh the credentials and retry the request.
+        default_host (Optional[str]): A host like "pubsub.googleapis.com".
+            This is used when a self-signed JWT is created from service
+            account credentials.
+    """
+
+    def __init__(
+        self,
+        credentials,
+        http=None,
+        refresh_status_codes=transport.DEFAULT_REFRESH_STATUS_CODES,
+        max_refresh_attempts=transport.DEFAULT_MAX_REFRESH_ATTEMPTS,
+        default_host=None,
+    ):
+        if http is None:
+            self.http = _make_default_http()
+            self._has_user_provided_http = False
+        else:
+            self.http = http
+            self._has_user_provided_http = True
+
+        self.credentials = credentials
+        self._refresh_status_codes = refresh_status_codes
+        self._max_refresh_attempts = max_refresh_attempts
+        self._default_host = default_host
+        # Request instance used by internal methods (for example,
+        # credentials.refresh).
+        self._request = Request(self.http)
+
+        # https://google.aip.dev/auth/4111
+        # Attempt to use self-signed JWTs when a service account is used.
+        if isinstance(self.credentials, service_account.Credentials):
+            self.credentials._create_self_signed_jwt(
+                "https://{}/".format(self._default_host) if self._default_host else None
+            )
+
+        super(AuthorizedHttp, self).__init__()
+
+    def configure_mtls_channel(self, client_cert_callback=None):
+        """Configures mutual TLS channel using the given client_cert_callback or
+        application default SSL credentials. The behavior is controlled by
+        `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable.
+        (1) If the environment variable value is `true`, the function returns True
+        if the channel is mutual TLS and False otherwise. The `http` provided
+        in the constructor will be overwritten.
+        (2) If the environment variable is not set or `false`, the function does
+        nothing and it always return False.
+
+        Args:
+            client_cert_callback (Optional[Callable[[], (bytes, bytes)]]):
+                The optional callback returns the client certificate and private
+                key bytes both in PEM format.
+                If the callback is None, application default SSL credentials
+                will be used.
+
+        Returns:
+            True if the channel is mutual TLS and False otherwise.
+
+        Raises:
+            google.auth.exceptions.MutualTLSChannelError: If mutual TLS channel
+                creation failed for any reason.
+        """
+        use_client_cert = os.getenv(
+            environment_vars.GOOGLE_API_USE_CLIENT_CERTIFICATE, "false"
+        )
+        if use_client_cert != "true":
+            return False
+
+        try:
+            import OpenSSL
+        except ImportError as caught_exc:
+            new_exc = exceptions.MutualTLSChannelError(caught_exc)
+            raise new_exc from caught_exc
+
+        try:
+            found_cert_key, cert, key = transport._mtls_helper.get_client_cert_and_key(
+                client_cert_callback
+            )
+
+            if found_cert_key:
+                self.http = _make_mutual_tls_http(cert, key)
+            else:
+                self.http = _make_default_http()
+        except (
+            exceptions.ClientCertError,
+            ImportError,
+            OpenSSL.crypto.Error,
+        ) as caught_exc:
+            new_exc = exceptions.MutualTLSChannelError(caught_exc)
+            raise new_exc from caught_exc
+
+        if self._has_user_provided_http:
+            self._has_user_provided_http = False
+            warnings.warn(
+                "`http` provided in the constructor is overwritten", UserWarning
+            )
+
+        return found_cert_key
+
+    def urlopen(self, method, url, body=None, headers=None, **kwargs):
+        """Implementation of urllib3's urlopen."""
+        # pylint: disable=arguments-differ
+        # We use kwargs to collect additional args that we don't need to
+        # introspect here. However, we do explicitly collect the two
+        # positional arguments.
+
+        # Use a kwarg for this instead of an attribute to maintain
+        # thread-safety.
+        _credential_refresh_attempt = kwargs.pop("_credential_refresh_attempt", 0)
+
+        if headers is None:
+            headers = self.headers
+
+        # Make a copy of the headers. They will be modified by the credentials
+        # and we want to pass the original headers if we recurse.
+        request_headers = headers.copy()
+
+        self.credentials.before_request(self._request, method, url, request_headers)
+
+        response = self.http.urlopen(
+            method, url, body=body, headers=request_headers, **kwargs
+        )
+
+        # If the response indicated that the credentials needed to be
+        # refreshed, then refresh the credentials and re-attempt the
+        # request.
+        # A stored token may expire between the time it is retrieved and
+        # the time the request is made, so we may need to try twice.
+        # The reason urllib3's retries aren't used is because they
+        # don't allow you to modify the request headers. :/
+        if (
+            response.status in self._refresh_status_codes
+            and _credential_refresh_attempt < self._max_refresh_attempts
+        ):
+
+            _LOGGER.info(
+                "Refreshing credentials due to a %s response. Attempt %s/%s.",
+                response.status,
+                _credential_refresh_attempt + 1,
+                self._max_refresh_attempts,
+            )
+
+            self.credentials.refresh(self._request)
+
+            # Recurse. Pass in the original headers, not our modified set.
+            return self.urlopen(
+                method,
+                url,
+                body=body,
+                headers=headers,
+                _credential_refresh_attempt=_credential_refresh_attempt + 1,
+                **kwargs
+            )
+
+        return response
+
+    # Proxy methods for compliance with the urllib3.PoolManager interface
+
+    def __enter__(self):
+        """Proxy to ``self.http``."""
+        return self.http.__enter__()
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        """Proxy to ``self.http``."""
+        return self.http.__exit__(exc_type, exc_val, exc_tb)
+
+    def __del__(self):
+        if hasattr(self, "http") and self.http is not None:
+            self.http.clear()
+
+    @property
+    def headers(self):
+        """Proxy to ``self.http``."""
+        return self.http.headers
+
+    @headers.setter
+    def headers(self, value):
+        """Proxy to ``self.http``."""
+        self.http.headers = value
diff --git a/.venv/lib/python3.12/site-packages/google/auth/version.py b/.venv/lib/python3.12/site-packages/google/auth/version.py
new file mode 100644
index 00000000..41a80e6c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/google/auth/version.py
@@ -0,0 +1,15 @@
+# Copyright 2021 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+__version__ = "2.38.0"