55
66use Auth0 \SDK \API \Helpers \RequestBuilder ;
77use Auth0 \SDK \Helpers \Cache \NoCacheHandler ;
8+ use Auth0 \SDK \Exception \CoreException ;
89use GuzzleHttp \Exception \ClientException ;
910use GuzzleHttp \Exception \RequestException ;
1011use Psr \SimpleCache \CacheInterface ;
1718class JWKFetcher
1819{
1920 /**
20- * How long should the cache persist? Set to 10 minutes.
21+ * Default length of cache persistence. Defaults to 10 minutes.
2122 *
2223 * @see https://www.php-fig.org/psr/psr-16/#12-definitions
2324 */
2425 const CACHE_TTL = 600 ;
2526
27+ /**
28+ * How long should the cache persist? Defaults to value of CACHE_TTL.
29+ * We strongly encouraged you leave the default value.
30+ *
31+ * @see https://www.php-fig.org/psr/psr-16/#12-definitions
32+ */
33+ private $ ttl = self ::CACHE_TTL ;
34+
2635 /**
2736 * Cache handler or null for no caching.
2837 *
2938 * @var CacheInterface|null
3039 */
3140 private $ cache ;
3241
42+ /**
43+ * Cache for unique cache ids.
44+ * Key for each entry is the url to the JWK. Value is cache id.
45+ *
46+ * @var array
47+ */
48+ private $ cachedEntryIds = [];
49+
3350 /**
3451 * Options for the Guzzle HTTP client.
3552 *
@@ -42,15 +59,20 @@ class JWKFetcher
4259 *
4360 * @param CacheInterface|null $cache Cache handler or null for no caching.
4461 * @param array $guzzleOptions Guzzle HTTP options.
62+ * @param options $options Class options to apply at initializion.
4563 */
46- public function __construct (CacheInterface $ cache = null , array $ guzzleOptions = [])
64+ public function __construct (CacheInterface $ cache = null , array $ guzzleOptions = [], array $ options = [] )
4765 {
4866 if ($ cache === null ) {
4967 $ cache = new NoCacheHandler ();
5068 }
5169
5270 $ this ->cache = $ cache ;
5371 $ this ->guzzleOptions = $ guzzleOptions ;
72+
73+ if (!empty ($ options ['ttl ' ])) {
74+ $ this ->setTtl ($ options ['ttl ' ]);
75+ }
5476 }
5577
5678 /**
@@ -98,12 +120,13 @@ public function getKey(string $kid, string $jwksUri = null)
98120 public function getKeys (string $ jwks_url = null , bool $ use_cache = true ) : array
99121 {
100122 $ jwks_url = $ jwks_url ?? $ this ->guzzleOptions ['base_uri ' ] ?? '' ;
123+
101124 if (empty ( $ jwks_url )) {
102125 return [];
103126 }
104127
105- $ cache_key = md5 ($ jwks_url );
106- $ cached_value = $ use_cache ? $ this -> cache -> get ( $ cache_key ) : null ;
128+ $ cached_value = $ use_cache ? $ this -> getCacheEntry ($ jwks_url ) : null ;
129+
107130 if (! empty ($ cached_value ) && is_array ($ cached_value )) {
108131 return $ cached_value ;
109132 }
@@ -123,7 +146,7 @@ public function getKeys(string $jwks_url = null, bool $use_cache = true) : array
123146 $ keys [$ key ['kid ' ]] = $ this ->convertCertToPem ( $ key ['x5c ' ][0 ] );
124147 }
125148
126- $ this ->cache -> set ( $ cache_key , $ keys, self :: CACHE_TTL );
149+ $ this ->setCacheEntry ( $ jwks_url , $ keys );
127150 return $ keys ;
128151 }
129152
@@ -148,4 +171,111 @@ protected function requestJwks(string $jwks_url) : array
148171
149172 return $ request ->call ();
150173 }
174+
175+ /**
176+ * Set how long to cache JWKs in seconds.
177+ * We strongly encouraged you leave the default value.
178+ *
179+ * @param string $ttlSeconds Number of seconds to keep a JWK in memory.
180+ *
181+ * @return $this
182+ *
183+ * @throws CoreException If $ttlSeconds is less than 60.
184+ */
185+ public function setTtl (int $ ttlSeconds )
186+ {
187+ if ($ ttlSeconds < 60 ) {
188+ throw new CoreException ('TTL cannot be less than 60 seconds. ' );
189+ }
190+
191+ $ this ->ttl = $ ttlSeconds ;
192+ return $ this ;
193+ }
194+
195+ /**
196+ * Returns how long we are caching JWKs in seconds.
197+ *
198+ * @return integer
199+ */
200+ public function getTtl ()
201+ {
202+ return $ this ->ttl ;
203+ }
204+
205+ /**
206+ * Generate a cache id to use for a URL.
207+ *
208+ * @param string $jwks_url Full URL to the JWKS.
209+ *
210+ * @return string
211+ */
212+ public function getCacheKey (string $ jwksUri )
213+ {
214+ if (isset ($ this ->cachedEntryIds [$ jwksUri ])) {
215+ return $ this ->cachedEntryIds [$ jwksUri ];
216+ }
217+
218+ $ cacheKey = md5 ($ jwksUri );
219+
220+ $ this ->cachedEntryIds [$ jwksUri ] = $ cacheKey ;
221+ return $ cacheKey ;
222+ }
223+
224+ /**
225+ * Get a specific JWK from the cache by it's URL.
226+ *
227+ * @param string $jwks_url Full URL to the JWKS.
228+ *
229+ * @return null|array
230+ */
231+ public function getCacheEntry (string $ jwksUri )
232+ {
233+ $ cache_key = $ this ->getCacheKey ($ jwksUri );
234+ $ cached_value = $ this ->cache ->get ($ cache_key );
235+
236+ if (! empty ($ cached_value ) && is_array ($ cached_value )) {
237+ return $ cached_value ;
238+ }
239+
240+ return null ;
241+ }
242+
243+ /**
244+ * Add or overwrite a specific JWK from the cache.
245+ *
246+ * @param string $jwks_url Full URL to the JWKS.
247+ * @param array $keys An array representing the JWKS.
248+ *
249+ * @return $this
250+ */
251+ public function setCacheEntry (string $ jwksUri , array $ keys )
252+ {
253+ $ cache_key = $ this ->getCacheKey ($ jwksUri );
254+
255+ $ this ->cache ->set ($ cache_key , $ keys , $ this ->ttl );
256+
257+ return $ this ;
258+ }
259+
260+ /**
261+ * Remove a specific JWK from the cache by it's URL.
262+ *
263+ * @param string $jwks_url Full URL to the JWKS.
264+ *
265+ * @return boolean
266+ */
267+ public function removeCacheEntry (string $ jwksUri )
268+ {
269+ return $ this ->cache ->delete ($ this ->getCacheKey ($ jwksUri ));
270+ }
271+
272+ /**
273+ * Clear the JWK cache.
274+ *
275+ * @return boolean
276+ */
277+ public function clearCache ()
278+ {
279+ return $ this ->cache ->clear ();
280+ }
151281}
0 commit comments