Retry failed JWT key refreshes with exponential backoff - #8226
Amaury Chamayou (achamayou) with Copilot wants to merge 9 commits into
Conversation
Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR improves JWT/JWK auto-refresh robustness in CCF by adding per-issuer retry scheduling with exponential backoff, so transient OpenID/JWKS endpoint failures recover quickly rather than waiting for the (often long) periodic refresh interval.
Changes:
- Add per-issuer retry state and delayed-task scheduling to retry failed refreshes quickly, then exponentially back off up to the configured refresh interval.
- Extend the connection-failure e2e test to verify recovery once the OpenID endpoint becomes available.
- Document the retry behavior in the JWT operator guide and add a changelog entry.
Custom instructions used:
.github/copilot-instructions.md.github/instructions/reviewing.instructions.md.github/instructions/changelog.instructions.md
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/node/jwt_key_auto_refresh.h |
Adds per-issuer retry scheduling with exponential backoff and cancellation on success/issuer removal/role change. |
tests/jwt_test.py |
Extends the connection-failure test to start the issuer after an initial failure and assert refresh recovery. |
doc/build_apps/auth/jwt.rst |
Documents the new retry + exponential backoff behavior. |
CHANGELOG.md |
Records the user-visible behavior change for JWT auto-refresh retries. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Keep the reserved local port bound until the OpenID server is about to bind it, so nothing else can claim it while the initial refresh failures are observed. - Look up the retried issuer directly instead of scanning every issuer, removing the optional filter and the issuer_found bookkeeping. - Handle a missing ca_cert_bundle_name rather than unwrapping the optional, which a non-default constitution could leave unset. - Simplify the backoff doubling. - Move the changelog entry to a new 7.0.14 section and keep python/pyproject.toml in sync. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f8de7f43-1588-4696-98fb-467b41ad04c2
Resolve the 7.0.14 changelog conflict and update retry locking to the ccf::ds API now used on main. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Rename the refresh interval cap and retry predicate, explain callback generations and shutdown locking, and add deterministic retry lifecycle coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Restore refresh_interval_s in the implementation and test fixture while retaining the other review improvements. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Exercise one-off and periodic refresh scheduling, initial response failures and rejected key updates. Assert shutdown leaves no runnable retries, including retries for multiple issuers, without tightening wall-clock timeouts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Main moved the curl client to src/http_client (#8285) and the HTTP RPC context into src/enclave (#8284). Apply the ccf::curl -> ccf::http_client rename and include moves to the refresh path extracted by this PR, and move the changelog entry from the released 7.0.14 section to 7.0.16. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
| args, network, kid, issuer.key_pub_pem | ||
| ), | ||
| timeout=15, | ||
| ) |
There was a problem hiding this comment.
I have approved, but I'll leave this discussion open to make sure there's better test coverage, although I'm not insisting, feel free to close and merge as is.
There was a problem hiding this comment.
The specifics of the timing are tested in detail here https://github.com/microsoft/CCF/pull/8226/changes#diff-af7d56e22a211a19785540ffa741b8699f0c4430f553189776c65f0d34e4b310R131, is there something specific that you think is not tested here and ought to be?
JWT key refresh previously waited for the configured periodic interval after failures, which defaults to 30 minutes. Existing outage coverage checked failure metrics but not subsequent recovery.
Changes
Retry scheduling
min(5 seconds, key_refresh_interval).ca_cert_bundle_nameas a refresh failure instead of unwrapping the optional, which a non-default constitution could leave unset.Outage recovery coverage
Documentation