Index: vendor/google/auth/src/Credentials/AppIdentityCredentials.php
===================================================================
--- vendor/google/auth/src/Credentials/AppIdentityCredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
+++ vendor/google/auth/src/Credentials/AppIdentityCredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
@@ -0,0 +1,238 @@
+<?php
+/*
+ * 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.
+ */
+
+namespace Google\Auth\Credentials;
+
+/*
+ * The AppIdentityService class is automatically defined on App Engine,
+ * so including this dependency is not necessary, and will result in a
+ * PHP fatal error in the App Engine environment.
+ */
+use google\appengine\api\app_identity\AppIdentityService;
+use Google\Auth\CredentialsLoader;
+use Google\Auth\ProjectIdProviderInterface;
+use Google\Auth\SignBlobInterface;
+
+/**
+ * @deprecated
+ *
+ * AppIdentityCredentials supports authorization on Google App Engine.
+ *
+ * It can be used to authorize requests using the AuthTokenMiddleware or
+ * AuthTokenSubscriber, but will only succeed if being run on App Engine:
+ *
+ * Example:
+ * ```
+ * use Google\Auth\Credentials\AppIdentityCredentials;
+ * use Google\Auth\Middleware\AuthTokenMiddleware;
+ * use GuzzleHttp\Client;
+ * use GuzzleHttp\HandlerStack;
+ *
+ * $gae = new AppIdentityCredentials('https://www.googleapis.com/auth/books');
+ * $middleware = new AuthTokenMiddleware($gae);
+ * $stack = HandlerStack::create();
+ * $stack->push($middleware);
+ *
+ * $client = new Client([
+ *     'handler' => $stack,
+ *     'base_uri' => 'https://www.googleapis.com/books/v1',
+ *     'auth' => 'google_auth'
+ * ]);
+ *
+ * $res = $client->get('volumes?q=Henry+David+Thoreau&country=US');
+ * ```
+ */
+class AppIdentityCredentials extends CredentialsLoader implements
+    SignBlobInterface,
+    ProjectIdProviderInterface
+{
+    /**
+     * Result of fetchAuthToken.
+     *
+     * @var array<mixed>
+     */
+    protected $lastReceivedToken;
+
+    /**
+     * Array of OAuth2 scopes to be requested.
+     *
+     * @var string[]
+     */
+    private $scope;
+
+    /**
+     * @var string
+     */
+    private $clientName;
+
+    /**
+     * @param string|string[] $scope One or more scopes.
+     */
+    public function __construct($scope = [])
+    {
+        $this->scope = is_array($scope) ? $scope : explode(' ', (string) $scope);
+    }
+
+    /**
+     * Determines if this an App Engine instance, by accessing the
+     * SERVER_SOFTWARE environment variable (prod) or the APPENGINE_RUNTIME
+     * environment variable (dev).
+     *
+     * @return bool true if this an App Engine Instance, false otherwise
+     */
+    public static function onAppEngine()
+    {
+        $appEngineProduction = isset($_SERVER['SERVER_SOFTWARE']) &&
+            0 === strpos($_SERVER['SERVER_SOFTWARE'], 'Google App Engine');
+        if ($appEngineProduction) {
+            return true;
+        }
+        $appEngineDevAppServer = isset($_SERVER['APPENGINE_RUNTIME']) &&
+            $_SERVER['APPENGINE_RUNTIME'] == 'php';
+        if ($appEngineDevAppServer) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Implements FetchAuthTokenInterface#fetchAuthToken.
+     *
+     * Fetches the auth tokens using the AppIdentityService if available.
+     * As the AppIdentityService uses protobufs to fetch the access token,
+     * the GuzzleHttp\ClientInterface instance passed in will not be used.
+     *
+     * @param callable|null $httpHandler callback which delivers psr7 request
+     * @return array<mixed> {
+     *     A set of auth related metadata, containing the following
+     *
+     *     @type string $access_token
+     *     @type string $expiration_time
+     * }
+     */
+    public function fetchAuthToken(?callable $httpHandler = null)
+    {
+        try {
+            $this->checkAppEngineContext();
+        } catch (\Exception $e) {
+            return [];
+        }
+
+        /** @phpstan-ignore-next-line */
+        $token = AppIdentityService::getAccessToken($this->scope);
+        $this->lastReceivedToken = $token;
+
+        return $token;
+    }
+
+    /**
+     * Sign a string using AppIdentityService.
+     *
+     * @param string $stringToSign The string to sign.
+     * @param bool $forceOpenSsl [optional] Does not apply to this credentials
+     *        type.
+     * @return string The signature, base64-encoded.
+     * @throws \Exception If AppEngine SDK or mock is not available.
+     */
+    public function signBlob($stringToSign, $forceOpenSsl = false)
+    {
+        $this->checkAppEngineContext();
+
+        /** @phpstan-ignore-next-line */
+        return base64_encode(AppIdentityService::signForApp($stringToSign)['signature']);
+    }
+
+    /**
+     * Get the project ID from AppIdentityService.
+     *
+     * Returns null if AppIdentityService is unavailable.
+     *
+     * @param callable|null $httpHandler Not used by this type.
+     * @return string|null
+     */
+    public function getProjectId(?callable $httpHandler = null)
+    {
+        try {
+            $this->checkAppEngineContext();
+        } catch (\Exception $e) {
+            return null;
+        }
+
+        /** @phpstan-ignore-next-line */
+        return AppIdentityService::getApplicationId();
+    }
+
+    /**
+     * Get the client name from AppIdentityService.
+     *
+     * Subsequent calls to this method will return a cached value.
+     *
+     * @param callable|null $httpHandler Not used in this implementation.
+     * @return string
+     * @throws \Exception If AppEngine SDK or mock is not available.
+     */
+    public function getClientName(?callable $httpHandler = null)
+    {
+        $this->checkAppEngineContext();
+
+        if (!$this->clientName) {
+            /** @phpstan-ignore-next-line */
+            $this->clientName = AppIdentityService::getServiceAccountName();
+        }
+
+        return $this->clientName;
+    }
+
+    /**
+     * @return array{access_token:string,expires_at:int}|null
+     */
+    public function getLastReceivedToken()
+    {
+        if ($this->lastReceivedToken) {
+            return [
+                'access_token' => $this->lastReceivedToken['access_token'],
+                'expires_at' => $this->lastReceivedToken['expiration_time'],
+            ];
+        }
+
+        return null;
+    }
+
+    /**
+     * Caching is handled by the underlying AppIdentityService, return empty string
+     * to prevent caching.
+     *
+     * @return string
+     */
+    public function getCacheKey()
+    {
+        return '';
+    }
+
+    /**
+     * @return void
+     */
+    private function checkAppEngineContext()
+    {
+        if (!self::onAppEngine() || !class_exists('google\appengine\api\app_identity\AppIdentityService')) {
+            throw new \Exception(
+                'This class must be run in App Engine, or you must include the AppIdentityService '
+                . 'mock class defined in tests/mocks/AppIdentityService.php'
+            );
+        }
+    }
+}
Index: vendor/google/auth/src/Credentials/ExternalAccountCredentials.php
===================================================================
--- vendor/google/auth/src/Credentials/ExternalAccountCredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
+++ vendor/google/auth/src/Credentials/ExternalAccountCredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
@@ -0,0 +1,378 @@
+<?php
+/*
+ * Copyright 2023 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.
+ */
+
+namespace Google\Auth\Credentials;
+
+use Google\Auth\CredentialSource\AwsNativeSource;
+use Google\Auth\CredentialSource\ExecutableSource;
+use Google\Auth\CredentialSource\FileSource;
+use Google\Auth\CredentialSource\UrlSource;
+use Google\Auth\ExecutableHandler\ExecutableHandler;
+use Google\Auth\ExternalAccountCredentialSourceInterface;
+use Google\Auth\FetchAuthTokenInterface;
+use Google\Auth\GetQuotaProjectInterface;
+use Google\Auth\GetUniverseDomainInterface;
+use Google\Auth\HttpHandler\HttpClientCache;
+use Google\Auth\HttpHandler\HttpHandlerFactory;
+use Google\Auth\OAuth2;
+use Google\Auth\ProjectIdProviderInterface;
+use Google\Auth\UpdateMetadataInterface;
+use Google\Auth\UpdateMetadataTrait;
+use GuzzleHttp\Psr7\Request;
+use InvalidArgumentException;
+
+class ExternalAccountCredentials implements
+    FetchAuthTokenInterface,
+    UpdateMetadataInterface,
+    GetQuotaProjectInterface,
+    GetUniverseDomainInterface,
+    ProjectIdProviderInterface
+{
+    use UpdateMetadataTrait;
+
+    private const EXTERNAL_ACCOUNT_TYPE = 'external_account';
+    private const CLOUD_RESOURCE_MANAGER_URL = 'https://cloudresourcemanager.UNIVERSE_DOMAIN/v1/projects/%s';
+
+    private OAuth2 $auth;
+    private ?string $quotaProject;
+    private ?string $serviceAccountImpersonationUrl;
+    private ?string $workforcePoolUserProject;
+    private ?string $projectId;
+    private string $universeDomain;
+
+    /**
+     * @param string|string[] $scope   The scope of the access request, expressed either as an array
+     *                                 or as a space-delimited string.
+     * @param array<mixed>    $jsonKey JSON credentials as an associative array.
+     */
+    public function __construct(
+        $scope,
+        array $jsonKey
+    ) {
+        if (!array_key_exists('type', $jsonKey)) {
+            throw new InvalidArgumentException('json key is missing the type field');
+        }
+        if ($jsonKey['type'] !== self::EXTERNAL_ACCOUNT_TYPE) {
+            throw new InvalidArgumentException(sprintf(
+                'expected "%s" type but received "%s"',
+                self::EXTERNAL_ACCOUNT_TYPE,
+                $jsonKey['type']
+            ));
+        }
+
+        if (!array_key_exists('token_url', $jsonKey)) {
+            throw new InvalidArgumentException(
+                'json key is missing the token_url field'
+            );
+        }
+
+        if (!array_key_exists('audience', $jsonKey)) {
+            throw new InvalidArgumentException(
+                'json key is missing the audience field'
+            );
+        }
+
+        if (!array_key_exists('subject_token_type', $jsonKey)) {
+            throw new InvalidArgumentException(
+                'json key is missing the subject_token_type field'
+            );
+        }
+
+        if (!array_key_exists('credential_source', $jsonKey)) {
+            throw new InvalidArgumentException(
+                'json key is missing the credential_source field'
+            );
+        }
+
+        $this->serviceAccountImpersonationUrl = $jsonKey['service_account_impersonation_url'] ?? null;
+
+        $this->quotaProject = $jsonKey['quota_project_id'] ?? null;
+        $this->workforcePoolUserProject = $jsonKey['workforce_pool_user_project'] ?? null;
+        $this->universeDomain = $jsonKey['universe_domain'] ?? GetUniverseDomainInterface::DEFAULT_UNIVERSE_DOMAIN;
+
+        $this->auth = new OAuth2([
+            'tokenCredentialUri' => $jsonKey['token_url'],
+            'audience' => $jsonKey['audience'],
+            'scope' => $scope,
+            'subjectTokenType' => $jsonKey['subject_token_type'],
+            'subjectTokenFetcher' => self::buildCredentialSource($jsonKey),
+            'additionalOptions' => $this->workforcePoolUserProject
+                ? ['userProject' => $this->workforcePoolUserProject]
+                : [],
+        ]);
+
+        if (!$this->isWorkforcePool() && $this->workforcePoolUserProject) {
+            throw new InvalidArgumentException(
+                'workforce_pool_user_project should not be set for non-workforce pool credentials.'
+            );
+        }
+    }
+
+    /**
+     * @param array<mixed> $jsonKey
+     */
+    private static function buildCredentialSource(array $jsonKey): ExternalAccountCredentialSourceInterface
+    {
+        $credentialSource = $jsonKey['credential_source'];
+        if (isset($credentialSource['file'])) {
+            return new FileSource(
+                $credentialSource['file'],
+                $credentialSource['format']['type'] ?? null,
+                $credentialSource['format']['subject_token_field_name'] ?? null
+            );
+        }
+
+        if (
+            isset($credentialSource['environment_id'])
+            && 1 === preg_match('/^aws(\d+)$/', $credentialSource['environment_id'], $matches)
+        ) {
+            if ($matches[1] !== '1') {
+                throw new InvalidArgumentException(
+                    "aws version \"$matches[1]\" is not supported in the current build."
+                );
+            }
+            if (!array_key_exists('regional_cred_verification_url', $credentialSource)) {
+                throw new InvalidArgumentException(
+                    'The regional_cred_verification_url field is required for aws1 credential source.'
+                );
+            }
+
+            return new AwsNativeSource(
+                $jsonKey['audience'],
+                $credentialSource['regional_cred_verification_url'],   // $regionalCredVerificationUrl
+                $credentialSource['region_url'] ?? null,               // $regionUrl
+                $credentialSource['url'] ?? null,                      // $securityCredentialsUrl
+                $credentialSource['imdsv2_session_token_url'] ?? null, // $imdsV2TokenUrl
+            );
+        }
+
+        if (isset($credentialSource['url'])) {
+            return new UrlSource(
+                $credentialSource['url'],
+                $credentialSource['format']['type'] ?? null,
+                $credentialSource['format']['subject_token_field_name'] ?? null,
+                $credentialSource['headers'] ?? null,
+            );
+        }
+
+        if (isset($credentialSource['executable'])) {
+            if (!array_key_exists('command', $credentialSource['executable'])) {
+                throw new InvalidArgumentException(
+                    'executable source requires a command to be set in the JSON file.'
+                );
+            }
+
+            // Build command environment variables
+            $env = [
+                'GOOGLE_EXTERNAL_ACCOUNT_AUDIENCE' => $jsonKey['audience'],
+                'GOOGLE_EXTERNAL_ACCOUNT_TOKEN_TYPE' => $jsonKey['subject_token_type'],
+                // Always set to 0 because interactive mode is not supported.
+                'GOOGLE_EXTERNAL_ACCOUNT_INTERACTIVE' => '0',
+            ];
+
+            if ($outputFile = $credentialSource['executable']['output_file'] ?? null) {
+                $env['GOOGLE_EXTERNAL_ACCOUNT_OUTPUT_FILE'] = $outputFile;
+            }
+
+            if ($serviceAccountImpersonationUrl = $jsonKey['service_account_impersonation_url'] ?? null) {
+                // Parse email from URL. The formal looks as follows:
+                // https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/name@project-id.iam.gserviceaccount.com:generateAccessToken
+                $regex = '/serviceAccounts\/(?<email>[^:]+):generateAccessToken$/';
+                if (preg_match($regex, $serviceAccountImpersonationUrl, $matches)) {
+                    $env['GOOGLE_EXTERNAL_ACCOUNT_IMPERSONATED_EMAIL'] = $matches['email'];
+                }
+            }
+
+            $timeoutMs = $credentialSource['executable']['timeout_millis'] ?? null;
+
+            return new ExecutableSource(
+                $credentialSource['executable']['command'],
+                $outputFile,
+                $timeoutMs ? new ExecutableHandler($env, $timeoutMs) : new ExecutableHandler($env)
+            );
+        }
+
+        throw new InvalidArgumentException('Unable to determine credential source from json key.');
+    }
+
+    /**
+     * @param string $stsToken
+     * @param callable|null $httpHandler
+     *
+     * @return array<mixed> {
+     *     A set of auth related metadata, containing the following
+     *
+     *     @type string $access_token
+     *     @type int $expires_at
+     * }
+     */
+    private function getImpersonatedAccessToken(string $stsToken, ?callable $httpHandler = null): array
+    {
+        if (!isset($this->serviceAccountImpersonationUrl)) {
+            throw new InvalidArgumentException(
+                'service_account_impersonation_url must be set in JSON credentials.'
+            );
+        }
+        $request = new Request(
+            'POST',
+            $this->serviceAccountImpersonationUrl,
+            [
+                'Content-Type' => 'application/json',
+                'Authorization' => 'Bearer ' . $stsToken,
+            ],
+            (string) json_encode([
+                'lifetime' => sprintf('%ss', OAuth2::DEFAULT_EXPIRY_SECONDS),
+                'scope' => explode(' ', $this->auth->getScope()),
+            ]),
+        );
+        if (is_null($httpHandler)) {
+            $httpHandler = HttpHandlerFactory::build(HttpClientCache::getHttpClient());
+        }
+        $response = $httpHandler($request);
+        $body = json_decode((string) $response->getBody(), true);
+        return [
+            'access_token' => $body['accessToken'],
+            'expires_at' => strtotime($body['expireTime']),
+        ];
+    }
+
+    /**
+     * @param callable|null $httpHandler
+     *
+     * @return array<mixed> {
+     *     A set of auth related metadata, containing the following
+     *
+     *     @type string $access_token
+     *     @type int $expires_at (impersonated service accounts only)
+     *     @type int $expires_in (identity pool only)
+     *     @type string $issued_token_type (identity pool only)
+     *     @type string $token_type (identity pool only)
+     * }
+     */
+    public function fetchAuthToken(?callable $httpHandler = null)
+    {
+        $stsToken = $this->auth->fetchAuthToken($httpHandler);
+
+        if (isset($this->serviceAccountImpersonationUrl)) {
+            return $this->getImpersonatedAccessToken($stsToken['access_token'], $httpHandler);
+        }
+
+        return $stsToken;
+    }
+
+    /**
+     * Get the cache token key for the credentials.
+     * The cache token key format depends on the type of source
+     * The format for the cache key one of the following:
+     * FetcherCacheKey.Scope.[ServiceAccount].[TokenType].[WorkforcePoolUserProject]
+     * FetcherCacheKey.Audience.[ServiceAccount].[TokenType].[WorkforcePoolUserProject]
+     *
+     * @return ?string;
+     */
+    public function getCacheKey(): ?string
+    {
+        $scopeOrAudience = $this->auth->getAudience();
+        if (!$scopeOrAudience) {
+            $scopeOrAudience = $this->auth->getScope();
+        }
+
+        return $this->auth->getSubjectTokenFetcher()->getCacheKey() .
+            '.' . $scopeOrAudience .
+            '.' . ($this->serviceAccountImpersonationUrl ?? '') .
+            '.' . ($this->auth->getSubjectTokenType() ?? '') .
+            '.' . ($this->workforcePoolUserProject ?? '');
+    }
+
+    public function getLastReceivedToken()
+    {
+        return $this->auth->getLastReceivedToken();
+    }
+
+    /**
+     * Get the quota project used for this API request
+     *
+     * @return string|null
+     */
+    public function getQuotaProject()
+    {
+        return $this->quotaProject;
+    }
+
+    /**
+     * Get the universe domain used for this API request
+     *
+     * @return string
+     */
+    public function getUniverseDomain(): string
+    {
+        return $this->universeDomain;
+    }
+
+    /**
+     * Get the project ID.
+     *
+     * @param callable|null $httpHandler Callback which delivers psr7 request
+     * @param string|null $accessToken The access token to use to sign the blob. If
+     *        provided, saves a call to the metadata server for a new access
+     *        token. **Defaults to** `null`.
+     * @return string|null
+     */
+    public function getProjectId(?callable $httpHandler = null, ?string $accessToken = null)
+    {
+        if (isset($this->projectId)) {
+            return $this->projectId;
+        }
+
+        $projectNumber = $this->getProjectNumber() ?: $this->workforcePoolUserProject;
+        if (!$projectNumber) {
+            return null;
+        }
+
+        if (is_null($httpHandler)) {
+            $httpHandler = HttpHandlerFactory::build(HttpClientCache::getHttpClient());
+        }
+
+        $url = str_replace(
+            'UNIVERSE_DOMAIN',
+            $this->getUniverseDomain(),
+            sprintf(self::CLOUD_RESOURCE_MANAGER_URL, $projectNumber)
+        );
+
+        if (is_null($accessToken)) {
+            $accessToken = $this->fetchAuthToken($httpHandler)['access_token'];
+        }
+
+        $request = new Request('GET', $url, ['authorization' => 'Bearer ' . $accessToken]);
+        $response = $httpHandler($request);
+
+        $body = json_decode((string) $response->getBody(), true);
+        return $this->projectId = $body['projectId'];
+    }
+
+    private function getProjectNumber(): ?string
+    {
+        $parts = explode('/', $this->auth->getAudience());
+        $i = array_search('projects', $parts);
+        return $parts[$i + 1] ?? null;
+    }
+
+    private function isWorkforcePool(): bool
+    {
+        $regex = '#//iam\.googleapis\.com/locations/[^/]+/workforcePools/#';
+        return preg_match($regex, $this->auth->getAudience()) === 1;
+    }
+}
Index: vendor/google/auth/src/Credentials/GCECredentials.php
===================================================================
--- vendor/google/auth/src/Credentials/GCECredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
+++ vendor/google/auth/src/Credentials/GCECredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
@@ -0,0 +1,683 @@
+<?php
+/*
+ * 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.
+ */
+
+namespace Google\Auth\Credentials;
+
+use COM;
+use com_exception;
+use Google\Auth\CredentialsLoader;
+use Google\Auth\GetQuotaProjectInterface;
+use Google\Auth\HttpHandler\HttpClientCache;
+use Google\Auth\HttpHandler\HttpHandlerFactory;
+use Google\Auth\Iam;
+use Google\Auth\IamSignerTrait;
+use Google\Auth\ProjectIdProviderInterface;
+use Google\Auth\SignBlobInterface;
+use GuzzleHttp\Exception\ClientException;
+use GuzzleHttp\Exception\ConnectException;
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Exception\ServerException;
+use GuzzleHttp\Psr7\Request;
+use InvalidArgumentException;
+
+/**
+ * GCECredentials supports authorization on Google Compute Engine.
+ *
+ * It can be used to authorize requests using the AuthTokenMiddleware, but will
+ * only succeed if being run on GCE:
+ *
+ *   use Google\Auth\Credentials\GCECredentials;
+ *   use Google\Auth\Middleware\AuthTokenMiddleware;
+ *   use GuzzleHttp\Client;
+ *   use GuzzleHttp\HandlerStack;
+ *
+ *   $gce = new GCECredentials();
+ *   $middleware = new AuthTokenMiddleware($gce);
+ *   $stack = HandlerStack::create();
+ *   $stack->push($middleware);
+ *
+ *   $client = new Client([
+ *      'handler' => $stack,
+ *      'base_uri' => 'https://www.googleapis.com/taskqueue/v1beta2/projects/',
+ *      'auth' => 'google_auth'
+ *   ]);
+ *
+ *   $res = $client->get('myproject/taskqueues/myqueue');
+ */
+class GCECredentials extends CredentialsLoader implements
+    SignBlobInterface,
+    ProjectIdProviderInterface,
+    GetQuotaProjectInterface
+{
+    use IamSignerTrait;
+
+    // phpcs:disable
+    const cacheKey = 'GOOGLE_AUTH_PHP_GCE';
+    // phpcs:enable
+
+    /**
+     * The metadata IP address on appengine instances.
+     *
+     * The IP is used instead of the domain 'metadata' to avoid slow responses
+     * when not on Compute Engine.
+     */
+    const METADATA_IP = '169.254.169.254';
+
+    /**
+     * The metadata path of the default token.
+     */
+    const TOKEN_URI_PATH = 'v1/instance/service-accounts/default/token';
+
+    /**
+     * The metadata path of the default id token.
+     */
+    const ID_TOKEN_URI_PATH = 'v1/instance/service-accounts/default/identity';
+
+    /**
+     * The metadata path of the client ID.
+     */
+    const CLIENT_ID_URI_PATH = 'v1/instance/service-accounts/default/email';
+
+    /**
+     * The metadata path of the project ID.
+     */
+    const PROJECT_ID_URI_PATH = 'v1/project/project-id';
+
+    /**
+     * The metadata path of the project ID.
+     */
+    const UNIVERSE_DOMAIN_URI_PATH = 'v1/universe/universe-domain';
+
+    /**
+     * The header whose presence indicates GCE presence.
+     */
+    const FLAVOR_HEADER = 'Metadata-Flavor';
+
+    /**
+     * The Linux file which contains the product name.
+     */
+    private const GKE_PRODUCT_NAME_FILE = '/sys/class/dmi/id/product_name';
+
+    /**
+     * The Windows Registry key path to the product name
+     */
+    private const WINDOWS_REGISTRY_KEY_PATH = 'HKEY_LOCAL_MACHINE\\SYSTEM\\HardwareConfig\\Current\\';
+
+    /**
+     * The Windows registry key name for the product name
+     */
+    private const WINDOWS_REGISTRY_KEY_NAME = 'SystemProductName';
+
+    /**
+     * The Name of the product expected from the windows registry
+     */
+    private const PRODUCT_NAME = 'Google';
+
+    private const CRED_TYPE = 'mds';
+
+    /**
+     * Note: the explicit `timeout` and `tries` below 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" since the expected 4-nines time is about 0.5 seconds.
+     * This allows us to limit the total ping maximum timeout to 1.5 seconds
+     * for developer desktop scenarios.
+     */
+    const MAX_COMPUTE_PING_TRIES = 3;
+    const COMPUTE_PING_CONNECTION_TIMEOUT_S = 0.5;
+
+    /**
+     * Flag used to ensure that the onGCE test is only done once;.
+     *
+     * @var bool
+     */
+    private $hasCheckedOnGce = false;
+
+    /**
+     * Flag that stores the value of the onGCE check.
+     *
+     * @var bool
+     */
+    private $isOnGce = false;
+
+    /**
+     * Result of fetchAuthToken.
+     *
+     * @var array<mixed>
+     */
+    protected $lastReceivedToken;
+
+    /**
+     * @var string|null
+     */
+    private $clientName;
+
+    /**
+     * @var string|null
+     */
+    private $projectId;
+
+    /**
+     * @var string
+     */
+    private $tokenUri;
+
+    /**
+     * @var string
+     */
+    private $targetAudience;
+
+    /**
+     * @var string|null
+     */
+    private $quotaProject;
+
+    /**
+     * @var string|null
+     */
+    private $serviceAccountIdentity;
+
+    /**
+     * @var string
+     */
+    private ?string $universeDomain;
+
+    /**
+     * @param Iam|null $iam [optional] An IAM instance.
+     * @param string|string[] $scope [optional] the scope of the access request,
+     *        expressed either as an array or as a space-delimited string.
+     * @param string $targetAudience [optional] The audience for the ID token.
+     * @param string $quotaProject [optional] Specifies a project to bill for access
+     *   charges associated with the request.
+     * @param string $serviceAccountIdentity [optional] Specify a service
+     *   account identity name to use instead of "default".
+     * @param string|null $universeDomain [optional] Specify a universe domain to use
+     *   instead of fetching one from the metadata server.
+     */
+    public function __construct(
+        ?Iam $iam = null,
+        $scope = null,
+        $targetAudience = null,
+        $quotaProject = null,
+        $serviceAccountIdentity = null,
+        ?string $universeDomain = null
+    ) {
+        $this->iam = $iam;
+
+        if ($scope && $targetAudience) {
+            throw new InvalidArgumentException(
+                'Scope and targetAudience cannot both be supplied'
+            );
+        }
+
+        $tokenUri = self::getTokenUri($serviceAccountIdentity);
+        if ($scope) {
+            if (is_string($scope)) {
+                $scope = explode(' ', $scope);
+            }
+
+            $scope = implode(',', $scope);
+
+            $tokenUri = $tokenUri . '?scopes=' . $scope;
+        } elseif ($targetAudience) {
+            $tokenUri = self::getIdTokenUri($serviceAccountIdentity);
+            $tokenUri = $tokenUri . '?audience=' . $targetAudience;
+            $this->targetAudience = $targetAudience;
+        }
+
+        $this->tokenUri = $tokenUri;
+        $this->quotaProject = $quotaProject;
+        $this->serviceAccountIdentity = $serviceAccountIdentity;
+        $this->universeDomain = $universeDomain;
+    }
+
+    /**
+     * The full uri for accessing the default token.
+     *
+     * @param string $serviceAccountIdentity [optional] Specify a service
+     *   account identity name to use instead of "default".
+     * @return string
+     */
+    public static function getTokenUri($serviceAccountIdentity = null)
+    {
+        $base = 'http://' . self::METADATA_IP . '/computeMetadata/';
+        $base .= self::TOKEN_URI_PATH;
+
+        if ($serviceAccountIdentity) {
+            return str_replace(
+                '/default/',
+                '/' . $serviceAccountIdentity . '/',
+                $base
+            );
+        }
+        return $base;
+    }
+
+    /**
+     * The full uri for accessing the default service account.
+     *
+     * @param string $serviceAccountIdentity [optional] Specify a service
+     *   account identity name to use instead of "default".
+     * @return string
+     */
+    public static function getClientNameUri($serviceAccountIdentity = null)
+    {
+        $base = 'http://' . self::METADATA_IP . '/computeMetadata/';
+        $base .= self::CLIENT_ID_URI_PATH;
+
+        if ($serviceAccountIdentity) {
+            return str_replace(
+                '/default/',
+                '/' . $serviceAccountIdentity . '/',
+                $base
+            );
+        }
+
+        return $base;
+    }
+
+    /**
+     * The full uri for accesesing the default identity token.
+     *
+     * @param string $serviceAccountIdentity [optional] Specify a service
+     *   account identity name to use instead of "default".
+     * @return string
+     */
+    private static function getIdTokenUri($serviceAccountIdentity = null)
+    {
+        $base = 'http://' . self::METADATA_IP . '/computeMetadata/';
+        $base .= self::ID_TOKEN_URI_PATH;
+
+        if ($serviceAccountIdentity) {
+            return str_replace(
+                '/default/',
+                '/' . $serviceAccountIdentity . '/',
+                $base
+            );
+        }
+
+        return $base;
+    }
+
+    /**
+     * The full uri for accessing the default project ID.
+     *
+     * @return string
+     */
+    private static function getProjectIdUri()
+    {
+        $base = 'http://' . self::METADATA_IP . '/computeMetadata/';
+
+        return $base . self::PROJECT_ID_URI_PATH;
+    }
+
+    /**
+     * The full uri for accessing the default universe domain.
+     *
+     * @return string
+     */
+    private static function getUniverseDomainUri()
+    {
+        $base = 'http://' . self::METADATA_IP . '/computeMetadata/';
+
+        return $base . self::UNIVERSE_DOMAIN_URI_PATH;
+    }
+
+    /**
+     * Determines if this an App Engine Flexible instance, by accessing the
+     * GAE_INSTANCE environment variable.
+     *
+     * @return bool true if this an App Engine Flexible Instance, false otherwise
+     */
+    public static function onAppEngineFlexible()
+    {
+        return substr((string) getenv('GAE_INSTANCE'), 0, 4) === 'aef-';
+    }
+
+    /**
+     * Determines if this a GCE instance, by accessing the expected metadata
+     * host.
+     * If $httpHandler is not specified a the default HttpHandler is used.
+     *
+     * @param callable|null $httpHandler callback which delivers psr7 request
+     * @return bool True if this a GCEInstance, false otherwise
+     */
+    public static function onGce(?callable $httpHandler = null)
+    {
+        $httpHandler = $httpHandler
+            ?: HttpHandlerFactory::build(HttpClientCache::getHttpClient());
+
+        $checkUri = 'http://' . self::METADATA_IP;
+        for ($i = 1; $i <= self::MAX_COMPUTE_PING_TRIES; $i++) {
+            try {
+                // Comment from: oauth2client/client.py
+                //
+                // Note: the explicit `timeout` below 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".
+                $resp = $httpHandler(
+                    new Request(
+                        'GET',
+                        $checkUri,
+                        [
+                            self::FLAVOR_HEADER => 'Google',
+                            self::$metricMetadataKey => self::getMetricsHeader('', 'mds')
+                        ]
+                    ),
+                    ['timeout' => self::COMPUTE_PING_CONNECTION_TIMEOUT_S]
+                );
+
+                return $resp->getHeaderLine(self::FLAVOR_HEADER) == 'Google';
+            } catch (ClientException $e) {
+            } catch (ServerException $e) {
+            } catch (RequestException $e) {
+            } catch (ConnectException $e) {
+            }
+        }
+
+        if (PHP_OS === 'Windows' || PHP_OS === 'WINNT') {
+            return self::detectResidencyWindows(
+                self::WINDOWS_REGISTRY_KEY_PATH . self::WINDOWS_REGISTRY_KEY_NAME
+            );
+        }
+
+        // Detect GCE residency on Linux
+        return self::detectResidencyLinux(self::GKE_PRODUCT_NAME_FILE);
+    }
+
+    private static function detectResidencyLinux(string $productNameFile): bool
+    {
+        if (file_exists($productNameFile)) {
+            $productName = trim((string) file_get_contents($productNameFile));
+            return 0 === strpos($productName, self::PRODUCT_NAME);
+        }
+        return false;
+    }
+
+    private static function detectResidencyWindows(string $registryProductKey): bool
+    {
+        if (!class_exists(COM::class)) {
+            // the COM extension must be installed and enabled to detect Windows residency
+            // see https://www.php.net/manual/en/book.com.php
+            return false;
+        }
+
+        $shell = new COM('WScript.Shell');
+        $productName = null;
+
+        try {
+            $productName = $shell->regRead($registryProductKey);
+        } catch (com_exception) {
+            // This means that we tried to read a key that doesn't exist on the registry
+            // which might mean that it is a windows instance that is not on GCE
+            return false;
+        }
+
+        return 0 === strpos($productName, self::PRODUCT_NAME);
+    }
+
+    /**
+     * Implements FetchAuthTokenInterface#fetchAuthToken.
+     *
+     * Fetches the auth tokens from the GCE metadata host if it is available.
+     * If $httpHandler is not specified a the default HttpHandler is used.
+     *
+     * @param callable|null $httpHandler callback which delivers psr7 request
+     *
+     * @return array<mixed> {
+     *     A set of auth related metadata, based on the token type.
+     *
+     *     @type string $access_token for access tokens
+     *     @type int    $expires_in   for access tokens
+     *     @type string $token_type   for access tokens
+     *     @type string $id_token     for ID tokens
+     * }
+     * @throws \Exception
+     */
+    public function fetchAuthToken(?callable $httpHandler = null)
+    {
+        $httpHandler = $httpHandler
+            ?: HttpHandlerFactory::build(HttpClientCache::getHttpClient());
+
+        if (!$this->hasCheckedOnGce) {
+            $this->isOnGce = self::onGce($httpHandler);
+            $this->hasCheckedOnGce = true;
+        }
+        if (!$this->isOnGce) {
+            return [];  // return an empty array with no access token
+        }
+
+        $response = $this->getFromMetadata(
+            $httpHandler,
+            $this->tokenUri,
+            $this->applyTokenEndpointMetrics([], $this->targetAudience ? 'it' : 'at')
+        );
+
+        if ($this->targetAudience) {
+            return $this->lastReceivedToken = ['id_token' => $response];
+        }
+
+        if (null === $json = json_decode($response, true)) {
+            throw new \Exception('Invalid JSON response');
+        }
+
+        $json['expires_at'] = time() + $json['expires_in'];
+
+        // store this so we can retrieve it later
+        $this->lastReceivedToken = $json;
+
+        return $json;
+    }
+
+    /**
+     * Returns the Cache Key for the credential token.
+     * The format for the cache key is:
+     * TokenURI
+     *
+     * @return string
+     */
+    public function getCacheKey()
+    {
+        return $this->tokenUri;
+    }
+
+    /**
+     * @return array<mixed>|null
+     */
+    public function getLastReceivedToken()
+    {
+        if ($this->lastReceivedToken) {
+            if (array_key_exists('id_token', $this->lastReceivedToken)) {
+                return $this->lastReceivedToken;
+            }
+
+            return [
+                'access_token' => $this->lastReceivedToken['access_token'],
+                'expires_at' => $this->lastReceivedToken['expires_at']
+            ];
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the client name from GCE metadata.
+     *
+     * Subsequent calls will return a cached value.
+     *
+     * @param callable|null $httpHandler callback which delivers psr7 request
+     * @return string
+     */
+    public function getClientName(?callable $httpHandler = null)
+    {
+        if ($this->clientName) {
+            return $this->clientName;
+        }
+
+        $httpHandler = $httpHandler
+            ?: HttpHandlerFactory::build(HttpClientCache::getHttpClient());
+
+        if (!$this->hasCheckedOnGce) {
+            $this->isOnGce = self::onGce($httpHandler);
+            $this->hasCheckedOnGce = true;
+        }
+
+        if (!$this->isOnGce) {
+            return '';
+        }
+
+        $this->clientName = $this->getFromMetadata(
+            $httpHandler,
+            self::getClientNameUri($this->serviceAccountIdentity)
+        );
+
+        return $this->clientName;
+    }
+
+    /**
+     * Fetch the default Project ID from compute engine.
+     *
+     * Returns null if called outside GCE.
+     *
+     * @param callable|null $httpHandler Callback which delivers psr7 request
+     * @return string|null
+     */
+    public function getProjectId(?callable $httpHandler = null)
+    {
+        if ($this->projectId) {
+            return $this->projectId;
+        }
+
+        $httpHandler = $httpHandler
+            ?: HttpHandlerFactory::build(HttpClientCache::getHttpClient());
+
+        if (!$this->hasCheckedOnGce) {
+            $this->isOnGce = self::onGce($httpHandler);
+            $this->hasCheckedOnGce = true;
+        }
+
+        if (!$this->isOnGce) {
+            return null;
+        }
+
+        $this->projectId = $this->getFromMetadata($httpHandler, self::getProjectIdUri());
+        return $this->projectId;
+    }
+
+    /**
+     * Fetch the default universe domain from the metadata server.
+     *
+     * @param callable|null $httpHandler Callback which delivers psr7 request
+     * @return string
+     */
+    public function getUniverseDomain(?callable $httpHandler = null): string
+    {
+        if (null !== $this->universeDomain) {
+            return $this->universeDomain;
+        }
+
+        $httpHandler = $httpHandler
+            ?: HttpHandlerFactory::build(HttpClientCache::getHttpClient());
+
+        if (!$this->hasCheckedOnGce) {
+            $this->isOnGce = self::onGce($httpHandler);
+            $this->hasCheckedOnGce = true;
+        }
+
+        try {
+            $this->universeDomain = $this->getFromMetadata(
+                $httpHandler,
+                self::getUniverseDomainUri()
+            );
+        } catch (ClientException $e) {
+            // If the metadata server exists, but returns a 404 for the universe domain, the auth
+            // libraries should safely assume this is an older metadata server running in GCU, and
+            // should return the default universe domain.
+            if (!$e->hasResponse() || 404 != $e->getResponse()->getStatusCode()) {
+                throw $e;
+            }
+            $this->universeDomain = self::DEFAULT_UNIVERSE_DOMAIN;
+        }
+
+        // We expect in some cases the metadata server will return an empty string for the universe
+        // domain. In this case, the auth library MUST return the default universe domain.
+        if ('' === $this->universeDomain) {
+            $this->universeDomain = self::DEFAULT_UNIVERSE_DOMAIN;
+        }
+
+        return $this->universeDomain;
+    }
+
+    /**
+     * Fetch the value of a GCE metadata server URI.
+     *
+     * @param callable $httpHandler An HTTP Handler to deliver PSR7 requests.
+     * @param string $uri The metadata URI.
+     * @param array<mixed> $headers [optional] If present, add these headers to the token
+     *        endpoint request.
+     *
+     * @return string
+     */
+    private function getFromMetadata(callable $httpHandler, $uri, array $headers = [])
+    {
+        $resp = $httpHandler(
+            new Request(
+                'GET',
+                $uri,
+                [self::FLAVOR_HEADER => 'Google'] + $headers
+            )
+        );
+
+        return (string) $resp->getBody();
+    }
+
+    /**
+     * Get the quota project used for this API request
+     *
+     * @return string|null
+     */
+    public function getQuotaProject()
+    {
+        return $this->quotaProject;
+    }
+
+    /**
+     * Set whether or not we've already checked the GCE environment.
+     *
+     * @param bool $isOnGce
+     *
+     * @return void
+     */
+    public function setIsOnGce($isOnGce)
+    {
+        // Implicitly set hasCheckedGce to true
+        $this->hasCheckedOnGce = true;
+
+        // Set isOnGce
+        $this->isOnGce = $isOnGce;
+    }
+
+    protected function getCredType(): string
+    {
+        return self::CRED_TYPE;
+    }
+}
Index: vendor/google/auth/src/Credentials/IAMCredentials.php
===================================================================
--- vendor/google/auth/src/Credentials/IAMCredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
+++ vendor/google/auth/src/Credentials/IAMCredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
@@ -0,0 +1,91 @@
+<?php
+/*
+ * 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.
+ */
+
+namespace Google\Auth\Credentials;
+
+/**
+ * Authenticates requests using IAM credentials.
+ */
+class IAMCredentials
+{
+    const SELECTOR_KEY = 'x-goog-iam-authority-selector';
+    const TOKEN_KEY = 'x-goog-iam-authorization-token';
+
+    /**
+     * @var string
+     */
+    private $selector;
+
+    /**
+     * @var string
+     */
+    private $token;
+
+    /**
+     * @param string $selector the IAM selector
+     * @param string $token the IAM token
+     */
+    public function __construct($selector, $token)
+    {
+        if (!is_string($selector)) {
+            throw new \InvalidArgumentException(
+                'selector must be a string'
+            );
+        }
+        if (!is_string($token)) {
+            throw new \InvalidArgumentException(
+                'token must be a string'
+            );
+        }
+
+        $this->selector = $selector;
+        $this->token = $token;
+    }
+
+    /**
+     * export a callback function which updates runtime metadata.
+     *
+     * @return callable updateMetadata function
+     */
+    public function getUpdateMetadataFunc()
+    {
+        return [$this, 'updateMetadata'];
+    }
+
+    /**
+     * Updates metadata with the appropriate header metadata.
+     *
+     * @param array<mixed> $metadata metadata hashmap
+     * @param string $unusedAuthUri optional auth uri
+     * @param callable|null $httpHandler callback which delivers psr7 request
+     *        Note: this param is unused here, only included here for
+     *        consistency with other credentials class
+     *
+     * @return array<mixed> updated metadata hashmap
+     */
+    public function updateMetadata(
+        $metadata,
+        $unusedAuthUri = null,
+        ?callable $httpHandler = null
+    ) {
+        $metadata_copy = $metadata;
+        $metadata_copy[self::SELECTOR_KEY] = $this->selector;
+        $metadata_copy[self::TOKEN_KEY] = $this->token;
+
+        return $metadata_copy;
+    }
+}
Index: vendor/google/auth/src/Credentials/ImpersonatedServiceAccountCredentials.php
===================================================================
--- vendor/google/auth/src/Credentials/ImpersonatedServiceAccountCredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
+++ vendor/google/auth/src/Credentials/ImpersonatedServiceAccountCredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
@@ -0,0 +1,156 @@
+<?php
+
+/*
+ * Copyright 2022 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.
+ */
+
+namespace Google\Auth\Credentials;
+
+use Google\Auth\CredentialsLoader;
+use Google\Auth\IamSignerTrait;
+use Google\Auth\SignBlobInterface;
+
+class ImpersonatedServiceAccountCredentials extends CredentialsLoader implements SignBlobInterface
+{
+    use IamSignerTrait;
+
+    private const CRED_TYPE = 'imp';
+
+    /**
+     * @var string
+     */
+    protected $impersonatedServiceAccountName;
+
+    /**
+     * @var UserRefreshCredentials
+     */
+    protected $sourceCredentials;
+
+    /**
+     * Instantiate an instance of ImpersonatedServiceAccountCredentials from a credentials file that
+     * has be created with the --impersonated-service-account flag.
+     *
+     * @param string|string[]     $scope   The scope of the access request, expressed either as an
+     *                                     array or as a space-delimited string.
+     * @param string|array<mixed> $jsonKey JSON credential file path or JSON credentials
+     *                                     as an associative array.
+     */
+    public function __construct(
+        $scope,
+        $jsonKey
+    ) {
+        if (is_string($jsonKey)) {
+            if (!file_exists($jsonKey)) {
+                throw new \InvalidArgumentException('file does not exist');
+            }
+            $json = file_get_contents($jsonKey);
+            if (!$jsonKey = json_decode((string) $json, true)) {
+                throw new \LogicException('invalid json for auth config');
+            }
+        }
+        if (!array_key_exists('service_account_impersonation_url', $jsonKey)) {
+            throw new \LogicException(
+                'json key is missing the service_account_impersonation_url field'
+            );
+        }
+        if (!array_key_exists('source_credentials', $jsonKey)) {
+            throw new \LogicException('json key is missing the source_credentials field');
+        }
+
+        $this->impersonatedServiceAccountName = $this->getImpersonatedServiceAccountNameFromUrl(
+            $jsonKey['service_account_impersonation_url']
+        );
+
+        $this->sourceCredentials = new UserRefreshCredentials(
+            $scope,
+            $jsonKey['source_credentials']
+        );
+    }
+
+    /**
+     * Helper function for extracting the Server Account Name from the URL saved in the account
+     * credentials file.
+     *
+     * @param $serviceAccountImpersonationUrl string URL from "service_account_impersonation_url"
+     * @return string Service account email or ID.
+     */
+    private function getImpersonatedServiceAccountNameFromUrl(
+        string $serviceAccountImpersonationUrl
+    ): string {
+        $fields = explode('/', $serviceAccountImpersonationUrl);
+        $lastField = end($fields);
+        $splitter = explode(':', $lastField);
+        return $splitter[0];
+    }
+
+    /**
+     * Get the client name from the keyfile
+     *
+     * In this implementation, it will return the issuers email from the oauth token.
+     *
+     * @param callable|null $unusedHttpHandler not used by this credentials type.
+     * @return string Token issuer email
+     */
+    public function getClientName(?callable $unusedHttpHandler = null)
+    {
+        return $this->impersonatedServiceAccountName;
+    }
+
+    /**
+     * @param callable|null $httpHandler
+     *
+     * @return array<mixed> {
+     *     A set of auth related metadata, containing the following
+     *
+     *     @type string $access_token
+     *     @type int $expires_in
+     *     @type string $scope
+     *     @type string $token_type
+     *     @type string $id_token
+     * }
+     */
+    public function fetchAuthToken(?callable $httpHandler = null)
+    {
+        // We don't support id token endpoint requests as of now for Impersonated Cred
+        return $this->sourceCredentials->fetchAuthToken(
+            $httpHandler,
+            $this->applyTokenEndpointMetrics([], 'at')
+        );
+    }
+
+    /**
+     * Returns the Cache Key for the credentials
+     * The cache key is the same as the UserRefreshCredentials class
+     *
+     * @return string
+     */
+    public function getCacheKey()
+    {
+        return $this->sourceCredentials->getCacheKey();
+    }
+
+    /**
+     * @return array<mixed>
+     */
+    public function getLastReceivedToken()
+    {
+        return $this->sourceCredentials->getLastReceivedToken();
+    }
+
+    protected function getCredType(): string
+    {
+        return self::CRED_TYPE;
+    }
+}
Index: vendor/google/auth/src/Credentials/InsecureCredentials.php
===================================================================
--- vendor/google/auth/src/Credentials/InsecureCredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
+++ vendor/google/auth/src/Credentials/InsecureCredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
@@ -0,0 +1,68 @@
+<?php
+/*
+ * 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.
+ */
+
+namespace Google\Auth\Credentials;
+
+use Google\Auth\FetchAuthTokenInterface;
+
+/**
+ * Provides a set of credentials that will always return an empty access token.
+ * This is useful for APIs which do not require authentication, for local
+ * service emulators, and for testing.
+ */
+class InsecureCredentials implements FetchAuthTokenInterface
+{
+    /**
+     * @var array{access_token:string}
+     */
+    private $token = [
+        'access_token' => ''
+    ];
+
+    /**
+     * Fetches the auth token. In this case it returns an empty string.
+     *
+     * @param callable|null $httpHandler
+     * @return array{access_token:string} A set of auth related metadata
+     */
+    public function fetchAuthToken(?callable $httpHandler = null)
+    {
+        return $this->token;
+    }
+
+    /**
+     * Returns the cache key. In this case it returns a null value, disabling
+     * caching.
+     *
+     * @return string|null
+     */
+    public function getCacheKey()
+    {
+        return null;
+    }
+
+    /**
+     * Fetches the last received token. In this case, it returns the same empty string
+     * auth token.
+     *
+     * @return array{access_token:string}
+     */
+    public function getLastReceivedToken()
+    {
+        return $this->token;
+    }
+}
Index: vendor/google/auth/src/Credentials/ServiceAccountCredentials.php
===================================================================
--- vendor/google/auth/src/Credentials/ServiceAccountCredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
+++ vendor/google/auth/src/Credentials/ServiceAccountCredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
@@ -0,0 +1,453 @@
+<?php
+/*
+ * 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.
+ */
+
+namespace Google\Auth\Credentials;
+
+use Firebase\JWT\JWT;
+use Google\Auth\CredentialsLoader;
+use Google\Auth\GetQuotaProjectInterface;
+use Google\Auth\Iam;
+use Google\Auth\OAuth2;
+use Google\Auth\ProjectIdProviderInterface;
+use Google\Auth\ServiceAccountSignerTrait;
+use Google\Auth\SignBlobInterface;
+use InvalidArgumentException;
+
+/**
+ * ServiceAccountCredentials supports authorization using a Google service
+ * account.
+ *
+ * (cf https://developers.google.com/accounts/docs/OAuth2ServiceAccount)
+ *
+ * It's initialized using the json key file that's downloadable from developer
+ * console, which should contain a private_key and client_email fields that it
+ * uses.
+ *
+ * Use it with AuthTokenMiddleware to authorize http requests:
+ *
+ *   use Google\Auth\Credentials\ServiceAccountCredentials;
+ *   use Google\Auth\Middleware\AuthTokenMiddleware;
+ *   use GuzzleHttp\Client;
+ *   use GuzzleHttp\HandlerStack;
+ *
+ *   $sa = new ServiceAccountCredentials(
+ *       'https://www.googleapis.com/auth/taskqueue',
+ *       '/path/to/your/json/key_file.json'
+ *   );
+ *   $middleware = new AuthTokenMiddleware($sa);
+ *   $stack = HandlerStack::create();
+ *   $stack->push($middleware);
+ *
+ *   $client = new Client([
+ *       'handler' => $stack,
+ *       'base_uri' => 'https://www.googleapis.com/taskqueue/v1beta2/projects/',
+ *       'auth' => 'google_auth' // authorize all requests
+ *   ]);
+ *
+ *   $res = $client->get('myproject/taskqueues/myqueue');
+ */
+class ServiceAccountCredentials extends CredentialsLoader implements
+    GetQuotaProjectInterface,
+    SignBlobInterface,
+    ProjectIdProviderInterface
+{
+    use ServiceAccountSignerTrait;
+
+    /**
+     * Used in observability metric headers
+     *
+     * @var string
+     */
+    private const CRED_TYPE = 'sa';
+    private const IAM_SCOPE = 'https://www.googleapis.com/auth/iam';
+
+    /**
+     * The OAuth2 instance used to conduct authorization.
+     *
+     * @var OAuth2
+     */
+    protected $auth;
+
+    /**
+     * The quota project associated with the JSON credentials
+     *
+     * @var string
+     */
+    protected $quotaProject;
+
+    /**
+     * @var string|null
+     */
+    protected $projectId;
+
+    /**
+     * @var array<mixed>|null
+     */
+    private $lastReceivedJwtAccessToken;
+
+    /**
+     * @var bool
+     */
+    private $useJwtAccessWithScope = false;
+
+    /**
+     * @var ServiceAccountJwtAccessCredentials|null
+     */
+    private $jwtAccessCredentials;
+
+    /**
+     * @var string
+     */
+    private string $universeDomain;
+
+    /**
+     * Create a new ServiceAccountCredentials.
+     *
+     * @param string|string[]|null $scope the scope of the access request, expressed
+     *   either as an Array or as a space-delimited String.
+     * @param string|array<mixed> $jsonKey JSON credential file path or JSON credentials
+     *   as an associative array
+     * @param string $sub an email address account to impersonate, in situations when
+     *   the service account has been delegated domain wide access.
+     * @param string $targetAudience The audience for the ID token.
+     */
+    public function __construct(
+        $scope,
+        $jsonKey,
+        $sub = null,
+        $targetAudience = null
+    ) {
+        if (is_string($jsonKey)) {
+            if (!file_exists($jsonKey)) {
+                throw new \InvalidArgumentException('file does not exist');
+            }
+            $jsonKeyStream = file_get_contents($jsonKey);
+            if (!$jsonKey = json_decode((string) $jsonKeyStream, true)) {
+                throw new \LogicException('invalid json for auth config');
+            }
+        }
+        if (!array_key_exists('client_email', $jsonKey)) {
+            throw new \InvalidArgumentException(
+                'json key is missing the client_email field'
+            );
+        }
+        if (!array_key_exists('private_key', $jsonKey)) {
+            throw new \InvalidArgumentException(
+                'json key is missing the private_key field'
+            );
+        }
+        if (array_key_exists('quota_project_id', $jsonKey)) {
+            $this->quotaProject = (string) $jsonKey['quota_project_id'];
+        }
+        if ($scope && $targetAudience) {
+            throw new InvalidArgumentException(
+                'Scope and targetAudience cannot both be supplied'
+            );
+        }
+        $additionalClaims = [];
+        if ($targetAudience) {
+            $additionalClaims = ['target_audience' => $targetAudience];
+        }
+        $this->auth = new OAuth2([
+            'audience' => self::TOKEN_CREDENTIAL_URI,
+            'issuer' => $jsonKey['client_email'],
+            'scope' => $scope,
+            'signingAlgorithm' => 'RS256',
+            'signingKey' => $jsonKey['private_key'],
+            'signingKeyId' => $jsonKey['private_key_id'] ?? null,
+            'sub' => $sub,
+            'tokenCredentialUri' => self::TOKEN_CREDENTIAL_URI,
+            'additionalClaims' => $additionalClaims,
+        ]);
+
+        $this->projectId = $jsonKey['project_id'] ?? null;
+        $this->universeDomain = $jsonKey['universe_domain'] ?? self::DEFAULT_UNIVERSE_DOMAIN;
+    }
+
+    /**
+     * When called, the ServiceAccountCredentials will use an instance of
+     * ServiceAccountJwtAccessCredentials to fetch (self-sign) an access token
+     * even when only scopes are supplied. Otherwise,
+     * ServiceAccountJwtAccessCredentials is only called when no scopes and an
+     * authUrl (audience) is suppled.
+     *
+     * @return void
+     */
+    public function useJwtAccessWithScope()
+    {
+        $this->useJwtAccessWithScope = true;
+    }
+
+    /**
+     * @param callable|null $httpHandler
+     *
+     * @return array<mixed> {
+     *     A set of auth related metadata, containing the following
+     *
+     *     @type string $access_token
+     *     @type int $expires_in
+     *     @type string $token_type
+     * }
+     */
+    public function fetchAuthToken(?callable $httpHandler = null)
+    {
+        if ($this->useSelfSignedJwt()) {
+            $jwtCreds = $this->createJwtAccessCredentials();
+
+            $accessToken = $jwtCreds->fetchAuthToken($httpHandler);
+
+            if ($lastReceivedToken = $jwtCreds->getLastReceivedToken()) {
+                // Keep self-signed JWTs in memory as the last received token
+                $this->lastReceivedJwtAccessToken = $lastReceivedToken;
+            }
+
+            return $accessToken;
+        }
+
+        if ($this->isIdTokenRequest() && $this->getUniverseDomain() !== self::DEFAULT_UNIVERSE_DOMAIN) {
+            $now = time();
+            $jwt = Jwt::encode(
+                [
+                    'iss' => $this->auth->getIssuer(),
+                    'sub' => $this->auth->getIssuer(),
+                    'scope' => self::IAM_SCOPE,
+                    'exp' => ($now + $this->auth->getExpiry()),
+                    'iat' => ($now - OAuth2::DEFAULT_SKEW_SECONDS),
+                ],
+                $this->auth->getSigningKey(),
+                $this->auth->getSigningAlgorithm(),
+                $this->auth->getSigningKeyId()
+            );
+            // We create a new instance of Iam each time because the `$httpHandler` might change.
+            $idToken = (new Iam($httpHandler, $this->getUniverseDomain()))->generateIdToken(
+                $this->auth->getIssuer(),
+                $this->auth->getAdditionalClaims()['target_audience'],
+                $jwt,
+                $this->applyTokenEndpointMetrics([], 'it')
+            );
+            return ['id_token' => $idToken];
+        }
+        return $this->auth->fetchAuthToken(
+            $httpHandler,
+            $this->applyTokenEndpointMetrics([], $this->isIdTokenRequest() ? 'it' : 'at')
+        );
+    }
+
+    /**
+     * Return the Cache Key for the credentials.
+     * For the cache key format is one of the following:
+     * ClientEmail.Scope[.Sub]
+     * ClientEmail.Audience[.Sub]
+     *
+     * @return string
+     */
+    public function getCacheKey()
+    {
+        $scopeOrAudience = $this->auth->getScope();
+        if (!$scopeOrAudience) {
+            $scopeOrAudience = $this->auth->getAudience();
+        }
+
+        $key = $this->auth->getIssuer() . '.' . $scopeOrAudience;
+        if ($sub = $this->auth->getSub()) {
+            $key .= '.' . $sub;
+        }
+
+        return $key;
+    }
+
+    /**
+     * @return array<mixed>
+     */
+    public function getLastReceivedToken()
+    {
+        // If self-signed JWTs are being used, fetch the last received token
+        // from memory. Else, fetch it from OAuth2
+        return $this->useSelfSignedJwt()
+            ? $this->lastReceivedJwtAccessToken
+            : $this->auth->getLastReceivedToken();
+    }
+
+    /**
+     * Get the project ID from the service account keyfile.
+     *
+     * Returns null if the project ID does not exist in the keyfile.
+     *
+     * @param callable|null $httpHandler Not used by this credentials type.
+     * @return string|null
+     */
+    public function getProjectId(?callable $httpHandler = null)
+    {
+        return $this->projectId;
+    }
+
+    /**
+     * Updates metadata with the authorization token.
+     *
+     * @param array<mixed> $metadata metadata hashmap
+     * @param string $authUri optional auth uri
+     * @param callable|null $httpHandler callback which delivers psr7 request
+     * @return array<mixed> updated metadata hashmap
+     */
+    public function updateMetadata(
+        $metadata,
+        $authUri = null,
+        ?callable $httpHandler = null
+    ) {
+        // scope exists. use oauth implementation
+        if (!$this->useSelfSignedJwt()) {
+            return parent::updateMetadata($metadata, $authUri, $httpHandler);
+        }
+
+        $jwtCreds = $this->createJwtAccessCredentials();
+        if ($this->auth->getScope()) {
+            // Prefer user-provided "scope" to "audience"
+            $updatedMetadata = $jwtCreds->updateMetadata($metadata, null, $httpHandler);
+        } else {
+            $updatedMetadata = $jwtCreds->updateMetadata($metadata, $authUri, $httpHandler);
+        }
+
+        if ($lastReceivedToken = $jwtCreds->getLastReceivedToken()) {
+            // Keep self-signed JWTs in memory as the last received token
+            $this->lastReceivedJwtAccessToken = $lastReceivedToken;
+        }
+
+        return $updatedMetadata;
+    }
+
+    /**
+     * @return ServiceAccountJwtAccessCredentials
+     */
+    private function createJwtAccessCredentials()
+    {
+        if (!$this->jwtAccessCredentials) {
+            // Create credentials for self-signing a JWT (JwtAccess)
+            $credJson = [
+                'private_key' => $this->auth->getSigningKey(),
+                'client_email' => $this->auth->getIssuer(),
+            ];
+            $this->jwtAccessCredentials = new ServiceAccountJwtAccessCredentials(
+                $credJson,
+                $this->auth->getScope()
+            );
+        }
+
+        return $this->jwtAccessCredentials;
+    }
+
+    /**
+     * @param string $sub an email address account to impersonate, in situations when
+     *   the service account has been delegated domain wide access.
+     * @return void
+     */
+    public function setSub($sub)
+    {
+        $this->auth->setSub($sub);
+    }
+
+    /**
+     * Get the client name from the keyfile.
+     *
+     * In this case, it returns the keyfile's client_email key.
+     *
+     * @param callable|null $httpHandler Not used by this credentials type.
+     * @return string
+     */
+    public function getClientName(?callable $httpHandler = null)
+    {
+        return $this->auth->getIssuer();
+    }
+
+    /**
+     * Get the private key from the keyfile.
+     *
+     * In this case, it returns the keyfile's private_key key, needed for JWT signing.
+     *
+     * @return string
+     */
+    public function getPrivateKey()
+    {
+        return $this->auth->getSigningKey();
+    }
+
+    /**
+     * Get the quota project used for this API request
+     *
+     * @return string|null
+     */
+    public function getQuotaProject()
+    {
+        return $this->quotaProject;
+    }
+
+    /**
+     * Get the universe domain configured in the JSON credential.
+     *
+     * @return string
+     */
+    public function getUniverseDomain(): string
+    {
+        return $this->universeDomain;
+    }
+
+    protected function getCredType(): string
+    {
+        return self::CRED_TYPE;
+    }
+
+    /**
+     * @return bool
+     */
+    private function useSelfSignedJwt()
+    {
+        // When a sub is supplied, the user is using domain-wide delegation, which not available
+        // with self-signed JWTs
+        if (null !== $this->auth->getSub()) {
+            // If we are outside the GDU, we can't use domain-wide delegation
+            if ($this->getUniverseDomain() !== self::DEFAULT_UNIVERSE_DOMAIN) {
+                throw new \LogicException(sprintf(
+                    'Service Account subject is configured for the credential. Domain-wide ' .
+                    'delegation is not supported in universes other than %s.',
+                    self::DEFAULT_UNIVERSE_DOMAIN
+                ));
+            }
+            return false;
+        }
+
+        // Do not use self-signed JWT for ID tokens
+        if ($this->isIdTokenRequest()) {
+            return false;
+        }
+
+        // When true, ServiceAccountCredentials will always use JwtAccess for access tokens
+        if ($this->useJwtAccessWithScope) {
+            return true;
+        }
+
+        // If the universe domain is outside the GDU, use JwtAccess for access tokens
+        if ($this->getUniverseDomain() !== self::DEFAULT_UNIVERSE_DOMAIN) {
+            return true;
+        }
+
+        return is_null($this->auth->getScope());
+    }
+
+    private function isIdTokenRequest(): bool
+    {
+        return !empty($this->auth->getAdditionalClaims()['target_audience']);
+    }
+}
Index: vendor/google/auth/src/Credentials/ServiceAccountJwtAccessCredentials.php
===================================================================
--- vendor/google/auth/src/Credentials/ServiceAccountJwtAccessCredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
+++ vendor/google/auth/src/Credentials/ServiceAccountJwtAccessCredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
@@ -0,0 +1,246 @@
+<?php
+/*
+ * 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.
+ */
+
+namespace Google\Auth\Credentials;
+
+use Google\Auth\CredentialsLoader;
+use Google\Auth\GetQuotaProjectInterface;
+use Google\Auth\OAuth2;
+use Google\Auth\ProjectIdProviderInterface;
+use Google\Auth\ServiceAccountSignerTrait;
+use Google\Auth\SignBlobInterface;
+
+/**
+ * Authenticates requests using Google's Service Account credentials via
+ * JWT Access.
+ *
+ * This class allows authorizing requests for service accounts directly
+ * from credentials from a json key file downloaded from the developer
+ * console (via 'Generate new Json Key').  It is not part of any OAuth2
+ * flow, rather it creates a JWT and sends that as a credential.
+ */
+class ServiceAccountJwtAccessCredentials extends CredentialsLoader implements
+    GetQuotaProjectInterface,
+    SignBlobInterface,
+    ProjectIdProviderInterface
+{
+    use ServiceAccountSignerTrait;
+
+    /**
+     * Used in observability metric headers
+     *
+     * @var string
+     */
+    private const CRED_TYPE = 'jwt';
+
+    /**
+     * The OAuth2 instance used to conduct authorization.
+     *
+     * @var OAuth2
+     */
+    protected $auth;
+
+    /**
+     * The quota project associated with the JSON credentials
+     *
+     * @var string
+     */
+    protected $quotaProject;
+
+    /**
+     * @var string
+     */
+    public $projectId;
+
+    /**
+     * Create a new ServiceAccountJwtAccessCredentials.
+     *
+     * @param string|array<mixed> $jsonKey JSON credential file path or JSON credentials
+     *   as an associative array
+     * @param string|string[] $scope the scope of the access request, expressed
+     *   either as an Array or as a space-delimited String.
+     */
+    public function __construct($jsonKey, $scope = null)
+    {
+        if (is_string($jsonKey)) {
+            if (!file_exists($jsonKey)) {
+                throw new \InvalidArgumentException('file does not exist');
+            }
+            $jsonKeyStream = file_get_contents($jsonKey);
+            if (!$jsonKey = json_decode((string) $jsonKeyStream, true)) {
+                throw new \LogicException('invalid json for auth config');
+            }
+        }
+        if (!array_key_exists('client_email', $jsonKey)) {
+            throw new \InvalidArgumentException(
+                'json key is missing the client_email field'
+            );
+        }
+        if (!array_key_exists('private_key', $jsonKey)) {
+            throw new \InvalidArgumentException(
+                'json key is missing the private_key field'
+            );
+        }
+        if (array_key_exists('quota_project_id', $jsonKey)) {
+            $this->quotaProject = (string) $jsonKey['quota_project_id'];
+        }
+        $this->auth = new OAuth2([
+            'issuer' => $jsonKey['client_email'],
+            'sub' => $jsonKey['client_email'],
+            'signingAlgorithm' => 'RS256',
+            'signingKey' => $jsonKey['private_key'],
+            'scope' => $scope,
+        ]);
+
+        $this->projectId = $jsonKey['project_id'] ?? null;
+    }
+
+    /**
+     * Updates metadata with the authorization token.
+     *
+     * @param array<mixed> $metadata metadata hashmap
+     * @param string $authUri optional auth uri
+     * @param callable|null $httpHandler callback which delivers psr7 request
+     * @return array<mixed> updated metadata hashmap
+     */
+    public function updateMetadata(
+        $metadata,
+        $authUri = null,
+        ?callable $httpHandler = null
+    ) {
+        $scope = $this->auth->getScope();
+        if (empty($authUri) && empty($scope)) {
+            return $metadata;
+        }
+
+        $this->auth->setAudience($authUri);
+
+        return parent::updateMetadata($metadata, $authUri, $httpHandler);
+    }
+
+    /**
+     * Implements FetchAuthTokenInterface#fetchAuthToken.
+     *
+     * @param callable|null $httpHandler
+     *
+     * @return null|array{access_token:string} A set of auth related metadata
+     */
+    public function fetchAuthToken(?callable $httpHandler = null)
+    {
+        $audience = $this->auth->getAudience();
+        $scope = $this->auth->getScope();
+        if (empty($audience) && empty($scope)) {
+            return null;
+        }
+
+        if (!empty($audience) && !empty($scope)) {
+            throw new \UnexpectedValueException(
+                'Cannot sign both audience and scope in JwtAccess'
+            );
+        }
+
+        $access_token = $this->auth->toJwt();
+
+        // Set the self-signed access token in OAuth2 for getLastReceivedToken
+        $this->auth->setAccessToken($access_token);
+
+        return [
+            'access_token' => $access_token,
+            'expires_in' => $this->auth->getExpiry(),
+            'token_type' => 'Bearer'
+        ];
+    }
+
+    /**
+     * Return the cache key for the credentials.
+     * The format for the Cache Key one of the following:
+     * ClientEmail.Scope
+     * ClientEmail.Audience
+     *
+     * @return string
+     */
+    public function getCacheKey()
+    {
+        $scopeOrAudience = $this->auth->getScope();
+        if (!$scopeOrAudience) {
+            $scopeOrAudience = $this->auth->getAudience();
+        }
+
+        return $this->auth->getIssuer() . '.' . $scopeOrAudience;
+    }
+
+    /**
+     * @return array<mixed>
+     */
+    public function getLastReceivedToken()
+    {
+        return $this->auth->getLastReceivedToken();
+    }
+
+    /**
+     * Get the project ID from the service account keyfile.
+     *
+     * Returns null if the project ID does not exist in the keyfile.
+     *
+     * @param callable|null $httpHandler Not used by this credentials type.
+     * @return string|null
+     */
+    public function getProjectId(?callable $httpHandler = null)
+    {
+        return $this->projectId;
+    }
+
+    /**
+     * Get the client name from the keyfile.
+     *
+     * In this case, it returns the keyfile's client_email key.
+     *
+     * @param callable|null $httpHandler Not used by this credentials type.
+     * @return string
+     */
+    public function getClientName(?callable $httpHandler = null)
+    {
+        return $this->auth->getIssuer();
+    }
+
+    /**
+     * Get the private key from the keyfile.
+     *
+     * In this case, it returns the keyfile's private_key key, needed for JWT signing.
+     *
+     * @return string
+     */
+    public function getPrivateKey()
+    {
+        return $this->auth->getSigningKey();
+    }
+
+    /**
+     * Get the quota project used for this API request
+     *
+     * @return string|null
+     */
+    public function getQuotaProject()
+    {
+        return $this->quotaProject;
+    }
+
+    protected function getCredType(): string
+    {
+        return self::CRED_TYPE;
+    }
+}
Index: vendor/google/auth/src/Credentials/UserRefreshCredentials.php
===================================================================
--- vendor/google/auth/src/Credentials/UserRefreshCredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
+++ vendor/google/auth/src/Credentials/UserRefreshCredentials.php	(revision e3d4e0a8bc0f13135f27c0b055b5c00f64a28f01)
@@ -0,0 +1,202 @@
+<?php
+/*
+ * 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.
+ */
+
+namespace Google\Auth\Credentials;
+
+use Google\Auth\CredentialsLoader;
+use Google\Auth\GetQuotaProjectInterface;
+use Google\Auth\OAuth2;
+use InvalidArgumentException;
+use LogicException;
+
+/**
+ * Authenticates requests using User Refresh credentials.
+ *
+ * This class allows authorizing requests from user refresh tokens.
+ *
+ * This the end of the result of a 3LO flow.  E.g, the end result of
+ * 'gcloud auth login' saves a file with these contents in well known
+ * location
+ *
+ * @see [Application Default Credentials](http://goo.gl/mkAHpZ)
+ */
+class UserRefreshCredentials extends CredentialsLoader implements GetQuotaProjectInterface
+{
+    /**
+     * Used in observability metric headers
+     *
+     * @var string
+     */
+    private const CRED_TYPE = 'u';
+
+    /**
+     * The OAuth2 instance used to conduct authorization.
+     *
+     * @var OAuth2
+     */
+    protected $auth;
+
+    /**
+     * The quota project associated with the JSON credentials
+     *
+     * @var string
+     */
+    protected $quotaProject;
+
+    /**
+     * Whether this is an ID token request or an access token request. Used when
+     * building the metric header.
+     */
+    private bool $isIdTokenRequest = false;
+
+    /**
+     * Create a new UserRefreshCredentials.
+     *
+     * @param string|string[]|null $scope the scope of the access request, expressed
+     *   either as an Array or as a space-delimited String.
+     * @param string|array<mixed> $jsonKey JSON credential file path or JSON credentials
+     *   as an associative array
+     * @param string|null $targetAudience The audience for the ID token.
+     */
+    public function __construct(
+        $scope,
+        $jsonKey,
+        string $targetAudience = null
+    ) {
+        if (is_string($jsonKey)) {
+            if (!file_exists($jsonKey)) {
+                throw new InvalidArgumentException('file does not exist or is unreadable');
+            }
+            $json = file_get_contents($jsonKey);
+            if (!$jsonKey = json_decode((string) $json, true)) {
+                throw new LogicException('invalid json for auth config');
+            }
+        }
+        if (!array_key_exists('client_id', $jsonKey)) {
+            throw new InvalidArgumentException(
+                'json key is missing the client_id field'
+            );
+        }
+        if (!array_key_exists('client_secret', $jsonKey)) {
+            throw new InvalidArgumentException(
+                'json key is missing the client_secret field'
+            );
+        }
+        if (!array_key_exists('refresh_token', $jsonKey)) {
+            throw new InvalidArgumentException(
+                'json key is missing the refresh_token field'
+            );
+        }
+        if ($scope && $targetAudience) {
+            throw new InvalidArgumentException(
+                'Scope and targetAudience cannot both be supplied'
+            );
+        }
+        $additionalClaims = [];
+        if ($targetAudience) {
+            $additionalClaims = ['target_audience' => $targetAudience];
+            $this->isIdTokenRequest = true;
+        }
+        $this->auth = new OAuth2([
+            'clientId' => $jsonKey['client_id'],
+            'clientSecret' => $jsonKey['client_secret'],
+            'refresh_token' => $jsonKey['refresh_token'],
+            'scope' => $scope,
+            'tokenCredentialUri' => self::TOKEN_CREDENTIAL_URI,
+            'additionalClaims' => $additionalClaims,
+        ]);
+        if (array_key_exists('quota_project_id', $jsonKey)) {
+            $this->quotaProject = (string) $jsonKey['quota_project_id'];
+        }
+    }
+
+    /**
+     * @param callable|null $httpHandler
+     * @param array<mixed> $metricsHeader [optional] Metrics headers to be inserted
+     *     into the token endpoint request present.
+     *     This could be passed from ImersonatedServiceAccountCredentials as it uses
+     *     UserRefreshCredentials as source credentials.
+     *
+     * @return array<mixed> {
+     *     A set of auth related metadata, containing the following
+     *
+     *     @type string $access_token
+     *     @type int $expires_in
+     *     @type string $scope
+     *     @type string $token_type
+     *     @type string $id_token
+     * }
+     */
+    public function fetchAuthToken(?callable $httpHandler = null, array $metricsHeader = [])
+    {
+        return $this->auth->fetchAuthToken(
+            $httpHandler,
+            $this->applyTokenEndpointMetrics($metricsHeader, $this->isIdTokenRequest ? 'it' : 'at')
+        );
+    }
+
+    /**
+     * Return the Cache Key for the credentials.
+     * The format for the Cache key is one of the following:
+     * ClientId.Scope
+     * ClientId.Audience
+     *
+     * @return string
+     */
+    public function getCacheKey()
+    {
+        $scopeOrAudience = $this->auth->getScope();
+        if (!$scopeOrAudience) {
+            $scopeOrAudience = $this->auth->getAudience();
+        }
+
+        return $this->auth->getClientId() . '.' . $scopeOrAudience;
+    }
+
+    /**
+     * @return array<mixed>
+     */
+    public function getLastReceivedToken()
+    {
+        return $this->auth->getLastReceivedToken();
+    }
+
+    /**
+     * Get the quota project used for this API request
+     *
+     * @return string|null
+     */
+    public function getQuotaProject()
+    {
+        return $this->quotaProject;
+    }
+
+    /**
+     * Get the granted scopes (if they exist) for the last fetched token.
+     *
+     * @return string|null
+     */
+    public function getGrantedScope()
+    {
+        return $this->auth->getGrantedScope();
+    }
+
+    protected function getCredType(): string
+    {
+        return self::CRED_TYPE;
+    }
+}
