Skip to content

Commit 22b1d02

Browse files
Cache signing keys (jpadilla#611)
* Cache the result of get_signing_key * Include URI in key for getting known signing keys * Add test_get_signing_key_caches_result test * Add test to make sure multiple uris are being distinguished in key caching * Ignore URI in caching * Use functools.lru_cache to cache signing keys * Allow opting out of key caching * Allow adjusting max cached keys * Add jpadilla#611 change to CHANGELOG.rst * Update CHANGELOG.rst Co-authored-by: José Padilla <jpadilla@webapplicate.com> Co-authored-by: José Padilla <jpadilla@webapplicate.com>
1 parent 373d4d8 commit 22b1d02

File tree

3 files changed

+41
-2
lines changed

3 files changed

+41
-2
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ Fixed
1818
Added
1919
~~~~~
2020

21+
- Add caching by default to PyJWKClient `#611 <https://github.com/jpadilla/pyjwt/pull/611>`__
22+
2123
`v2.0.1 <https://github.com/jpadilla/pyjwt/compare/2.0.0...2.0.1>`__
2224
--------------------------------------------------------------------
2325

jwt/jwks_client.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
import urllib.request
3+
from functools import lru_cache
34
from typing import Any, List
45

56
from .api_jwk import PyJWK, PyJWKSet
@@ -8,8 +9,12 @@
89

910

1011
class PyJWKClient:
11-
def __init__(self, uri: str):
12+
def __init__(self, uri: str, cache_keys: bool = True, max_cached_keys: int = 16):
1213
self.uri = uri
14+
if cache_keys:
15+
# Cache signing keys
16+
# Ignore mypy (https://github.com/python/mypy/issues/2427)
17+
self.get_signing_key = lru_cache(maxsize=max_cached_keys)(self.get_signing_key) # type: ignore
1318

1419
def fetch_data(self) -> Any:
1520
with urllib.request.urlopen(self.uri) as response:

tests/test_jwks_client.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def mocked_response(data):
3737
response.__exit__ = mock.Mock()
3838
response.read.side_effect = [json.dumps(data)]
3939
urlopen_mock.return_value = response
40-
yield
40+
yield urlopen_mock
4141

4242

4343
@crypto_required
@@ -88,6 +88,38 @@ def test_get_signing_key(self):
8888
assert signing_key.key_id == kid
8989
assert signing_key.public_key_use == "sig"
9090

91+
def test_get_signing_key_caches_result(self):
92+
url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json"
93+
kid = "NEE1QURBOTM4MzI5RkFDNTYxOTU1MDg2ODgwQ0UzMTk1QjYyRkRFQw"
94+
95+
jwks_client = PyJWKClient(url)
96+
97+
with mocked_response(RESPONSE_DATA):
98+
jwks_client.get_signing_key(kid)
99+
100+
# mocked_response does not allow urllib.request.urlopen to be called twice
101+
# so a second mock is needed
102+
with mocked_response(RESPONSE_DATA) as repeated_call:
103+
jwks_client.get_signing_key(kid)
104+
105+
assert repeated_call.call_count == 0
106+
107+
def test_get_signing_key_does_not_cache_opt_out(self):
108+
url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json"
109+
kid = "NEE1QURBOTM4MzI5RkFDNTYxOTU1MDg2ODgwQ0UzMTk1QjYyRkRFQw"
110+
111+
jwks_client = PyJWKClient(url, cache_keys=False)
112+
113+
with mocked_response(RESPONSE_DATA):
114+
jwks_client.get_signing_key(kid)
115+
116+
# mocked_response does not allow urllib.request.urlopen to be called twice
117+
# so a second mock is needed
118+
with mocked_response(RESPONSE_DATA) as repeated_call:
119+
jwks_client.get_signing_key(kid)
120+
121+
assert repeated_call.call_count == 1
122+
91123
def test_get_signing_key_from_jwt(self):
92124
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FRTFRVVJCT1RNNE16STVSa0ZETlRZeE9UVTFNRGcyT0Rnd1EwVXpNVGsxUWpZeVJrUkZRdyJ9.eyJpc3MiOiJodHRwczovL2Rldi04N2V2eDlydS5hdXRoMC5jb20vIiwic3ViIjoiYVc0Q2NhNzl4UmVMV1V6MGFFMkg2a0QwTzNjWEJWdENAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vZXhwZW5zZXMtYXBpIiwiaWF0IjoxNTcyMDA2OTU0LCJleHAiOjE1NzIwMDY5NjQsImF6cCI6ImFXNENjYTc5eFJlTFdVejBhRTJINmtEME8zY1hCVnRDIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.PUxE7xn52aTCohGiWoSdMBZGiYAHwE5FYie0Y1qUT68IHSTXwXVd6hn02HTah6epvHHVKA2FqcFZ4GGv5VTHEvYpeggiiZMgbxFrmTEY0csL6VNkX1eaJGcuehwQCRBKRLL3zKmA5IKGy5GeUnIbpPHLHDxr-GXvgFzsdsyWlVQvPX2xjeaQ217r2PtxDeqjlf66UYl6oY6AqNS8DH3iryCvIfCcybRZkc_hdy-6ZMoKT6Piijvk_aXdm7-QQqKJFHLuEqrVSOuBqqiNfVrG27QzAPuPOxvfXTVLXL2jek5meH6n-VWgrBdoMFH93QEszEDowDAEhQPHVs0xj7SIzA"
93125
url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json"

0 commit comments

Comments
 (0)