PluginProbe ʕ •ᴥ•ʔ
WP Mail SMTP by WPForms – The Most Popular SMTP and Email Log Plugin / 3.4.0
WP Mail SMTP by WPForms – The Most Popular SMTP and Email Log Plugin v3.4.0
0.9.6 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.2 1.7.0 1.7.1 1.8.0 1.8.1 1.9.0 2.0.0 2.0.1 2.1.1 2.2.1 2.3.1 2.4.0 2.5.0 2.5.1 2.6.0 2.7.0 2.8.0 2.9.0 3.0.1 3.0.2 3.0.3 3.1.0 3.10.0 3.11.0 3.11.1 3.2.0 3.2.1 3.3.0 3.4.0 3.5.0 3.5.1 3.5.2 3.6.1 3.7.0 3.8.0 3.8.2 3.9.0 4.0.1 4.1.0 4.1.1 4.2.0 4.3.0 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.8.0 trunk 0.10.0 0.10.1 0.11.1 0.11.2 0.3.1 0.3.2 0.4 0.4.1 0.4.2 0.5.0 0.5.1 0.5.2 0.6 0.7 0.8 0.8.2 0.8.3 0.8.4 0.8.5 0.8.6 0.8.7 0.9.0 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5
wp-mail-smtp / vendor_prefixed / google / auth / src / AccessToken.php
wp-mail-smtp / vendor_prefixed / google / auth / src Last commit date
Cache 4 years ago Credentials 4 years ago HttpHandler 4 years ago Middleware 4 years ago AccessToken.php 4 years ago ApplicationDefaultCredentials.php 4 years ago CacheTrait.php 4 years ago CredentialsLoader.php 4 years ago FetchAuthTokenCache.php 4 years ago FetchAuthTokenInterface.php 4 years ago GCECache.php 4 years ago GetQuotaProjectInterface.php 4 years ago Iam.php 4 years ago OAuth2.php 4 years ago ProjectIdProviderInterface.php 4 years ago ServiceAccountSignerTrait.php 4 years ago SignBlobInterface.php 4 years ago UpdateMetadataInterface.php 4 years ago
AccessToken.php
389 lines
1 <?php
2
3 /*
4 * Copyright 2019 Google LLC
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18 namespace WPMailSMTP\Vendor\Google\Auth;
19
20 use DateTime;
21 use Exception;
22 use WPMailSMTP\Vendor\Firebase\JWT\ExpiredException;
23 use WPMailSMTP\Vendor\Firebase\JWT\JWT;
24 use WPMailSMTP\Vendor\Firebase\JWT\SignatureInvalidException;
25 use WPMailSMTP\Vendor\Google\Auth\Cache\MemoryCacheItemPool;
26 use WPMailSMTP\Vendor\Google\Auth\HttpHandler\HttpClientCache;
27 use WPMailSMTP\Vendor\Google\Auth\HttpHandler\HttpHandlerFactory;
28 use WPMailSMTP\Vendor\GuzzleHttp\Psr7\Request;
29 use WPMailSMTP\Vendor\GuzzleHttp\Psr7\Utils;
30 use InvalidArgumentException;
31 use WPMailSMTP\Vendor\phpseclib\Crypt\RSA;
32 use WPMailSMTP\Vendor\phpseclib\Math\BigInteger;
33 use WPMailSMTP\Vendor\Psr\Cache\CacheItemPoolInterface;
34 use RuntimeException;
35 use WPMailSMTP\Vendor\SimpleJWT\InvalidTokenException;
36 use WPMailSMTP\Vendor\SimpleJWT\JWT as SimpleJWT;
37 use WPMailSMTP\Vendor\SimpleJWT\Keys\KeyFactory;
38 use WPMailSMTP\Vendor\SimpleJWT\Keys\KeySet;
39 use UnexpectedValueException;
40 /**
41 * Wrapper around Google Access Tokens which provides convenience functions.
42 *
43 * @experimental
44 */
45 class AccessToken
46 {
47 const FEDERATED_SIGNON_CERT_URL = 'https://www.googleapis.com/oauth2/v3/certs';
48 const IAP_CERT_URL = 'https://www.gstatic.com/iap/verify/public_key-jwk';
49 const IAP_ISSUER = 'https://cloud.google.com/iap';
50 const OAUTH2_ISSUER = 'accounts.google.com';
51 const OAUTH2_ISSUER_HTTPS = 'https://accounts.google.com';
52 const OAUTH2_REVOKE_URI = 'https://oauth2.googleapis.com/revoke';
53 /**
54 * @var callable
55 */
56 private $httpHandler;
57 /**
58 * @var CacheItemPoolInterface
59 */
60 private $cache;
61 /**
62 * @param callable $httpHandler [optional] An HTTP Handler to deliver PSR-7 requests.
63 * @param CacheItemPoolInterface $cache [optional] A PSR-6 compatible cache implementation.
64 */
65 public function __construct(callable $httpHandler = null, \WPMailSMTP\Vendor\Psr\Cache\CacheItemPoolInterface $cache = null)
66 {
67 $this->httpHandler = $httpHandler ?: \WPMailSMTP\Vendor\Google\Auth\HttpHandler\HttpHandlerFactory::build(\WPMailSMTP\Vendor\Google\Auth\HttpHandler\HttpClientCache::getHttpClient());
68 $this->cache = $cache ?: new \WPMailSMTP\Vendor\Google\Auth\Cache\MemoryCacheItemPool();
69 }
70 /**
71 * Verifies an id token and returns the authenticated apiLoginTicket.
72 * Throws an exception if the id token is not valid.
73 * The audience parameter can be used to control which id tokens are
74 * accepted. By default, the id token must have been issued to this OAuth2 client.
75 *
76 * @param string $token The JSON Web Token to be verified.
77 * @param array $options [optional] Configuration options.
78 * @param string $options.audience The indended recipient of the token.
79 * @param string $options.issuer The intended issuer of the token.
80 * @param string $options.cacheKey The cache key of the cached certs. Defaults to
81 * the sha1 of $certsLocation if provided, otherwise is set to
82 * "federated_signon_certs_v3".
83 * @param string $options.certsLocation The location (remote or local) from which
84 * to retrieve certificates, if not cached. This value should only be
85 * provided in limited circumstances in which you are sure of the
86 * behavior.
87 * @param bool $options.throwException Whether the function should throw an
88 * exception if the verification fails. This is useful for
89 * determining the reason verification failed.
90 * @return array|bool the token payload, if successful, or false if not.
91 * @throws InvalidArgumentException If certs could not be retrieved from a local file.
92 * @throws InvalidArgumentException If received certs are in an invalid format.
93 * @throws InvalidArgumentException If the cert alg is not supported.
94 * @throws RuntimeException If certs could not be retrieved from a remote location.
95 * @throws UnexpectedValueException If the token issuer does not match.
96 * @throws UnexpectedValueException If the token audience does not match.
97 */
98 public function verify($token, array $options = [])
99 {
100 $audience = isset($options['audience']) ? $options['audience'] : null;
101 $issuer = isset($options['issuer']) ? $options['issuer'] : null;
102 $certsLocation = isset($options['certsLocation']) ? $options['certsLocation'] : self::FEDERATED_SIGNON_CERT_URL;
103 $cacheKey = isset($options['cacheKey']) ? $options['cacheKey'] : $this->getCacheKeyFromCertLocation($certsLocation);
104 $throwException = isset($options['throwException']) ? $options['throwException'] : \false;
105 // for backwards compatibility
106 // Check signature against each available cert.
107 $certs = $this->getCerts($certsLocation, $cacheKey, $options);
108 $alg = $this->determineAlg($certs);
109 if (!\in_array($alg, ['RS256', 'ES256'])) {
110 throw new \InvalidArgumentException('unrecognized "alg" in certs, expected ES256 or RS256');
111 }
112 try {
113 if ($alg == 'RS256') {
114 return $this->verifyRs256($token, $certs, $audience, $issuer);
115 }
116 return $this->verifyEs256($token, $certs, $audience, $issuer);
117 } catch (\WPMailSMTP\Vendor\Firebase\JWT\ExpiredException $e) {
118 // firebase/php-jwt 3+
119 } catch (\WPMailSMTP\Vendor\ExpiredException $e) {
120 // firebase/php-jwt 2
121 } catch (\WPMailSMTP\Vendor\Firebase\JWT\SignatureInvalidException $e) {
122 // firebase/php-jwt 3+
123 } catch (\WPMailSMTP\Vendor\SignatureInvalidException $e) {
124 // firebase/php-jwt 2
125 } catch (\WPMailSMTP\Vendor\SimpleJWT\InvalidTokenException $e) {
126 // simplejwt
127 } catch (\WPMailSMTP\Vendor\Google\Auth\DomainException $e) {
128 } catch (\InvalidArgumentException $e) {
129 } catch (\UnexpectedValueException $e) {
130 }
131 if ($throwException) {
132 throw $e;
133 }
134 return \false;
135 }
136 /**
137 * Identifies the expected algorithm to verify by looking at the "alg" key
138 * of the provided certs.
139 *
140 * @param array $certs Certificate array according to the JWK spec (see
141 * https://tools.ietf.org/html/rfc7517).
142 * @return string The expected algorithm, such as "ES256" or "RS256".
143 */
144 private function determineAlg(array $certs)
145 {
146 $alg = null;
147 foreach ($certs as $cert) {
148 if (empty($cert['alg'])) {
149 throw new \InvalidArgumentException('certs expects "alg" to be set');
150 }
151 $alg = $alg ?: $cert['alg'];
152 if ($alg != $cert['alg']) {
153 throw new \InvalidArgumentException('More than one alg detected in certs');
154 }
155 }
156 return $alg;
157 }
158 /**
159 * Verifies an ES256-signed JWT.
160 *
161 * @param string $token The JSON Web Token to be verified.
162 * @param array $certs Certificate array according to the JWK spec (see
163 * https://tools.ietf.org/html/rfc7517).
164 * @param string|null $audience If set, returns false if the provided
165 * audience does not match the "aud" claim on the JWT.
166 * @param string|null $issuer If set, returns false if the provided
167 * issuer does not match the "iss" claim on the JWT.
168 * @return array|bool the token payload, if successful, or false if not.
169 */
170 private function verifyEs256($token, array $certs, $audience = null, $issuer = null)
171 {
172 $this->checkSimpleJwt();
173 $jwkset = new \WPMailSMTP\Vendor\SimpleJWT\Keys\KeySet();
174 foreach ($certs as $cert) {
175 $jwkset->add(\WPMailSMTP\Vendor\SimpleJWT\Keys\KeyFactory::create($cert, 'php'));
176 }
177 // Validate the signature using the key set and ES256 algorithm.
178 $jwt = $this->callSimpleJwtDecode([$token, $jwkset, 'ES256']);
179 $payload = $jwt->getClaims();
180 if (isset($payload['aud'])) {
181 if ($audience && $payload['aud'] != $audience) {
182 throw new \UnexpectedValueException('Audience does not match');
183 }
184 }
185 // @see https://cloud.google.com/iap/docs/signed-headers-howto#verifying_the_jwt_payload
186 $issuer = $issuer ?: self::IAP_ISSUER;
187 if (!isset($payload['iss']) || $payload['iss'] !== $issuer) {
188 throw new \UnexpectedValueException('Issuer does not match');
189 }
190 return $payload;
191 }
192 /**
193 * Verifies an RS256-signed JWT.
194 *
195 * @param string $token The JSON Web Token to be verified.
196 * @param array $certs Certificate array according to the JWK spec (see
197 * https://tools.ietf.org/html/rfc7517).
198 * @param string|null $audience If set, returns false if the provided
199 * audience does not match the "aud" claim on the JWT.
200 * @param string|null $issuer If set, returns false if the provided
201 * issuer does not match the "iss" claim on the JWT.
202 * @return array|bool the token payload, if successful, or false if not.
203 */
204 private function verifyRs256($token, array $certs, $audience = null, $issuer = null)
205 {
206 $this->checkAndInitializePhpsec();
207 $keys = [];
208 foreach ($certs as $cert) {
209 if (empty($cert['kid'])) {
210 throw new \InvalidArgumentException('certs expects "kid" to be set');
211 }
212 if (empty($cert['n']) || empty($cert['e'])) {
213 throw new \InvalidArgumentException('RSA certs expects "n" and "e" to be set');
214 }
215 $rsa = new \WPMailSMTP\Vendor\phpseclib\Crypt\RSA();
216 $rsa->loadKey(['n' => new \WPMailSMTP\Vendor\phpseclib\Math\BigInteger($this->callJwtStatic('urlsafeB64Decode', [$cert['n']]), 256), 'e' => new \WPMailSMTP\Vendor\phpseclib\Math\BigInteger($this->callJwtStatic('urlsafeB64Decode', [$cert['e']]), 256)]);
217 // create an array of key IDs to certs for the JWT library
218 $keys[$cert['kid']] = $rsa->getPublicKey();
219 }
220 $payload = $this->callJwtStatic('decode', [$token, $keys, ['RS256']]);
221 if (\property_exists($payload, 'aud')) {
222 if ($audience && $payload->aud != $audience) {
223 throw new \UnexpectedValueException('Audience does not match');
224 }
225 }
226 // support HTTP and HTTPS issuers
227 // @see https://developers.google.com/identity/sign-in/web/backend-auth
228 $issuers = $issuer ? [$issuer] : [self::OAUTH2_ISSUER, self::OAUTH2_ISSUER_HTTPS];
229 if (!isset($payload->iss) || !\in_array($payload->iss, $issuers)) {
230 throw new \UnexpectedValueException('Issuer does not match');
231 }
232 return (array) $payload;
233 }
234 /**
235 * Revoke an OAuth2 access token or refresh token. This method will revoke the current access
236 * token, if a token isn't provided.
237 *
238 * @param string|array $token The token (access token or a refresh token) that should be revoked.
239 * @param array $options [optional] Configuration options.
240 * @return bool Returns True if the revocation was successful, otherwise False.
241 */
242 public function revoke($token, array $options = [])
243 {
244 if (\is_array($token)) {
245 if (isset($token['refresh_token'])) {
246 $token = $token['refresh_token'];
247 } else {
248 $token = $token['access_token'];
249 }
250 }
251 $body = \WPMailSMTP\Vendor\GuzzleHttp\Psr7\Utils::streamFor(\http_build_query(['token' => $token]));
252 $request = new \WPMailSMTP\Vendor\GuzzleHttp\Psr7\Request('POST', self::OAUTH2_REVOKE_URI, ['Cache-Control' => 'no-store', 'Content-Type' => 'application/x-www-form-urlencoded'], $body);
253 $httpHandler = $this->httpHandler;
254 $response = $httpHandler($request, $options);
255 return $response->getStatusCode() == 200;
256 }
257 /**
258 * Gets federated sign-on certificates to use for verifying identity tokens.
259 * Returns certs as array structure, where keys are key ids, and values
260 * are PEM encoded certificates.
261 *
262 * @param string $location The location from which to retrieve certs.
263 * @param string $cacheKey The key under which to cache the retrieved certs.
264 * @param array $options [optional] Configuration options.
265 * @return array
266 * @throws InvalidArgumentException If received certs are in an invalid format.
267 */
268 private function getCerts($location, $cacheKey, array $options = [])
269 {
270 $cacheItem = $this->cache->getItem($cacheKey);
271 $certs = $cacheItem ? $cacheItem->get() : null;
272 $gotNewCerts = \false;
273 if (!$certs) {
274 $certs = $this->retrieveCertsFromLocation($location, $options);
275 $gotNewCerts = \true;
276 }
277 if (!isset($certs['keys'])) {
278 if ($location !== self::IAP_CERT_URL) {
279 throw new \InvalidArgumentException('federated sign-on certs expects "keys" to be set');
280 }
281 throw new \InvalidArgumentException('certs expects "keys" to be set');
282 }
283 // Push caching off until after verifying certs are in a valid format.
284 // Don't want to cache bad data.
285 if ($gotNewCerts) {
286 $cacheItem->expiresAt(new \DateTime('+1 hour'));
287 $cacheItem->set($certs);
288 $this->cache->save($cacheItem);
289 }
290 return $certs['keys'];
291 }
292 /**
293 * Retrieve and cache a certificates file.
294 *
295 * @param $url string location
296 * @param array $options [optional] Configuration options.
297 * @return array certificates
298 * @throws InvalidArgumentException If certs could not be retrieved from a local file.
299 * @throws RuntimeException If certs could not be retrieved from a remote location.
300 */
301 private function retrieveCertsFromLocation($url, array $options = [])
302 {
303 // If we're retrieving a local file, just grab it.
304 if (\strpos($url, 'http') !== 0) {
305 if (!\file_exists($url)) {
306 throw new \InvalidArgumentException(\sprintf('Failed to retrieve verification certificates from path: %s.', $url));
307 }
308 return \json_decode(\file_get_contents($url), \true);
309 }
310 $httpHandler = $this->httpHandler;
311 $response = $httpHandler(new \WPMailSMTP\Vendor\GuzzleHttp\Psr7\Request('GET', $url), $options);
312 if ($response->getStatusCode() == 200) {
313 return \json_decode((string) $response->getBody(), \true);
314 }
315 throw new \RuntimeException(\sprintf('Failed to retrieve verification certificates: "%s".', $response->getBody()->getContents()), $response->getStatusCode());
316 }
317 private function checkAndInitializePhpsec()
318 {
319 // @codeCoverageIgnoreStart
320 if (!\class_exists('WPMailSMTP\\Vendor\\phpseclib\\Crypt\\RSA')) {
321 throw new \RuntimeException('Please require phpseclib/phpseclib v2 to use this utility.');
322 }
323 // @codeCoverageIgnoreEnd
324 $this->setPhpsecConstants();
325 }
326 private function checkSimpleJwt()
327 {
328 // @codeCoverageIgnoreStart
329 if (!\class_exists('WPMailSMTP\\Vendor\\SimpleJWT\\JWT')) {
330 throw new \RuntimeException('Please require kelvinmo/simplejwt ^0.2 to use this utility.');
331 }
332 // @codeCoverageIgnoreEnd
333 }
334 /**
335 * phpseclib calls "phpinfo" by default, which requires special
336 * whitelisting in the AppEngine VM environment. This function
337 * sets constants to bypass the need for phpseclib to check phpinfo
338 *
339 * @see phpseclib/Math/BigInteger
340 * @see https://github.com/GoogleCloudPlatform/getting-started-php/issues/85
341 * @codeCoverageIgnore
342 */
343 private function setPhpsecConstants()
344 {
345 if (\filter_var(\getenv('GAE_VM'), \FILTER_VALIDATE_BOOLEAN)) {
346 if (!\defined('WPMailSMTP\\Vendor\\MATH_BIGINTEGER_OPENSSL_ENABLED')) {
347 \define('WPMailSMTP\\Vendor\\MATH_BIGINTEGER_OPENSSL_ENABLED', \true);
348 }
349 if (!\defined('WPMailSMTP\\Vendor\\CRYPT_RSA_MODE')) {
350 \define('WPMailSMTP\\Vendor\\CRYPT_RSA_MODE', \WPMailSMTP\Vendor\phpseclib\Crypt\RSA::MODE_OPENSSL);
351 }
352 }
353 }
354 /**
355 * Provide a hook to mock calls to the JWT static methods.
356 *
357 * @param string $method
358 * @param array $args
359 * @return mixed
360 */
361 protected function callJwtStatic($method, array $args = [])
362 {
363 $class = 'WPMailSMTP\\Vendor\\Firebase\\JWT\\JWT';
364 return \call_user_func_array([$class, $method], $args);
365 }
366 /**
367 * Provide a hook to mock calls to the JWT static methods.
368 *
369 * @param array $args
370 * @return mixed
371 */
372 protected function callSimpleJwtDecode(array $args = [])
373 {
374 return \call_user_func_array(['SimpleJWT\\JWT', 'decode'], $args);
375 }
376 /**
377 * Generate a cache key based on the cert location using sha1 with the
378 * exception of using "federated_signon_certs_v3" to preserve BC.
379 *
380 * @param string $certsLocation
381 * @return string
382 */
383 private function getCacheKeyFromCertLocation($certsLocation)
384 {
385 $key = $certsLocation === self::FEDERATED_SIGNON_CERT_URL ? 'federated_signon_certs_v3' : \sha1($certsLocation);
386 return 'google_auth_certs_cache|' . $key;
387 }
388 }
389