feat(auth): resolve Auth's signing keys from its published JWKS with static keys as fallback - #22
Merged
Merged
Conversation
…static keys as fallback TL;DR: Explorer now verifies back-channel logout tokens with Auth's published key set as well as the key pasted into its environment. Rotating Auth's signing key no longer requires editing every Explorer host's env: Auth publishes the new key at /jwks.json, Explorer picks it up within ten minutes or immediately when a token names it. The static key stays as the bootstrap and offline fallback. Problem: Every consumer verified Auth's Ed25519 signatures with AUTH_SIGNING_PUBKEY (plus AUTH_SIGNING_PREVIOUS_PUBKEYS during rotation), so a rotation meant touching every application host in the right order or logout events started failing with 400s until the env caught up. Auth already publishes current and overlapping keys at /jwks.json; nothing read it. Fix: - AuthKeyResolver: static env keys plus keys parsed from <issuer>/jwks.json (OKP/Ed25519 entries only, 32-byte x, converted to the base64 form the verifier expects). Cached ten minutes; a fetch failure keeps the cached set; no redirects followed; 64 KB cap. - keys_for_token refreshes once when a token's kid matches no known key, rate-limited to one attempt a minute so unknown-kid tokens cannot amplify requests to Auth. - CentralAuthProvider owns one resolver; the back-channel receiver asks it for the candidate keys per token. Startup still requires one valid static key so verification works when Auth is unreachable at boot; the error message now says why. - Tests never touch the network: an autouse fixture replaces the fetch function, and the resolver looks it up at call time. - DEPLOYMENT.md documents the behaviour and demotes AUTH_SIGNING_PREVIOUS_PUBKEYS to a manual override. Tests: - New: static and published keys merge in order; the cache holds inside the TTL; a fetch failure after the TTL keeps cached keys; an unknown kid triggers exactly one refresh per minute; malformed JWKS entries (RSA, short x, non-object) are ignored; a token signed by a key present only in the JWKS verifies. - Ran: pytest -q (full suite), ruff check, ruff format --check.
The autouse fixture that keeps tests off the network carried a leftover line that indexed __defaults__ on AuthKeyResolver.__init__, which is None now that the fetch argument defaults to None; every test errored at setup. Fixture reduced to the one monkeypatch it needs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TLDR
Explorer verifies back-channel logout tokens with Auth's published
/jwks.jsonkeys as well as the staticAUTH_SIGNING_PUBKEY. An Auth signing-key rotation no longer needs an env edit on Explorer hosts: new keys are picked up within ten minutes, or immediately when a token names an unknownkid(one refresh per minute at most). The static key remains required as bootstrap and offline fallback.Design
AuthKeyResolver: merge static + JWKS keys (OKP/Ed25519 only), 10-minute cache, fetch failure keeps the cache, no redirects, 64 KB cap, rate-limited refresh on unknown kid. Owned byCentralAuthProvider; the receiver asks it for candidate keys per token.Tests
Merge order, cache TTL, failure fallback, rate-limited unknown-kid refresh, malformed-entry filtering, verification with a JWKS-only key. Tests never hit the network (autouse fixture). Full suite, ruff clean.