PolicyEngine / PolicyEngine/policyengine-household-api

Add cache-invalidation strategy for rotated JWKS keys

Open
#1,474 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
1
Forks
3
Avg merge
6h 34m
Merged PRs (30d)
8

Description

Summary

#1471 added lazy-retry for the JWKS fetch, so a failing fetch at boot no longer caches None indefinitely. But there is no cache-invalidation strategy once a successful JWKS is cached — if Auth0 rotates signing keys between fetches, tokens signed with the new key will fail validation until the process restarts or a manual cache-clear runs.

What goes wrong

  • After a successful fetch, _jwks_cache[issuer] is held for the process lifetime.
  • Auth0 rotates signing keys periodically; PyJWKClient has a 5-minute internal cache but the outer _jwks_cache wrapper doesn't expire successful fetches at all.
  • Result: a rotation event causes every token signed with the new kid to 401 on this API until pods restart.

Suggested fix

Add a success-cache TTL consistent with rotation cadence (Auth0 default is days; conservative is 1 hour):

_JWKS_SUCCESS_TTL_SECONDS = 3600

def _fetch_jwks(issuer: str) -> PyJWKClient | None:
    cached = _jwks_cache.get(issuer)
    now = time.monotonic()
    if cached is not None:
        fetched_at, client = cached
        if client is not None and now - fetched_at < _JWKS_SUCCESS_TTL_SECONDS:
            return client
        # success older than TTL → re-fetch
    # … rest of fetch logic

Alternatively, because PyJWKClient already handles per-kid lookup + its own short cache, let it handle rotation internally and simplify the outer wrapper to just cache the client instance (not the JWKS itself) forever — the client will refetch individual kids on miss.

Severity

Medium — no active incident, but rotation-day outages are silent and self-healing only after restarts.

Relates to

Fixes #1471 (merged), #1468 (merged).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating _fetch_jwks and the _jwks_cache wrapper, then inspect how PyJWKClient is used for issuer lookups. Compare the proposed success-cache TTL with delegating rotation handling to PyJWKClient. Done means a newly rotated signing key can validate without a process restart, with coverage for cached and expired-client behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
authentication, backend, security
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.