updated plugin WP Mail SMTP
version 2.0.0
This commit is contained in:
@ -17,6 +17,8 @@
|
||||
|
||||
namespace Google\Auth;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use Firebase\JWT\ExpiredException;
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\SignatureInvalidException;
|
||||
@ -25,9 +27,16 @@ use Google\Auth\HttpHandler\HttpClientCache;
|
||||
use Google\Auth\HttpHandler\HttpHandlerFactory;
|
||||
use GuzzleHttp\Psr7;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use InvalidArgumentException;
|
||||
use phpseclib\Crypt\RSA;
|
||||
use phpseclib\Math\BigInteger;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use RuntimeException;
|
||||
use SimpleJWT\InvalidTokenException;
|
||||
use SimpleJWT\JWT as SimpleJWT;
|
||||
use SimpleJWT\Keys\KeyFactory;
|
||||
use SimpleJWT\Keys\KeySet;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Wrapper around Google Access Tokens which provides convenience functions.
|
||||
@ -37,6 +46,8 @@ use Psr\Cache\CacheItemPoolInterface;
|
||||
class AccessToken
|
||||
{
|
||||
const FEDERATED_SIGNON_CERT_URL = 'https://www.googleapis.com/oauth2/v3/certs';
|
||||
const IAP_CERT_URL = 'https://www.gstatic.com/iap/verify/public_key-jwk';
|
||||
const IAP_ISSUER = 'https://cloud.google.com/iap';
|
||||
const OAUTH2_ISSUER = 'accounts.google.com';
|
||||
const OAUTH2_ISSUER_HTTPS = 'https://accounts.google.com';
|
||||
const OAUTH2_REVOKE_URI = 'https://oauth2.googleapis.com/revoke';
|
||||
@ -59,19 +70,9 @@ class AccessToken
|
||||
callable $httpHandler = null,
|
||||
CacheItemPoolInterface $cache = null
|
||||
) {
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!class_exists('phpseclib\Crypt\RSA')) {
|
||||
throw new \RuntimeException('Please require phpseclib/phpseclib v2 to use this utility.');
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
$this->httpHandler = $httpHandler
|
||||
?: HttpHandlerFactory::build(HttpClientCache::getHttpClient());
|
||||
$this->cache = $cache ?: new MemoryCacheItemPool();
|
||||
$this->configureJwtService();
|
||||
|
||||
// set phpseclib constants if applicable
|
||||
$this->setPhpsecConstants();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,87 +86,215 @@ class AccessToken
|
||||
* Configuration options.
|
||||
*
|
||||
* @type string $audience The indended recipient of the token.
|
||||
* @type string $issuer The intended issuer of the token.
|
||||
* @type string $certsLocation The location (remote or local) from which
|
||||
* to retrieve certificates, if not cached. This value should only be
|
||||
* provided in limited circumstances in which you are sure of the
|
||||
* behavior.
|
||||
* @type string $cacheKey The cache key of the cached certs. Defaults to
|
||||
* the sha1 of $certsLocation if provided, otherwise is set to
|
||||
* "federated_signon_certs_v3".
|
||||
* @type bool $throwException Whether the function should throw an
|
||||
* exception if the verification fails. This is useful for
|
||||
* determining the reason verification failed.
|
||||
* }
|
||||
* @return array|bool the token payload, if successful, or false if not.
|
||||
* @throws \InvalidArgumentException If certs could not be retrieved from a local file.
|
||||
* @throws \InvalidArgumentException If received certs are in an invalid format.
|
||||
* @throws \RuntimeException If certs could not be retrieved from a remote location.
|
||||
* @throws InvalidArgumentException If certs could not be retrieved from a local file.
|
||||
* @throws InvalidArgumentException If received certs are in an invalid format.
|
||||
* @throws InvalidArgumentException If the cert alg is not supported.
|
||||
* @throws RuntimeException If certs could not be retrieved from a remote location.
|
||||
* @throws UnexpectedValueException If the token issuer does not match.
|
||||
* @throws UnexpectedValueException If the token audience does not match.
|
||||
*/
|
||||
public function verify($token, array $options = [])
|
||||
{
|
||||
$audience = isset($options['audience'])
|
||||
? $options['audience']
|
||||
: null;
|
||||
$issuer = isset($options['issuer'])
|
||||
? $options['issuer']
|
||||
: null;
|
||||
$certsLocation = isset($options['certsLocation'])
|
||||
? $options['certsLocation']
|
||||
: self::FEDERATED_SIGNON_CERT_URL;
|
||||
|
||||
unset($options['audience'], $options['certsLocation']);
|
||||
$cacheKey = isset($options['cacheKey'])
|
||||
? $options['cacheKey']
|
||||
: $this->getCacheKeyFromCertLocation($certsLocation);
|
||||
$throwException = isset($options['throwException'])
|
||||
? $options['throwException']
|
||||
: false; // for backwards compatibility
|
||||
|
||||
// Check signature against each available cert.
|
||||
// allow the loop to complete unless a known bad result is encountered.
|
||||
$certs = $this->getFederatedSignOnCerts($certsLocation, $options);
|
||||
foreach ($certs as $cert) {
|
||||
$rsa = new RSA();
|
||||
$rsa->loadKey([
|
||||
'n' => new BigInteger($this->callJwtStatic('urlsafeB64Decode', [
|
||||
$cert['n']
|
||||
]), 256),
|
||||
'e' => new BigInteger($this->callJwtStatic('urlsafeB64Decode', [
|
||||
$cert['e']
|
||||
]), 256)
|
||||
]);
|
||||
|
||||
try {
|
||||
$pubkey = $rsa->getPublicKey();
|
||||
$payload = $this->callJwtStatic('decode', [
|
||||
$token,
|
||||
$pubkey,
|
||||
['RS256']
|
||||
]);
|
||||
|
||||
if (property_exists($payload, 'aud')) {
|
||||
if ($audience && $payload->aud != $audience) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// support HTTP and HTTPS issuers
|
||||
// @see https://developers.google.com/identity/sign-in/web/backend-auth
|
||||
$issuers = [self::OAUTH2_ISSUER, self::OAUTH2_ISSUER_HTTPS];
|
||||
if (!isset($payload->iss) || !in_array($payload->iss, $issuers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (array) $payload;
|
||||
} catch (ExpiredException $e) {
|
||||
return false;
|
||||
} catch (\ExpiredException $e) {
|
||||
// (firebase/php-jwt 2)
|
||||
return false;
|
||||
} catch (SignatureInvalidException $e) {
|
||||
// continue
|
||||
} catch (\SignatureInvalidException $e) {
|
||||
// continue (firebase/php-jwt 2)
|
||||
} catch (\DomainException $e) {
|
||||
// continue
|
||||
$certs = $this->getCerts($certsLocation, $cacheKey, $options);
|
||||
$alg = $this->determineAlg($certs);
|
||||
if (!in_array($alg, ['RS256', 'ES256'])) {
|
||||
throw new InvalidArgumentException(
|
||||
'unrecognized "alg" in certs, expected ES256 or RS256');
|
||||
}
|
||||
try {
|
||||
if ($alg == 'RS256') {
|
||||
return $this->verifyRs256($token, $certs, $audience, $issuer);
|
||||
}
|
||||
return $this->verifyEs256($token, $certs, $audience, $issuer);
|
||||
} catch (ExpiredException $e) { // firebase/php-jwt 3+
|
||||
} catch (\ExpiredException $e) { // firebase/php-jwt 2
|
||||
} catch (SignatureInvalidException $e) { // firebase/php-jwt 3+
|
||||
} catch (\SignatureInvalidException $e) { // firebase/php-jwt 2
|
||||
} catch (InvalidTokenException $e) { // simplejwt
|
||||
} catch (DomainException $e) {
|
||||
} catch (InvalidArgumentException $e) {
|
||||
} catch (UnexpectedValueException $e) {
|
||||
}
|
||||
|
||||
if ($throwException) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies the expected algorithm to verify by looking at the "alg" key
|
||||
* of the provided certs.
|
||||
*
|
||||
* @param array $certs Certificate array according to the JWK spec (see
|
||||
* https://tools.ietf.org/html/rfc7517).
|
||||
* @return string The expected algorithm, such as "ES256" or "RS256".
|
||||
*/
|
||||
private function determineAlg(array $certs)
|
||||
{
|
||||
$alg = null;
|
||||
foreach ($certs as $cert) {
|
||||
if (empty($cert['alg'])) {
|
||||
throw new InvalidArgumentException(
|
||||
'certs expects "alg" to be set'
|
||||
);
|
||||
}
|
||||
$alg = $alg ?: $cert['alg'];
|
||||
|
||||
if ($alg != $cert['alg']) {
|
||||
throw new InvalidArgumentException(
|
||||
'More than one alg detected in certs'
|
||||
);
|
||||
}
|
||||
}
|
||||
return $alg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies an ES256-signed JWT.
|
||||
*
|
||||
* @param string $token The JSON Web Token to be verified.
|
||||
* @param array $certs Certificate array according to the JWK spec (see
|
||||
* https://tools.ietf.org/html/rfc7517).
|
||||
* @param string|null $audience If set, returns false if the provided
|
||||
* audience does not match the "aud" claim on
|
||||
* the JWT.
|
||||
* @param string|null $issuer If set, returns false if the provided
|
||||
* issuer does not match the "iss" claim on
|
||||
* the JWT.
|
||||
* @return array|bool the token payload, if successful, or false if not.
|
||||
*/
|
||||
private function verifyEs256($token, array $certs, $audience = null, $issuer = null)
|
||||
{
|
||||
$this->checkSimpleJwt();
|
||||
|
||||
$jwkset = new KeySet();
|
||||
foreach ($certs as $cert) {
|
||||
$jwkset->add(KeyFactory::create($cert, 'php'));
|
||||
}
|
||||
|
||||
// Validate the signature using the key set and ES256 algorithm.
|
||||
$jwt = $this->callSimpleJwtDecode([$token, $jwkset, 'ES256']);
|
||||
$payload = $jwt->getClaims();
|
||||
|
||||
if (isset($payload['aud'])) {
|
||||
if ($audience && $payload['aud'] != $audience) {
|
||||
throw new UnexpectedValueException('Audience does not match');
|
||||
}
|
||||
}
|
||||
|
||||
// @see https://cloud.google.com/iap/docs/signed-headers-howto#verifying_the_jwt_payload
|
||||
$issuer = $issuer ?: self::IAP_ISSUER;
|
||||
if (!isset($payload['iss']) || $payload['iss'] !== $issuer) {
|
||||
throw new UnexpectedValueException('Issuer does not match');
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies an RS256-signed JWT.
|
||||
*
|
||||
* @param string $token The JSON Web Token to be verified.
|
||||
* @param array $certs Certificate array according to the JWK spec (see
|
||||
* https://tools.ietf.org/html/rfc7517).
|
||||
* @param string|null $audience If set, returns false if the provided
|
||||
* audience does not match the "aud" claim on
|
||||
* the JWT.
|
||||
* @param string|null $issuer If set, returns false if the provided
|
||||
* issuer does not match the "iss" claim on
|
||||
* the JWT.
|
||||
* @return array|bool the token payload, if successful, or false if not.
|
||||
*/
|
||||
private function verifyRs256($token, array $certs, $audience = null, $issuer = null)
|
||||
{
|
||||
$this->checkAndInitializePhpsec();
|
||||
$keys = [];
|
||||
foreach ($certs as $cert) {
|
||||
if (empty($cert['kid'])) {
|
||||
throw new InvalidArgumentException(
|
||||
'certs expects "kid" to be set'
|
||||
);
|
||||
}
|
||||
if (empty($cert['n']) || empty($cert['e'])) {
|
||||
throw new InvalidArgumentException(
|
||||
'RSA certs expects "n" and "e" to be set'
|
||||
);
|
||||
}
|
||||
$rsa = new RSA();
|
||||
$rsa->loadKey([
|
||||
'n' => new BigInteger($this->callJwtStatic('urlsafeB64Decode', [
|
||||
$cert['n'],
|
||||
]), 256),
|
||||
'e' => new BigInteger($this->callJwtStatic('urlsafeB64Decode', [
|
||||
$cert['e']
|
||||
]), 256),
|
||||
]);
|
||||
|
||||
// create an array of key IDs to certs for the JWT library
|
||||
$keys[$cert['kid']] = $rsa->getPublicKey();
|
||||
}
|
||||
|
||||
$payload = $this->callJwtStatic('decode', [
|
||||
$token,
|
||||
$keys,
|
||||
['RS256']
|
||||
]);
|
||||
|
||||
if (property_exists($payload, 'aud')) {
|
||||
if ($audience && $payload->aud != $audience) {
|
||||
throw new UnexpectedValueException('Audience does not match');
|
||||
}
|
||||
}
|
||||
|
||||
// support HTTP and HTTPS issuers
|
||||
// @see https://developers.google.com/identity/sign-in/web/backend-auth
|
||||
$issuers = $issuer ? [$issuer] : [self::OAUTH2_ISSUER, self::OAUTH2_ISSUER_HTTPS];
|
||||
if (!isset($payload->iss) || !in_array($payload->iss, $issuers)) {
|
||||
throw new UnexpectedValueException('Issuer does not match');
|
||||
}
|
||||
|
||||
return (array) $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke an OAuth2 access token or refresh token. This method will revoke the current access
|
||||
* token, if a token isn't provided.
|
||||
*
|
||||
* @param string|array $token The token (access token or a refresh token) that should be revoked.
|
||||
* @param array $options [optional] Configuration options.
|
||||
* @return boolean Returns True if the revocation was successful, otherwise False.
|
||||
* @return bool Returns True if the revocation was successful, otherwise False.
|
||||
*/
|
||||
public function revoke($token, array $options = [])
|
||||
{
|
||||
@ -198,11 +327,11 @@ class AccessToken
|
||||
* @param string $location The location from which to retrieve certs.
|
||||
* @param array $options [optional] Configuration options.
|
||||
* @return array
|
||||
* @throws \InvalidArgumentException If received certs are in an invalid format.
|
||||
* @throws InvalidArgumentException If received certs are in an invalid format.
|
||||
*/
|
||||
private function getFederatedSignOnCerts($location, array $options = [])
|
||||
private function getCerts($location, $cacheKey, array $options = [])
|
||||
{
|
||||
$cacheItem = $this->cache->getItem('federated_signon_certs_v3');
|
||||
$cacheItem = $this->cache->getItem($cacheKey);
|
||||
$certs = $cacheItem ? $cacheItem->get() : null;
|
||||
|
||||
$gotNewCerts = false;
|
||||
@ -213,15 +342,20 @@ class AccessToken
|
||||
}
|
||||
|
||||
if (!isset($certs['keys'])) {
|
||||
throw new \InvalidArgumentException(
|
||||
'federated sign-on certs expects "keys" to be set'
|
||||
if ($location !== self::IAP_CERT_URL) {
|
||||
throw new InvalidArgumentException(
|
||||
'federated sign-on certs expects "keys" to be set'
|
||||
);
|
||||
}
|
||||
throw new InvalidArgumentException(
|
||||
'certs expects "keys" to be set'
|
||||
);
|
||||
}
|
||||
|
||||
// Push caching off until after verifying certs are in a valid format.
|
||||
// Don't want to cache bad data.
|
||||
if ($gotNewCerts) {
|
||||
$cacheItem->expiresAt(new \DateTime('+1 hour'));
|
||||
$cacheItem->expiresAt(new DateTime('+1 hour'));
|
||||
$cacheItem->set($certs);
|
||||
$this->cache->save($cacheItem);
|
||||
}
|
||||
@ -234,17 +368,16 @@ class AccessToken
|
||||
*
|
||||
* @param $url string location
|
||||
* @param array $options [optional] Configuration options.
|
||||
* @throws \RuntimeException
|
||||
* @return array certificates
|
||||
* @throws \InvalidArgumentException If certs could not be retrieved from a local file.
|
||||
* @throws \RuntimeException If certs could not be retrieved from a remote location.
|
||||
* @throws InvalidArgumentException If certs could not be retrieved from a local file.
|
||||
* @throws RuntimeException If certs could not be retrieved from a remote location.
|
||||
*/
|
||||
private function retrieveCertsFromLocation($url, array $options = [])
|
||||
{
|
||||
// If we're retrieving a local file, just grab it.
|
||||
if (strpos($url, 'http') !== 0) {
|
||||
if (!file_exists($url)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Failed to retrieve verification certificates from path: %s.',
|
||||
$url
|
||||
));
|
||||
@ -260,26 +393,30 @@ class AccessToken
|
||||
return json_decode((string) $response->getBody(), true);
|
||||
}
|
||||
|
||||
throw new \RuntimeException(sprintf(
|
||||
throw new RuntimeException(sprintf(
|
||||
'Failed to retrieve verification certificates: "%s".',
|
||||
$response->getBody()->getContents()
|
||||
), $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set required defaults for JWT.
|
||||
*/
|
||||
private function configureJwtService()
|
||||
private function checkAndInitializePhpsec()
|
||||
{
|
||||
$class = class_exists('Firebase\JWT\JWT')
|
||||
? 'Firebase\JWT\JWT'
|
||||
: '\JWT';
|
||||
|
||||
if (property_exists($class, 'leeway') && $class::$leeway < 1) {
|
||||
// Ensures JWT leeway is at least 1
|
||||
// @see https://github.com/google/google-api-php-client/issues/827
|
||||
$class::$leeway = 1;
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!class_exists('phpseclib\Crypt\RSA')) {
|
||||
throw new RuntimeException('Please require phpseclib/phpseclib v2 to use this utility.');
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
$this->setPhpsecConstants();
|
||||
}
|
||||
|
||||
private function checkSimpleJwt()
|
||||
{
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!class_exists('SimpleJWT\JWT')) {
|
||||
throw new RuntimeException('Please require kelvinmo/simplejwt ^0.2 to use this utility.');
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
@ -317,4 +454,31 @@ class AccessToken
|
||||
: 'JWT';
|
||||
return call_user_func_array([$class, $method], $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a hook to mock calls to the JWT static methods.
|
||||
*
|
||||
* @param array $args
|
||||
* @return mixed
|
||||
*/
|
||||
protected function callSimpleJwtDecode(array $args = [])
|
||||
{
|
||||
return call_user_func_array(['SimpleJWT\JWT', 'decode'], $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a cache key based on the cert location using sha1 with the
|
||||
* exception of using "federated_signon_certs_v3" to preserve BC.
|
||||
*
|
||||
* @param string $certsLocation
|
||||
* @return string
|
||||
*/
|
||||
private function getCacheKeyFromCertLocation($certsLocation)
|
||||
{
|
||||
$key = $certsLocation === self::FEDERATED_SIGNON_CERT_URL
|
||||
? 'federated_signon_certs_v3'
|
||||
: sha1($certsLocation);
|
||||
|
||||
return 'google_auth_certs_cache|' . $key;
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,13 @@ namespace Google\Auth;
|
||||
use DomainException;
|
||||
use Google\Auth\Credentials\AppIdentityCredentials;
|
||||
use Google\Auth\Credentials\GCECredentials;
|
||||
use Google\Auth\Credentials\ServiceAccountCredentials;
|
||||
use Google\Auth\HttpHandler\HttpClientCache;
|
||||
use Google\Auth\HttpHandler\HttpHandlerFactory;
|
||||
use Google\Auth\Middleware\AuthTokenMiddleware;
|
||||
use Google\Auth\Subscriber\AuthTokenSubscriber;
|
||||
use GuzzleHttp\Client;
|
||||
use InvalidArgumentException;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
|
||||
/**
|
||||
@ -121,8 +123,9 @@ class ApplicationDefaultCredentials
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the default FetchAuthTokenInterface implementation to use
|
||||
* in this environment.
|
||||
* Obtains an AuthTokenMiddleware which will fetch an access token to use in
|
||||
* the Authorization header. The middleware is configured with the default
|
||||
* FetchAuthTokenInterface implementation to use in this environment.
|
||||
*
|
||||
* If supplied, $scope is used to in creating the credentials instance if
|
||||
* this does not fallback to the Compute Engine defaults.
|
||||
@ -165,7 +168,97 @@ class ApplicationDefaultCredentials
|
||||
}
|
||||
|
||||
if (is_null($creds)) {
|
||||
throw new \DomainException(self::notFound());
|
||||
throw new DomainException(self::notFound());
|
||||
}
|
||||
if (!is_null($cache)) {
|
||||
$creds = new FetchAuthTokenCache($creds, $cacheConfig, $cache);
|
||||
}
|
||||
return $creds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains an AuthTokenMiddleware which will fetch an ID token to use in the
|
||||
* Authorization header. The middleware is configured with the default
|
||||
* FetchAuthTokenInterface implementation to use in this environment.
|
||||
*
|
||||
* If supplied, $targetAudience is used to set the "aud" on the resulting
|
||||
* ID token.
|
||||
*
|
||||
* @param string $targetAudience The audience for the ID token.
|
||||
* @param callable $httpHandler callback which delivers psr7 request
|
||||
* @param array $cacheConfig configuration for the cache when it's present
|
||||
* @param CacheItemPoolInterface $cache
|
||||
*
|
||||
* @return AuthTokenMiddleware
|
||||
*
|
||||
* @throws DomainException if no implementation can be obtained.
|
||||
*/
|
||||
public static function getIdTokenMiddleware(
|
||||
$targetAudience,
|
||||
callable $httpHandler = null,
|
||||
array $cacheConfig = null,
|
||||
CacheItemPoolInterface $cache = null
|
||||
|
||||
) {
|
||||
$creds = self::getIdTokenCredentials($targetAudience, $httpHandler, $cacheConfig, $cache);
|
||||
|
||||
return new AuthTokenMiddleware($creds, $httpHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the default FetchAuthTokenInterface implementation to use
|
||||
* in this environment, configured with a $targetAudience for fetching an ID
|
||||
* token.
|
||||
*
|
||||
* @param string $targetAudience The audience for the ID token.
|
||||
* @param callable $httpHandler callback which delivers psr7 request
|
||||
* @param array $cacheConfig configuration for the cache when it's present
|
||||
* @param CacheItemPoolInterface $cache
|
||||
*
|
||||
* @return CredentialsLoader
|
||||
*
|
||||
* @throws DomainException if no implementation can be obtained.
|
||||
* @throws InvalidArgumentException if JSON "type" key is invalid
|
||||
*/
|
||||
public static function getIdTokenCredentials(
|
||||
$targetAudience,
|
||||
callable $httpHandler = null,
|
||||
array $cacheConfig = null,
|
||||
CacheItemPoolInterface $cache = null
|
||||
) {
|
||||
$creds = null;
|
||||
$jsonKey = CredentialsLoader::fromEnv()
|
||||
?: CredentialsLoader::fromWellKnownFile();
|
||||
|
||||
if (!$httpHandler) {
|
||||
if (!($client = HttpClientCache::getHttpClient())) {
|
||||
$client = new Client();
|
||||
HttpClientCache::setHttpClient($client);
|
||||
}
|
||||
|
||||
$httpHandler = HttpHandlerFactory::build($client);
|
||||
}
|
||||
|
||||
if (!is_null($jsonKey)) {
|
||||
if (!array_key_exists('type', $jsonKey)) {
|
||||
throw new \InvalidArgumentException('json key is missing the type field');
|
||||
}
|
||||
|
||||
if ($jsonKey['type'] == 'authorized_user') {
|
||||
throw new InvalidArgumentException('ID tokens are not supported for end user credentials');
|
||||
}
|
||||
|
||||
if ($jsonKey['type'] != 'service_account') {
|
||||
throw new InvalidArgumentException('invalid value in the type field');
|
||||
}
|
||||
|
||||
$creds = new ServiceAccountCredentials(null, $jsonKey, null, $targetAudience);
|
||||
} elseif (GCECredentials::onGce($httpHandler)) {
|
||||
$creds = new GCECredentials(null, null, $targetAudience);
|
||||
}
|
||||
|
||||
if (is_null($creds)) {
|
||||
throw new DomainException(self::notFound());
|
||||
}
|
||||
if (!is_null($cache)) {
|
||||
$creds = new FetchAuthTokenCache($creds, $cacheConfig, $cache);
|
||||
|
@ -76,7 +76,7 @@ class SysVCacheItemPool implements CacheItemPoolInterface
|
||||
public function __construct($options = [])
|
||||
{
|
||||
if (! extension_loaded('sysvshm')) {
|
||||
throw \RuntimeException(
|
||||
throw new \RuntimeException(
|
||||
'sysvshm extension is required to use this ItemPool');
|
||||
}
|
||||
$this->options = $options + [
|
||||
|
@ -24,6 +24,7 @@ namespace Google\Auth\Credentials;
|
||||
*/
|
||||
use google\appengine\api\app_identity\AppIdentityService;
|
||||
use Google\Auth\CredentialsLoader;
|
||||
use Google\Auth\ProjectIdProviderInterface;
|
||||
use Google\Auth\SignBlobInterface;
|
||||
|
||||
/**
|
||||
@ -32,35 +33,42 @@ use Google\Auth\SignBlobInterface;
|
||||
* It can be used to authorize requests using the AuthTokenMiddleware or
|
||||
* AuthTokenSubscriber, but will only succeed if being run on App Engine:
|
||||
*
|
||||
* use Google\Auth\Credentials\AppIdentityCredentials;
|
||||
* use Google\Auth\Middleware\AuthTokenMiddleware;
|
||||
* use GuzzleHttp\Client;
|
||||
* use GuzzleHttp\HandlerStack;
|
||||
* 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);
|
||||
* $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'
|
||||
* ]);
|
||||
* $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');
|
||||
* $res = $client->get('volumes?q=Henry+David+Thoreau&country=US');
|
||||
* ```
|
||||
*/
|
||||
class AppIdentityCredentials extends CredentialsLoader implements SignBlobInterface
|
||||
class AppIdentityCredentials extends CredentialsLoader implements
|
||||
SignBlobInterface,
|
||||
ProjectIdProviderInterface
|
||||
{
|
||||
/**
|
||||
* Result of fetchAuthToken.
|
||||
*
|
||||
* @array
|
||||
* @var array
|
||||
*/
|
||||
protected $lastReceivedToken;
|
||||
|
||||
/**
|
||||
* Array of OAuth2 scopes to be requested.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $scope;
|
||||
|
||||
@ -69,6 +77,9 @@ class AppIdentityCredentials extends CredentialsLoader implements SignBlobInterf
|
||||
*/
|
||||
private $clientName;
|
||||
|
||||
/**
|
||||
* @param array $scope One or more scopes.
|
||||
*/
|
||||
public function __construct($scope = array())
|
||||
{
|
||||
$this->scope = $scope;
|
||||
@ -143,6 +154,25 @@ class AppIdentityCredentials extends CredentialsLoader implements SignBlobInterf
|
||||
return base64_encode(AppIdentityService::signForApp($stringToSign)['signature']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the project ID from AppIdentityService.
|
||||
*
|
||||
* Returns null if AppIdentityService is unavailable.
|
||||
*
|
||||
* @param callable $httpHandler Not used by this type.
|
||||
* @return string|null
|
||||
*/
|
||||
public function getProjectId(callable $httpHander = null)
|
||||
{
|
||||
try {
|
||||
$this->checkAppEngineContext();
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return AppIdentityService::getApplicationId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client name from AppIdentityService.
|
||||
*
|
||||
|
@ -21,11 +21,13 @@ use Google\Auth\CredentialsLoader;
|
||||
use Google\Auth\HttpHandler\HttpClientCache;
|
||||
use Google\Auth\HttpHandler\HttpHandlerFactory;
|
||||
use Google\Auth\Iam;
|
||||
use Google\Auth\ProjectIdProviderInterface;
|
||||
use Google\Auth\SignBlobInterface;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use GuzzleHttp\Exception\ServerException;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* GCECredentials supports authorization on Google Compute Engine.
|
||||
@ -51,7 +53,9 @@ use GuzzleHttp\Psr7\Request;
|
||||
*
|
||||
* $res = $client->get('myproject/taskqueues/myqueue');
|
||||
*/
|
||||
class GCECredentials extends CredentialsLoader implements SignBlobInterface
|
||||
class GCECredentials extends CredentialsLoader implements
|
||||
SignBlobInterface,
|
||||
ProjectIdProviderInterface
|
||||
{
|
||||
const cacheKey = 'GOOGLE_AUTH_PHP_GCE';
|
||||
|
||||
@ -68,11 +72,21 @@ class GCECredentials extends CredentialsLoader implements SignBlobInterface
|
||||
*/
|
||||
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 header whose presence indicates GCE presence.
|
||||
*/
|
||||
@ -111,10 +125,15 @@ class GCECredentials extends CredentialsLoader implements SignBlobInterface
|
||||
protected $lastReceivedToken;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
private $clientName;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $projectId;
|
||||
|
||||
/**
|
||||
* @var Iam|null
|
||||
*/
|
||||
@ -125,15 +144,26 @@ class GCECredentials extends CredentialsLoader implements SignBlobInterface
|
||||
*/
|
||||
private $tokenUri;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $targetAudience;
|
||||
|
||||
/**
|
||||
* @param Iam $iam [optional] An IAM instance.
|
||||
* @param string|array $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.
|
||||
*/
|
||||
public function __construct(Iam $iam = null, $scope = null)
|
||||
public function __construct(Iam $iam = null, $scope = null, $targetAudience = null)
|
||||
{
|
||||
$this->iam = $iam;
|
||||
|
||||
if ($scope && $targetAudience) {
|
||||
throw new InvalidArgumentException(
|
||||
'Scope and targetAudience cannot both be supplied');
|
||||
}
|
||||
|
||||
$tokenUri = self::getTokenUri();
|
||||
if ($scope) {
|
||||
if (is_string($scope)) {
|
||||
@ -143,6 +173,13 @@ class GCECredentials extends CredentialsLoader implements SignBlobInterface
|
||||
$scope = implode(',', $scope);
|
||||
|
||||
$tokenUri = $tokenUri . '?scopes='. $scope;
|
||||
} elseif ($targetAudience) {
|
||||
$tokenUri = sprintf('http://%s/computeMetadata/%s?audience=%s',
|
||||
self::METADATA_IP,
|
||||
self::ID_TOKEN_URI_PATH,
|
||||
$targetAudience
|
||||
);
|
||||
$this->targetAudience = $targetAudience;
|
||||
}
|
||||
|
||||
$this->tokenUri = $tokenUri;
|
||||
@ -172,6 +209,18 @@ class GCECredentials extends CredentialsLoader implements SignBlobInterface
|
||||
return $base . self::CLIENT_ID_URI_PATH;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this an App Engine Flexible instance, by accessing the
|
||||
* GAE_INSTANCE environment variable.
|
||||
@ -189,8 +238,7 @@ class GCECredentials extends CredentialsLoader implements SignBlobInterface
|
||||
* If $httpHandler is not specified a the default HttpHandler is used.
|
||||
*
|
||||
* @param callable $httpHandler callback which delivers psr7 request
|
||||
*
|
||||
* @return true if this a GCEInstance false otherwise
|
||||
* @return bool True if this a GCEInstance, false otherwise
|
||||
*/
|
||||
public static function onGce(callable $httpHandler = null)
|
||||
{
|
||||
@ -234,11 +282,14 @@ class GCECredentials extends CredentialsLoader implements SignBlobInterface
|
||||
*
|
||||
* @param callable $httpHandler callback which delivers psr7 request
|
||||
*
|
||||
* @return array A set of auth related metadata, containing the following
|
||||
* keys:
|
||||
* @return array A set of auth related metadata, based on the token type.
|
||||
*
|
||||
* Access tokens have the following keys:
|
||||
* - access_token (string)
|
||||
* - expires_in (int)
|
||||
* - token_type (string)
|
||||
* ID tokens have the following keys:
|
||||
* - id_token (string)
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
@ -255,8 +306,13 @@ class GCECredentials extends CredentialsLoader implements SignBlobInterface
|
||||
return array(); // return an empty array with no access token
|
||||
}
|
||||
|
||||
$json = $this->getFromMetadata($httpHandler, $this->tokenUri);
|
||||
if (null === $json = json_decode($json, true)) {
|
||||
$response = $this->getFromMetadata($httpHandler, $this->tokenUri);
|
||||
|
||||
if ($this->targetAudience) {
|
||||
return ['id_token' => $response];
|
||||
}
|
||||
|
||||
if (null === $json = json_decode($response, true)) {
|
||||
throw new \Exception('Invalid JSON response');
|
||||
}
|
||||
|
||||
@ -351,6 +407,36 @@ class GCECredentials extends CredentialsLoader implements SignBlobInterface
|
||||
return $signer->signBlob($email, $accessToken, $stringToSign);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the default Project ID from compute engine.
|
||||
*
|
||||
* Returns null if called outside GCE.
|
||||
*
|
||||
* @param callable $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 value of a GCE metadata server URI.
|
||||
*
|
||||
|
@ -18,9 +18,12 @@
|
||||
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;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* ServiceAccountCredentials supports authorization using a Google service
|
||||
@ -55,7 +58,10 @@ use Google\Auth\SignBlobInterface;
|
||||
*
|
||||
* $res = $client->get('myproject/taskqueues/myqueue');
|
||||
*/
|
||||
class ServiceAccountCredentials extends CredentialsLoader implements SignBlobInterface
|
||||
class ServiceAccountCredentials extends CredentialsLoader implements
|
||||
GetQuotaProjectInterface,
|
||||
SignBlobInterface,
|
||||
ProjectIdProviderInterface
|
||||
{
|
||||
use ServiceAccountSignerTrait;
|
||||
|
||||
@ -66,6 +72,18 @@ class ServiceAccountCredentials extends CredentialsLoader implements SignBlobInt
|
||||
*/
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* The quota project associated with the JSON credentials
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $quotaProject;
|
||||
|
||||
/*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $projectId;
|
||||
|
||||
/**
|
||||
* Create a new ServiceAccountCredentials.
|
||||
*
|
||||
@ -75,11 +93,13 @@ class ServiceAccountCredentials extends CredentialsLoader implements SignBlobInt
|
||||
* 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
|
||||
$sub = null,
|
||||
$targetAudience = null
|
||||
) {
|
||||
if (is_string($jsonKey)) {
|
||||
if (!file_exists($jsonKey)) {
|
||||
@ -98,6 +118,17 @@ class ServiceAccountCredentials extends CredentialsLoader implements SignBlobInt
|
||||
throw new \InvalidArgumentException(
|
||||
'json key is missing the private_key field');
|
||||
}
|
||||
if (array_key_exists('quota_project', $jsonKey)) {
|
||||
$this->quotaProject = (string) $jsonKey['quota_project'];
|
||||
}
|
||||
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'],
|
||||
@ -106,7 +137,12 @@ class ServiceAccountCredentials extends CredentialsLoader implements SignBlobInt
|
||||
'signingKey' => $jsonKey['private_key'],
|
||||
'sub' => $sub,
|
||||
'tokenCredentialUri' => self::TOKEN_CREDENTIAL_URI,
|
||||
'additionalClaims' => $additionalClaims,
|
||||
]);
|
||||
|
||||
$this->projectId = isset($jsonKey['project_id'])
|
||||
? $jsonKey['project_id']
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -144,6 +180,19 @@ class ServiceAccountCredentials extends CredentialsLoader implements SignBlobInt
|
||||
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 $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.
|
||||
*
|
||||
@ -195,4 +244,14 @@ class ServiceAccountCredentials extends CredentialsLoader implements SignBlobInt
|
||||
{
|
||||
return $this->auth->getIssuer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the quota project used for this API request
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getQuotaProject()
|
||||
{
|
||||
return $this->quotaProject;
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,9 @@
|
||||
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;
|
||||
|
||||
@ -31,7 +33,10 @@ use Google\Auth\SignBlobInterface;
|
||||
* 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 SignBlobInterface
|
||||
class ServiceAccountJwtAccessCredentials extends CredentialsLoader implements
|
||||
GetQuotaProjectInterface,
|
||||
SignBlobInterface,
|
||||
ProjectIdProviderInterface
|
||||
{
|
||||
use ServiceAccountSignerTrait;
|
||||
|
||||
@ -42,6 +47,11 @@ class ServiceAccountJwtAccessCredentials extends CredentialsLoader implements Si
|
||||
*/
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* The quota project associated with the JSON credentials
|
||||
*/
|
||||
protected $quotaProject;
|
||||
|
||||
/**
|
||||
* Create a new ServiceAccountJwtAccessCredentials.
|
||||
*
|
||||
@ -67,12 +77,19 @@ class ServiceAccountJwtAccessCredentials extends CredentialsLoader implements Si
|
||||
throw new \InvalidArgumentException(
|
||||
'json key is missing the private_key field');
|
||||
}
|
||||
if (array_key_exists('quota_project', $jsonKey)) {
|
||||
$this->quotaProject = (string) $jsonKey['quota_project'];
|
||||
}
|
||||
$this->auth = new OAuth2([
|
||||
'issuer' => $jsonKey['client_email'],
|
||||
'sub' => $jsonKey['client_email'],
|
||||
'signingAlgorithm' => 'RS256',
|
||||
'signingKey' => $jsonKey['private_key'],
|
||||
]);
|
||||
|
||||
$this->projectId = isset($jsonKey['project_id'])
|
||||
? $jsonKey['project_id']
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -135,6 +152,19 @@ class ServiceAccountJwtAccessCredentials extends CredentialsLoader implements Si
|
||||
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 $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.
|
||||
*
|
||||
@ -147,4 +177,14 @@ class ServiceAccountJwtAccessCredentials extends CredentialsLoader implements Si
|
||||
{
|
||||
return $this->auth->getIssuer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the quota project used for this API request
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getQuotaProject()
|
||||
{
|
||||
return $this->quotaProject;
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
namespace Google\Auth\Credentials;
|
||||
|
||||
use Google\Auth\CredentialsLoader;
|
||||
use Google\Auth\GetQuotaProjectInterface;
|
||||
use Google\Auth\OAuth2;
|
||||
|
||||
/**
|
||||
@ -31,7 +32,7 @@ use Google\Auth\OAuth2;
|
||||
*
|
||||
* @see [Application Default Credentials](http://goo.gl/mkAHpZ)
|
||||
*/
|
||||
class UserRefreshCredentials extends CredentialsLoader
|
||||
class UserRefreshCredentials extends CredentialsLoader implements GetQuotaProjectInterface
|
||||
{
|
||||
const CLOUD_SDK_CLIENT_ID =
|
||||
'764086051850-6qr4p6gpi6hn506pt8ejuq83di341hur.apps.googleusercontent.com';
|
||||
@ -45,6 +46,11 @@ class UserRefreshCredentials extends CredentialsLoader
|
||||
*/
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* The quota project associated with the JSON credentials
|
||||
*/
|
||||
protected $quotaProject;
|
||||
|
||||
/**
|
||||
* Create a new UserRefreshCredentials.
|
||||
*
|
||||
@ -85,7 +91,11 @@ class UserRefreshCredentials extends CredentialsLoader
|
||||
'scope' => $scope,
|
||||
'tokenCredentialUri' => self::TOKEN_CREDENTIAL_URI,
|
||||
]);
|
||||
if (array_key_exists('quota_project', $jsonKey)) {
|
||||
$this->quotaProject = (string) $jsonKey['quota_project'];
|
||||
}
|
||||
if ($jsonKey['client_id'] === self::CLOUD_SDK_CLIENT_ID
|
||||
&& is_null($this->quotaProject)
|
||||
&& getenv(self::SUPPRESS_CLOUD_SDK_CREDS_WARNING_ENV) !== 'true') {
|
||||
trigger_error(
|
||||
'Your application has authenticated using end user credentials '
|
||||
@ -134,4 +144,14 @@ class UserRefreshCredentials extends CredentialsLoader
|
||||
{
|
||||
return $this->auth->getLastReceivedToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the quota project used for this API request
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getQuotaProject()
|
||||
{
|
||||
return $this->quotaProject;
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,11 @@ use Psr\Cache\CacheItemPoolInterface;
|
||||
* A class to implement caching for any object implementing
|
||||
* FetchAuthTokenInterface
|
||||
*/
|
||||
class FetchAuthTokenCache implements FetchAuthTokenInterface, SignBlobInterface
|
||||
class FetchAuthTokenCache implements
|
||||
FetchAuthTokenInterface,
|
||||
GetQuotaProjectInterface,
|
||||
SignBlobInterface,
|
||||
ProjectIdProviderInterface
|
||||
{
|
||||
use CacheTrait;
|
||||
|
||||
@ -139,4 +143,37 @@ class FetchAuthTokenCache implements FetchAuthTokenInterface, SignBlobInterface
|
||||
|
||||
return $this->fetcher->signBlob($stringToSign, $forceOpenSsl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the quota project used for this API request from the credentials
|
||||
* fetcher.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getQuotaProject()
|
||||
{
|
||||
if ($this->fetcher instanceof GetQuotaProjectInterface) {
|
||||
return $this->fetcher->getQuotaProject();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the Project ID from the fetcher.
|
||||
*
|
||||
* @param callable $httpHandler Callback which delivers psr7 request
|
||||
* @return string|null
|
||||
* @throws \RuntimeException If the fetcher does not implement
|
||||
* `Google\Auth\ProvidesProjectIdInterface`.
|
||||
*/
|
||||
public function getProjectId(callable $httpHandler = null)
|
||||
{
|
||||
if (!$this->fetcher instanceof ProjectIdProviderInterface) {
|
||||
throw new \RuntimeException(
|
||||
'Credentials fetcher does not implement ' .
|
||||
'Google\Auth\ProvidesProjectIdInterface'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->fetcher->getProjectId($httpHandler);
|
||||
}
|
||||
}
|
||||
|
33
wp-content/plugins/wp-mail-smtp/vendor/google/auth/src/GetQuotaProjectInterface.php
vendored
Normal file
33
wp-content/plugins/wp-mail-smtp/vendor/google/auth/src/GetQuotaProjectInterface.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright 2019 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.
|
||||
*/
|
||||
|
||||
namespace Google\Auth;
|
||||
|
||||
/**
|
||||
* An interface implemented by objects that can get quota projects.
|
||||
*/
|
||||
interface GetQuotaProjectInterface
|
||||
{
|
||||
const X_GOOG_USER_PROJECT_HEADER = 'X-Goog-User-Project';
|
||||
|
||||
/**
|
||||
* Get the quota project used for this API request
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getQuotaProject();
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
namespace Google\Auth\Middleware;
|
||||
|
||||
use Google\Auth\FetchAuthTokenInterface;
|
||||
use Google\Auth\GetQuotaProjectInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
@ -101,6 +102,13 @@ class AuthTokenMiddleware
|
||||
|
||||
$request = $request->withHeader('authorization', 'Bearer ' . $this->fetchToken());
|
||||
|
||||
if ($quotaProject = $this->getQuotaProject()) {
|
||||
$request = $request->withHeader(
|
||||
GetQuotaProjectInterface::X_GOOG_USER_PROJECT_HEADER,
|
||||
$quotaProject
|
||||
);
|
||||
}
|
||||
|
||||
return $handler($request, $options);
|
||||
};
|
||||
}
|
||||
@ -122,5 +130,16 @@ class AuthTokenMiddleware
|
||||
|
||||
return $auth_tokens['access_token'];
|
||||
}
|
||||
|
||||
if (array_key_exists('id_token', $auth_tokens)) {
|
||||
return $auth_tokens['id_token'];
|
||||
}
|
||||
}
|
||||
|
||||
private function getQuotaProject()
|
||||
{
|
||||
if ($this->fetcher instanceof GetQuotaProjectInterface) {
|
||||
return $this->fetcher->getQuotaProject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
32
wp-content/plugins/wp-mail-smtp/vendor/google/auth/src/ProjectIdProviderInterface.php
vendored
Normal file
32
wp-content/plugins/wp-mail-smtp/vendor/google/auth/src/ProjectIdProviderInterface.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace Google\Auth;
|
||||
|
||||
/**
|
||||
* Describes a Credentials object which supports fetching the project ID.
|
||||
*/
|
||||
interface ProjectIdProviderInterface
|
||||
{
|
||||
/**
|
||||
* Get the project ID.
|
||||
*
|
||||
* @param callable $httpHandler Callback which delivers psr7 request
|
||||
* @return string|null
|
||||
*/
|
||||
public function getProjectId(callable $httpHandler = null);
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
namespace Google\Auth\Subscriber;
|
||||
|
||||
use Google\Auth\FetchAuthTokenInterface;
|
||||
use Google\Auth\GetQuotaProjectInterface;
|
||||
use GuzzleHttp\Event\BeforeEvent;
|
||||
use GuzzleHttp\Event\RequestEvents;
|
||||
use GuzzleHttp\Event\SubscriberInterface;
|
||||
@ -114,5 +115,19 @@ class AuthTokenSubscriber implements SubscriberInterface
|
||||
call_user_func($this->tokenCallback, $this->fetcher->getCacheKey(), $auth_tokens['access_token']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($quotaProject = $this->getQuotaProject()) {
|
||||
$request->setHeader(
|
||||
GetQuotaProjectInterface::X_GOOG_USER_PROJECT_HEADER,
|
||||
$quotaProject
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function getQuotaProject()
|
||||
{
|
||||
if ($this->fetcher instanceof GetQuotaProjectInterface) {
|
||||
return $this->fetcher->getQuotaProject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user