Xj$ddlmZddlZddlZddlmZddlmZddl m Z ddl m Z m Z ddlmZdd lmZmZdd lmZdd lmZmZdd lmZGd dZy)) annotationsN) lru_cache) SSLContext)Any) HTTPErrorURLError)urlparse)PyJWKPyJWKSet)decode_complete)PyJWKClientConnectionErrorPyJWKClientError) JWKSetCacheceZdZ d d dZd dZd d dZd ddZddZddZe ddZ y) PyJWKClientNc f|i}t|jj} | dvrtd| d||_d|_||_||_||_|r%|dkrtd|dt||_nd|_|r$t||j} | |_ yy) uA client for retrieving signing keys from a JWKS endpoint. ``PyJWKClient`` uses a two-tier caching system to avoid unnecessary network requests: **Tier 1 — JWK Set cache** (enabled by default): Caches the entire JSON Web Key Set response from the endpoint. Controlled by: - ``cache_jwk_set``: Set to ``True`` (the default) to enable this cache. When enabled, the JWK Set is fetched from the network only when the cache is empty or expired. - ``lifespan``: Time in seconds before the cached JWK Set expires. Defaults to ``300`` (5 minutes). Must be greater than 0. **Tier 2 — Signing key cache** (disabled by default): Caches individual signing keys (looked up by ``kid``) using an LRU cache with **no time-based expiration**. Keys are evicted only when the cache reaches its maximum size. Controlled by: - ``cache_keys``: Set to ``True`` to enable this cache. Defaults to ``False``. - ``max_cached_keys``: Maximum number of signing keys to keep in the LRU cache. Defaults to ``16``. :param uri: The URL of the JWKS endpoint. :type uri: str :param cache_keys: Enable the per-key LRU cache (Tier 2). :type cache_keys: bool :param max_cached_keys: Max entries in the signing key LRU cache. :type max_cached_keys: int :param cache_jwk_set: Enable the JWK Set response cache (Tier 1). :type cache_jwk_set: bool :param lifespan: TTL in seconds for the JWK Set cache. :type lifespan: float :param headers: Optional HTTP headers to include in requests. :type headers: dict or None :param timeout: HTTP request timeout in seconds. :type timeout: float :param ssl_context: Optional SSL context for the request. :type ssl_context: ssl.SSLContext or None N)httphttpszInvalid JWKS URI scheme z(: only 'http' and 'https' are supported.rz/Lifespan must be greater than 0, the input is "")maxsize) r schemelowerruri jwk_set_cacheheaderstimeout ssl_contextrrget_signing_key) selfr cache_keysmax_cached_keys cache_jwk_setlifespanrrrrrs c/mnt/ssd/data/Dropbox/adrian/vault-secondbrain/venv/lib/python3.12/site-packages/jwt/jwks_client.py__init__zPyJWKClient.__init__sj ?G #%%++- * *"*6*5!" 15  & 1}&EhZqQ"-X!6D !%D  @i@AUAUVO#2D c tjj|j|j}tjj ||j |j5}tj|}ddd|j |j j#S#1swY2xYw#ttf$r5}t|tr|jtd|d|d}~wwxYw)aeFetch the JWK Set from the JWKS endpoint. Makes an HTTP request to the configured ``uri`` and returns the parsed JSON response. If the JWK Set cache is enabled, the response is stored in the cache. :returns: The parsed JWK Set as a dictionary. :raises PyJWKClientConnectionError: If the HTTP request fails. )urlr)rcontextNz'Fail to fetch data from the url, err: "r)urllibrequestRequestrrurlopenrrjsonloadr TimeoutError isinstancercloserrput)r rresponsejwk_setes r% fetch_datazPyJWKClient.fetch_datajs &&488T\\&JA''4<<1A1A( .))H- .    )    " "7 +# . .,' !Y' ,9!A>  s0A+C-B4C4B=9CD0C??Dcd}|j|s|jj}||j}t|ts t dt j|S)aNReturn the JWK Set, using the cache when available. :param refresh: Force a fresh fetch from the endpoint, bypassing the cache. :type refresh: bool :returns: The JWK Set. :rtype: PyJWKSet :raises PyJWKClientError: If the endpoint does not return a JSON object. Nz.The JWKS endpoint did not return a JSON object)rgetr9r2dictrr from_dict)r refreshdatas r% get_jwk_setzPyJWKClient.get_jwk_setsc    )'%%))+D <??$D$%"#ST T!!$''r'c|j|}|jDcgc]}|jdvr|jr| }}|s t d|Scc}w)aReturn all signing keys from the JWK Set. Filters the JWK Set to keys whose ``use`` is ``"sig"`` (or unspecified) and that have a ``kid``. :param refresh: Force a fresh fetch from the endpoint, bypassing the cache. :type refresh: bool :returns: A list of signing keys. :rtype: list[PyJWK] :raises PyJWKClientError: If no signing keys are found. )sigNz2The JWKS endpoint did not contain any signing keys)r@keyspublic_key_usekey_idr)r r>r7 jwk_set_key signing_keyss r%get_signing_keyszPyJWKClient.get_signing_keyssi""7+ '|| ))]:{?Q?Q   "#WX X s#Ac|j}|j||}|s5|jd}|j||}|std|d|S)aReturn the signing key matching the given ``kid``. If no match is found in the current JWK Set, the set is refreshed from the endpoint and the lookup is retried once. :param kid: The key ID to look up. :type kid: str :returns: The matching signing key. :rtype: PyJWK :raises PyJWKClientError: If no matching key is found after refreshing. T)r>z,Unable to find a signing key that matches: "r)rH match_kidr)r kidrG signing_keys r%rzPyJWKClient.get_signing_keysl,,. nn\37 000>L..s;K&B3%qIr'cjt|ddi}|d}|j|jdS)aGReturn the signing key for a JWT by reading its ``kid`` header. Extracts the ``kid`` from the token's unverified header and delegates to :meth:`get_signing_key`. :param token: The encoded JWT. :type token: str or bytes :returns: The matching signing key. :rtype: PyJWK verify_signatureF)optionsheaderrK) decode_tokenrr;)r token unverifiedrPs r%get_signing_key_from_jwtz$PyJWKClient.get_signing_key_from_jwts:"%2De1LM H%##FJJu$566r'c@d}|D]}|j|k(s|}|S|S)a7Find a key in *signing_keys* that matches *kid*. :param signing_keys: The list of keys to search. :type signing_keys: list[PyJWK] :param kid: The key ID to match. :type kid: str :returns: The matching key, or ``None`` if not found. :rtype: PyJWK or None N)rE)rGrKrLkeys r%rJzPyJWKClient.match_kids:  CzzS !    r')FTi,NN)rstrr!boolr"intr#rZr$floatrzdict[str, Any] | Nonerr\rzSSLContext | None)returnr)F)r>rZr]r )r>rZr] list[PyJWK])rKrYr]r )rRz str | bytesr]r )rGr^rKrYr]z PyJWK | None) __name__ __module__ __qualname__r&r9r@rHrrT staticmethodrJr'r%rrs!!")-)-V3 V3V3 V3  V3  V3'V3V3'V3p>(.28 7r'r) __future__rr/urllib.requestr+ functoolsrsslrtypingr urllib.errorrr urllib.parser api_jwkr r api_jwtr rQ exceptionsrrrrrrcr'r%rns5" ,!$4D&eer'