| @@ -2,9 +2,8 @@ | ||
| 2 | 2 | |
| 3 | 3 | namespace Firebase\JWT; |
| 4 | 4 | |
| 5 | 5 | use ArrayAccess; |
| 6 | -use InvalidArgumentException; | |
| 7 | 6 | use LogicException; |
| 8 | 7 | use OutOfBoundsException; |
| 9 | 8 | use Psr\Cache\CacheItemInterface; |
| 10 | 9 | use Psr\Cache\CacheItemPoolInterface; |
| @@ -10,9 +9,8 @@ | ||
| 10 | 9 | use Psr\Cache\CacheItemPoolInterface; |
| 11 | 10 | use Psr\Http\Client\ClientInterface; |
| 12 | 11 | use Psr\Http\Message\RequestFactoryInterface; |
| 13 | 12 | use RuntimeException; |
| 14 | -use UnexpectedValueException; | |
| 15 | 13 | |
| 16 | 14 | /** |
| 17 | 15 | * @implements ArrayAccess<string, Key> |
| 18 | 16 | */ |
| @@ -42,9 +40,9 @@ | ||
| 42 | 40 | * @var ?CacheItemInterface |
| 43 | 41 | */ |
| 44 | 42 | private $cacheItem; |
| 45 | 43 | /** |
| 46 | - * @var array<string, array<mixed>> | |
| 44 | + * @var array<string, Key> | |
| 47 | 45 | */ |
| 48 | 46 | private $keySet; |
| 49 | 47 | /** |
| 50 | 48 | * @var string |
| @@ -79,11 +77,11 @@ | ||
| 79 | 77 | string $jwksUri, |
| 80 | 78 | ClientInterface $httpClient, |
| 81 | 79 | RequestFactoryInterface $httpFactory, |
| 82 | 80 | CacheItemPoolInterface $cache, |
| 83 | - ?int $expiresAfter = null, | |
| 81 | + int $expiresAfter = null, | |
| 84 | 82 | bool $rateLimit = false, |
| 85 | - ?string $defaultAlg = null | |
| 83 | + string $defaultAlg = null | |
| 86 | 84 | ) { |
| 87 | 85 | $this->jwksUri = $jwksUri; |
| 88 | 86 | $this->httpClient = $httpClient; |
| 89 | 87 | $this->httpFactory = $httpFactory; |
| @@ -102,9 +100,9 @@ | ||
| 102 | 100 | { |
| 103 | 101 | if (!$this->keyIdExists($keyId)) { |
| 104 | 102 | throw new OutOfBoundsException('Key ID not found'); |
| 105 | 103 | } |
| 106 | - return JWK::parseKey($this->keySet[$keyId], $this->defaultAlg); | |
| 104 | + return $this->keySet[$keyId]; | |
| 107 | 105 | } |
| 108 | 106 | |
| 109 | 107 | /** |
| 110 | 108 | * @param string $keyId |
| @@ -131,32 +129,8 @@ | ||
| 131 | 129 | { |
| 132 | 130 | throw new LogicException('Method not implemented'); |
| 133 | 131 | } |
| 134 | 132 | |
| 135 | - /** | |
| 136 | - * @return array<mixed> | |
| 137 | - */ | |
| 138 | - private function formatJwksForCache(string $jwks): array | |
| 139 | - { | |
| 140 | - $jwks = json_decode($jwks, true); | |
| 141 | - | |
| 142 | - if (!isset($jwks['keys'])) { | |
| 143 | - throw new UnexpectedValueException('"keys" member must exist in the JWK Set'); | |
| 144 | - } | |
| 145 | - | |
| 146 | - if (empty($jwks['keys'])) { | |
| 147 | - throw new InvalidArgumentException('JWK Set did not contain any keys'); | |
| 148 | - } | |
| 149 | - | |
| 150 | - $keys = []; | |
| 151 | - foreach ($jwks['keys'] as $k => $v) { | |
| 152 | - $kid = isset($v['kid']) ? $v['kid'] : $k; | |
| 153 | - $keys[(string) $kid] = $v; | |
| 154 | - } | |
| 155 | - | |
| 156 | - return $keys; | |
| 157 | - } | |
| 158 | - | |
| 159 | 133 | private function keyIdExists(string $keyId): bool |
| 160 | 134 | { |
| 161 | 135 | if (null === $this->keySet) { |
| 162 | 136 | $item = $this->getCacheItem(); |
| @@ -161,15 +135,11 @@ | ||
| 161 | 135 | if (null === $this->keySet) { |
| 162 | 136 | $item = $this->getCacheItem(); |
| 163 | 137 | // Try to load keys from cache |
| 164 | 138 | if ($item->isHit()) { |
| 165 | - // item found! retrieve it | |
| 166 | - $this->keySet = $item->get(); | |
| 167 | - // If the cached item is a string, the JWKS response was cached (previous behavior). | |
| 168 | - // Parse this into expected format array<kid, jwk> instead. | |
| 169 | - if (\is_string($this->keySet)) { | |
| 170 | - $this->keySet = $this->formatJwksForCache($this->keySet); | |
| 171 | - } | |
| 139 | + // item found! Return it | |
| 140 | + $jwks = $item->get(); | |
| 141 | + $this->keySet = JWK::parseKeySet(json_decode($jwks, true), $this->defaultAlg); | |
| 172 | 142 | } |
| 173 | 143 | } |
| 174 | 144 | |
| 175 | 145 | if (!isset($this->keySet[$keyId])) { |
| @@ -177,20 +147,10 @@ | ||
| 177 | 147 | return false; |
| 178 | 148 | } |
| 179 | 149 | $request = $this->httpFactory->createRequest('GET', $this->jwksUri); |
| 180 | 150 | $jwksResponse = $this->httpClient->sendRequest($request); |
| 181 | - if ($jwksResponse->getStatusCode() !== 200) { | |
| 182 | - throw new UnexpectedValueException( | |
| 183 | - \sprintf( | |
| 184 | - 'HTTP Error: %d %s for URI "%s"', | |
| 185 | - $jwksResponse->getStatusCode(), | |
| 186 | - $jwksResponse->getReasonPhrase(), | |
| 187 | - $this->jwksUri, | |
| 188 | - ), | |
| 189 | - $jwksResponse->getStatusCode() | |
| 190 | - ); | |
| 191 | - } | |
| 192 | - $this->keySet = $this->formatJwksForCache((string) $jwksResponse->getBody()); | |
| 151 | + $jwks = (string) $jwksResponse->getBody(); | |
| 152 | + $this->keySet = JWK::parseKeySet(json_decode($jwks, true), $this->defaultAlg); | |
| 193 | 153 | |
| 194 | 154 | if (!isset($this->keySet[$keyId])) { |
| 195 | 155 | return false; |
| 196 | 156 | } |
| @@ -195,9 +155,9 @@ | ||
| 195 | 155 | return false; |
| 196 | 156 | } |
| 197 | 157 | |
| 198 | 158 | $item = $this->getCacheItem(); |
| 199 | - $item->set($this->keySet); | |
| 159 | + $item->set($jwks); | |
| 200 | 160 | if ($this->expiresAfter) { |
| 201 | 161 | $item->expiresAfter($this->expiresAfter); |
| 202 | 162 | } |
| 203 | 163 | $this->cache->save($item); |
| @@ -212,23 +172,17 @@ | ||
| 212 | 172 | return false; |
| 213 | 173 | } |
| 214 | 174 | |
| 215 | 175 | $cacheItem = $this->cache->getItem($this->rateLimitCacheKey); |
| 216 | - | |
| 217 | - $cacheItemData = []; | |
| 218 | - if ($cacheItem->isHit() && \is_array($data = $cacheItem->get())) { | |
| 219 | - $cacheItemData = $data; | |
| 176 | + if (!$cacheItem->isHit()) { | |
| 177 | + $cacheItem->expiresAfter(1); // # of calls are cached each minute | |
| 220 | 178 | } |
| 221 | 179 | |
| 222 | - $callsPerMinute = $cacheItemData['callsPerMinute'] ?? 0; | |
| 223 | - $expiry = $cacheItemData['expiry'] ?? new \DateTime('+60 seconds', new \DateTimeZone('UTC')); | |
| 224 | - | |
| 180 | + $callsPerMinute = (int) $cacheItem->get(); | |
| 225 | 181 | if (++$callsPerMinute > $this->maxCallsPerMinute) { |
| 226 | 182 | return true; |
| 227 | 183 | } |
| 228 | - | |
| 229 | - $cacheItem->set(['expiry' => $expiry, 'callsPerMinute' => $callsPerMinute]); | |
| 230 | - $cacheItem->expiresAt($expiry); | |
| 184 | + $cacheItem->set($callsPerMinute); | |
| 231 | 185 | $this->cache->save($cacheItem); |
| 232 | 186 | return false; |
| 233 | 187 | } |
| 234 | 188 | |