1 | <?php |
||
2 | |||
3 | /* |
||
4 | * Copyright 2008 Google Inc. |
||
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 | |||
19 | use Firebase\JWT\ExpiredException as ExpiredExceptionV3; |
||
20 | use Firebase\JWT\SignatureInvalidException; |
||
21 | use GuzzleHttp\Client; |
||
22 | use GuzzleHttp\ClientInterface; |
||
23 | use Psr\Cache\CacheItemPoolInterface; |
||
24 | use Google\Auth\Cache\MemoryCacheItemPool; |
||
25 | use Stash\Driver\FileSystem; |
||
0 ignored issues
–
show
|
|||
26 | use Stash\Pool; |
||
0 ignored issues
–
show
This use statement conflicts with another class in this namespace,
Pool . Consider defining an alias.
Let?s assume that you have a directory layout like this: .
|-- OtherDir
| |-- Bar.php
| `-- Foo.php
`-- SomeDir
`-- Foo.php
and let?s assume the following content of // Bar.php
namespace OtherDir;
use SomeDir\Foo; // This now conflicts the class OtherDir\Foo
If both files PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php
However, as // Bar.php
namespace OtherDir;
use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
![]() The type
Stash\Pool was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||
27 | |||
28 | /** |
||
29 | * Wrapper around Google Access Tokens which provides convenience functions |
||
30 | * |
||
31 | */ |
||
32 | class Google_AccessToken_Verify |
||
33 | { |
||
34 | const FEDERATED_SIGNON_CERT_URL = 'https://www.googleapis.com/oauth2/v3/certs'; |
||
35 | const OAUTH2_ISSUER = 'accounts.google.com'; |
||
36 | const OAUTH2_ISSUER_HTTPS = 'https://accounts.google.com'; |
||
37 | |||
38 | /** |
||
39 | * @var GuzzleHttp\ClientInterface The http client |
||
40 | */ |
||
41 | private $http; |
||
42 | |||
43 | /** |
||
44 | * @var Psr\Cache\CacheItemPoolInterface cache class |
||
45 | */ |
||
46 | private $cache; |
||
47 | |||
48 | /** |
||
49 | * Instantiates the class, but does not initiate the login flow, leaving it |
||
50 | * to the discretion of the caller. |
||
51 | */ |
||
52 | public function __construct( |
||
53 | ClientInterface $http = null, |
||
54 | CacheItemPoolInterface $cache = null, |
||
55 | $jwt = null |
||
56 | ) { |
||
57 | if (null === $http) { |
||
58 | $http = new Client(); |
||
59 | } |
||
60 | |||
61 | if (null === $cache) { |
||
62 | $cache = new MemoryCacheItemPool; |
||
63 | } |
||
64 | |||
65 | $this->http = $http; |
||
66 | $this->cache = $cache; |
||
67 | $this->jwt = $jwt ?: $this->getJwtService(); |
||
0 ignored issues
–
show
|
|||
68 | } |
||
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 $audience |
||
77 | * @return array the token payload, if successful |
||
78 | */ |
||
79 | public function verifyIdToken($idToken, $audience = null) |
||
80 | { |
||
81 | if (empty($idToken)) { |
||
82 | throw new LogicException('id_token cannot be null'); |
||
83 | } |
||
84 | |||
85 | // set phpseclib constants if applicable |
||
86 | $this->setPhpsecConstants(); |
||
87 | |||
88 | // Check signature |
||
89 | $certs = $this->getFederatedSignOnCerts(); |
||
90 | foreach ($certs as $cert) { |
||
91 | $bigIntClass = $this->getBigIntClass(); |
||
92 | $rsaClass = $this->getRsaClass(); |
||
93 | $modulus = new $bigIntClass($this->jwt->urlsafeB64Decode($cert['n']), 256); |
||
94 | $exponent = new $bigIntClass($this->jwt->urlsafeB64Decode($cert['e']), 256); |
||
95 | |||
96 | $rsa = new $rsaClass(); |
||
97 | $rsa->loadKey(array('n' => $modulus, 'e' => $exponent)); |
||
98 | |||
99 | try { |
||
100 | $payload = $this->jwt->decode( |
||
101 | $idToken, |
||
102 | $rsa->getPublicKey(), |
||
103 | array('RS256') |
||
104 | ); |
||
105 | |||
106 | if (property_exists($payload, 'aud')) { |
||
107 | if ($audience && $payload->aud != $audience) { |
||
108 | return false; |
||
0 ignored issues
–
show
|
|||
109 | } |
||
110 | } |
||
111 | |||
112 | // support HTTP and HTTPS issuers |
||
113 | // @see https://developers.google.com/identity/sign-in/web/backend-auth |
||
114 | $issuers = array(self::OAUTH2_ISSUER, self::OAUTH2_ISSUER_HTTPS); |
||
115 | if (!isset($payload->iss) || !in_array($payload->iss, $issuers)) { |
||
116 | return false; |
||
0 ignored issues
–
show
|
|||
117 | } |
||
118 | |||
119 | return (array) $payload; |
||
120 | } catch (ExpiredException $e) { |
||
0 ignored issues
–
show
The type
ExpiredException was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||
121 | return false; |
||
0 ignored issues
–
show
|
|||
122 | } catch (ExpiredExceptionV3 $e) { |
||
123 | return false; |
||
0 ignored issues
–
show
|
|||
124 | } catch (SignatureInvalidException $e) { |
||
125 | // continue |
||
126 | } catch (DomainException $e) { |
||
127 | // continue |
||
128 | } |
||
129 | } |
||
130 | |||
131 | return false; |
||
0 ignored issues
–
show
|
|||
132 | } |
||
133 | |||
134 | private function getCache() |
||
135 | { |
||
136 | return $this->cache; |
||
137 | } |
||
138 | |||
139 | /** |
||
140 | * Retrieve and cache a certificates file. |
||
141 | * |
||
142 | * @param $url string location |
||
143 | * @throws Google_Exception |
||
144 | * @return array certificates |
||
145 | */ |
||
146 | private function retrieveCertsFromLocation($url) |
||
147 | { |
||
148 | // If we're retrieving a local file, just grab it. |
||
149 | if (0 !== strpos($url, 'http')) { |
||
150 | if (!$file = file_get_contents($url)) { |
||
151 | throw new Google_Exception( |
||
152 | "Failed to retrieve verification certificates: '" . |
||
153 | $url . "'." |
||
154 | ); |
||
155 | } |
||
156 | |||
157 | return json_decode($file, true); |
||
158 | } |
||
159 | |||
160 | $response = $this->http->get($url); |
||
161 | |||
162 | if ($response->getStatusCode() == 200) { |
||
163 | return json_decode((string) $response->getBody(), true); |
||
164 | } |
||
165 | throw new Google_Exception( |
||
166 | sprintf( |
||
167 | 'Failed to retrieve verification certificates: "%s".', |
||
168 | $response->getBody()->getContents() |
||
169 | ), |
||
170 | $response->getStatusCode() |
||
171 | ); |
||
172 | } |
||
173 | |||
174 | // Gets federated sign-on certificates to use for verifying identity tokens. |
||
175 | // Returns certs as array structure, where keys are key ids, and values |
||
176 | // are PEM encoded certificates. |
||
177 | private function getFederatedSignOnCerts() |
||
178 | { |
||
179 | $certs = null; |
||
180 | if ($cache = $this->getCache()) { |
||
181 | $cacheItem = $cache->getItem('federated_signon_certs_v3'); |
||
182 | $certs = $cacheItem->get(); |
||
183 | } |
||
184 | |||
185 | |||
186 | if (!$certs) { |
||
187 | $certs = $this->retrieveCertsFromLocation( |
||
188 | self::FEDERATED_SIGNON_CERT_URL |
||
189 | ); |
||
190 | |||
191 | if ($cache) { |
||
0 ignored issues
–
show
|
|||
192 | $cacheItem->expiresAt(new DateTime('+1 hour')); |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||
193 | $cacheItem->set($certs); |
||
194 | $cache->save($cacheItem); |
||
195 | } |
||
196 | } |
||
197 | |||
198 | if (!isset($certs['keys'])) { |
||
199 | throw new InvalidArgumentException( |
||
200 | 'federated sign-on certs expects "keys" to be set' |
||
201 | ); |
||
202 | } |
||
203 | |||
204 | return $certs['keys']; |
||
205 | } |
||
206 | |||
207 | private function getJwtService() |
||
208 | { |
||
209 | $jwtClass = 'JWT'; |
||
210 | if (class_exists('\Firebase\JWT\JWT')) { |
||
211 | $jwtClass = 'Firebase\JWT\JWT'; |
||
212 | } |
||
213 | |||
214 | if (property_exists($jwtClass, 'leeway') && $jwtClass::$leeway < 1) { |
||
215 | // Ensures JWT leeway is at least 1 |
||
216 | // @see https://github.com/google/google-api-php-client/issues/827 |
||
217 | $jwtClass::$leeway = 1; |
||
218 | } |
||
219 | |||
220 | return new $jwtClass; |
||
221 | } |
||
222 | |||
223 | private function getRsaClass() |
||
224 | { |
||
225 | if (class_exists('phpseclib\Crypt\RSA')) { |
||
226 | return 'phpseclib\Crypt\RSA'; |
||
227 | } |
||
228 | |||
229 | return 'Crypt_RSA'; |
||
230 | } |
||
231 | |||
232 | private function getBigIntClass() |
||
233 | { |
||
234 | if (class_exists('phpseclib\Math\BigInteger')) { |
||
235 | return 'phpseclib\Math\BigInteger'; |
||
236 | } |
||
237 | |||
238 | return 'Math_BigInteger'; |
||
239 | } |
||
240 | |||
241 | private function getOpenSslConstant() |
||
242 | { |
||
243 | if (class_exists('phpseclib\Crypt\RSA')) { |
||
244 | return 'phpseclib\Crypt\RSA::MODE_OPENSSL'; |
||
245 | } |
||
246 | |||
247 | if (class_exists('Crypt_RSA')) { |
||
248 | return 'CRYPT_RSA_MODE_OPENSSL'; |
||
249 | } |
||
250 | |||
251 | throw new \Exception('Cannot find RSA class'); |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * phpseclib calls "phpinfo" by default, which requires special |
||
256 | * whitelisting in the AppEngine VM environment. This function |
||
257 | * sets constants to bypass the need for phpseclib to check phpinfo |
||
258 | * |
||
259 | * @see phpseclib/Math/BigInteger |
||
260 | * @see https://github.com/GoogleCloudPlatform/getting-started-php/issues/85 |
||
261 | */ |
||
262 | private function setPhpsecConstants() |
||
263 | { |
||
264 | if (filter_var(getenv('GAE_VM'), FILTER_VALIDATE_BOOLEAN)) { |
||
265 | if (!defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { |
||
266 | define('MATH_BIGINTEGER_OPENSSL_ENABLED', true); |
||
267 | } |
||
268 | if (!defined('CRYPT_RSA_MODE')) { |
||
269 | define('CRYPT_RSA_MODE', constant($this->getOpenSslConstant())); |
||
270 | } |
||
271 | } |
||
272 | } |
||
273 | } |
||
274 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths