Skip to content

Commit

Permalink
Cache signing keys (#611)
Browse files Browse the repository at this point in the history
* 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 #611 change to CHANGELOG.rst

* Update CHANGELOG.rst

Co-authored-by: José Padilla <jpadilla@webapplicate.com>

Co-authored-by: José Padilla <jpadilla@webapplicate.com>
  • Loading branch information
stevenpitts and jpadilla authored Feb 27, 2021
1 parent 373d4d8 commit 22b1d02
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Fixed
Added
~~~~~

- Add caching by default to PyJWKClient `#611 <https://github.com/jpadilla/pyjwt/pull/611>`__

`v2.0.1 <https://github.com/jpadilla/pyjwt/compare/2.0.0...2.0.1>`__
--------------------------------------------------------------------

Expand Down
7 changes: 6 additions & 1 deletion jwt/jwks_client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import urllib.request
from functools import lru_cache
from typing import Any, List

from .api_jwk import PyJWK, PyJWKSet
Expand All @@ -8,8 +9,12 @@


class PyJWKClient:
def __init__(self, uri: str):
def __init__(self, uri: str, cache_keys: bool = True, max_cached_keys: int = 16):
self.uri = uri
if cache_keys:
# Cache signing keys
# Ignore mypy (https://github.com/python/mypy/issues/2427)
self.get_signing_key = lru_cache(maxsize=max_cached_keys)(self.get_signing_key) # type: ignore

def fetch_data(self) -> Any:
with urllib.request.urlopen(self.uri) as response:
Expand Down
34 changes: 33 additions & 1 deletion tests/test_jwks_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def mocked_response(data):
response.__exit__ = mock.Mock()
response.read.side_effect = [json.dumps(data)]
urlopen_mock.return_value = response
yield
yield urlopen_mock


@crypto_required
Expand Down Expand Up @@ -88,6 +88,38 @@ def test_get_signing_key(self):
assert signing_key.key_id == kid
assert signing_key.public_key_use == "sig"

def test_get_signing_key_caches_result(self):
url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json"
kid = "NEE1QURBOTM4MzI5RkFDNTYxOTU1MDg2ODgwQ0UzMTk1QjYyRkRFQw"

jwks_client = PyJWKClient(url)

with mocked_response(RESPONSE_DATA):
jwks_client.get_signing_key(kid)

# mocked_response does not allow urllib.request.urlopen to be called twice
# so a second mock is needed
with mocked_response(RESPONSE_DATA) as repeated_call:
jwks_client.get_signing_key(kid)

assert repeated_call.call_count == 0

def test_get_signing_key_does_not_cache_opt_out(self):
url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json"
kid = "NEE1QURBOTM4MzI5RkFDNTYxOTU1MDg2ODgwQ0UzMTk1QjYyRkRFQw"

jwks_client = PyJWKClient(url, cache_keys=False)

with mocked_response(RESPONSE_DATA):
jwks_client.get_signing_key(kid)

# mocked_response does not allow urllib.request.urlopen to be called twice
# so a second mock is needed
with mocked_response(RESPONSE_DATA) as repeated_call:
jwks_client.get_signing_key(kid)

assert repeated_call.call_count == 1

def test_get_signing_key_from_jwt(self):
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FRTFRVVJCT1RNNE16STVSa0ZETlRZeE9UVTFNRGcyT0Rnd1EwVXpNVGsxUWpZeVJrUkZRdyJ9.eyJpc3MiOiJodHRwczovL2Rldi04N2V2eDlydS5hdXRoMC5jb20vIiwic3ViIjoiYVc0Q2NhNzl4UmVMV1V6MGFFMkg2a0QwTzNjWEJWdENAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vZXhwZW5zZXMtYXBpIiwiaWF0IjoxNTcyMDA2OTU0LCJleHAiOjE1NzIwMDY5NjQsImF6cCI6ImFXNENjYTc5eFJlTFdVejBhRTJINmtEME8zY1hCVnRDIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.PUxE7xn52aTCohGiWoSdMBZGiYAHwE5FYie0Y1qUT68IHSTXwXVd6hn02HTah6epvHHVKA2FqcFZ4GGv5VTHEvYpeggiiZMgbxFrmTEY0csL6VNkX1eaJGcuehwQCRBKRLL3zKmA5IKGy5GeUnIbpPHLHDxr-GXvgFzsdsyWlVQvPX2xjeaQ217r2PtxDeqjlf66UYl6oY6AqNS8DH3iryCvIfCcybRZkc_hdy-6ZMoKT6Piijvk_aXdm7-QQqKJFHLuEqrVSOuBqqiNfVrG27QzAPuPOxvfXTVLXL2jek5meH6n-VWgrBdoMFH93QEszEDowDAEhQPHVs0xj7SIzA"
url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json"
Expand Down

0 comments on commit 22b1d02

Please sign in to comment.