$jsonKey JSON credential file path or JSON credentials * as an associative array */ public function __construct( string|array|null $scope, array $jsonKey, ) { 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 (!array_key_exists('token_url', $jsonKey)) { throw new InvalidArgumentException( 'json key is missing the token_url field' ); } $this->clientId = $jsonKey['client_id']; $this->clientSecret = $jsonKey['client_secret']; $this->universeDomain = $jsonKey['universe_domain'] ?? GetUniverseDomainInterface::DEFAULT_UNIVERSE_DOMAIN; $this->auth = new OAuth2([ 'refresh_token' => $jsonKey['refresh_token'], 'tokenCredentialUri' => $jsonKey['token_url'], 'scope' => $scope, ]); if (array_key_exists('quota_project_id', $jsonKey)) { $this->quotaProject = (string) $jsonKey['quota_project_id']; } } /** * @param callable|null $httpHandler * @param array $headers * * @return array { * 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, array $headers = []) { $headers['Authorization'] = sprintf( 'Basic %s', base64_encode($this->clientId . ':' . $this->clientSecret) ); return $this->auth->fetchAuthToken( $httpHandler, $this->applyTokenEndpointMetrics($headers, 'at') ); } /** * Updates metadata with the authorization token. * * @param array $metadata metadata hashmap * @param string $authUri optional auth uri * @param callable|null $httpHandler callback which delivers psr7 request * @return array updated metadata hashmap */ public function updateMetadata( $metadata, $authUri = null, ?callable $httpHandler = null ) { $metadata = $this->traitUpdateMetadata($metadata, $authUri, $httpHandler); if ($this->enableRegionalAccessBoundary) { $metadata = $this->updateRegionalAccessBoundaryMetadata( $metadata, $this->buildRegionalAccessBoundaryLookupUrl(), $this->getUniverseDomain(), $httpHandler, ); } return $metadata; } /** * Return the Cache Key for the credentials. * The format for the Cache key is * Hash(ClientId.Scope.RefreshToken) * * @return string */ public function getCacheKey() { return hash('sha256', implode('.', [ $this->clientId, $this->auth->getScope(), $this->auth->getRefreshToken() ])); } /** * @return array */ public function getLastReceivedToken() { return $this->auth->getLastReceivedToken(); } /** * Get the quota project used for this API request * * @return string|null */ public function getQuotaProject(): string|null { return $this->quotaProject; } /** * Get the universe domain used for this API request * * @return string */ public function getUniverseDomain(): string { return $this->universeDomain; } /** * 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; } /** * Builds and returns the URL for the RAB lookup API. */ private function buildRegionalAccessBoundaryLookupUrl(): string { // Try to parse as a workload identity pool. // Audience format: //iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID $regex = '/projects\/([^\/]+)\/locations\/global\/workloadIdentityPools\/([^\/]+)/'; if (preg_match($regex, $this->auth->getAudience(), $matches)) { [$_, $projectNumber, $poolId] = $matches; return $this->traitBuildRegionalAccessBoundaryLookupUrl( poolId: $poolId, projectNumber: $projectNumber, ); } // If that fails, try to parse as a workforce pool. // Audience format: //iam.googleapis.com/locations/global/workforcePools/POOL_ID/providers/PROVIDER_ID if (preg_match('/locations\/[^\/]+\/workforcePools\/([^\/]+)/', $this->auth->getAudience(), $matches)) { return $this->traitBuildRegionalAccessBoundaryLookupUrl( poolId: $matches[1], ); } throw new LogicException('Invalid audience format'); } }