pyjwt icon indicating copy to clipboard operation
pyjwt copied to clipboard

Support swapping out the cache backend for JWKClient

Open rabbit-aaron opened this issue 1 year ago • 3 comments

Great project!

I'm using it with my Django project, where it runs multiple containers with scaling. I have a redis server shared between these instances.

I would like to cache JWKs in redis. I could provide a PR if people are interested.

I'd propose the following:

from typing import Protocol
from .jwk_set_cache import JWKSetCache

class JWKSetCacheProtocol(Protocol):
    def put(self, jwk_set: PyJWKSet) -> None: ...
        ...

    def get(self) -> Optional[PyJWKSet]:
        ...

    def is_expired(self) -> bool:
        ...



class PyJWKClient:
    def __init__(
        self,
        uri: str,
        cache_keys: bool = False,
        max_cached_keys: int = 16,
        cache_jwk_set: bool = True,
        lifespan: int = 300,
        headers: Optional[Dict[str, Any]] = None,
        timeout: int = 30,
        ssl_context: Optional[SSLContext] = None,
        jwk_set_cache: Optional[JWKSetCacheProtocol] = None,
    ):
            if headers is None:
            headers = {}
        self.uri = uri
        self.jwk_set_cache: Optional[JWKSetCache] = None
        self.headers = headers
        self.timeout = timeout
        self.ssl_context = ssl_context

        if jwk_set_cache:
            self.jwk_set_cache = jwk_set_cache

        elif cache_jwk_set:
            # Init jwt set cache with default or given lifespan.
            # Default lifespan is 300 seconds (5 minutes).
            if lifespan <= 0:
                raise PyJWKClientError(
                    f'Lifespan must be greater than 0, the input is "{lifespan}"'
                )
            self.jwk_set_cache = JWKSetCache(lifespan)
        else:
            self.jwk_set_cache = None

        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

rabbit-aaron avatar Aug 29 '24 02:08 rabbit-aaron

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days

github-actions[bot] avatar Oct 29 '24 02:10 github-actions[bot]

This is a good approach for APIs that throttle requests and could end up returning 429's to JWK fetches, when multiple workers run concurrently.

At the moment, the hacky workaround is to override the jwk_set_cache client attribute right after instantiating it, replacing it with a Redis client that implements the Protocol you suggested.

adamantike avatar Jan 30 '25 12:01 adamantike

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days

github-actions[bot] avatar Apr 07 '25 02:04 github-actions[bot]

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days

github-actions[bot] avatar Oct 03 '25 02:10 github-actions[bot]