feat(montandon-eoapi): enforce external-token revocation in stac-auth--proxy - #207
Merged
Merged
Conversation
…-proxy stac-auth-proxy validates go-api JWTs locally (signature + exp) only, so a token revoked before its natural expiry stays valid for up to a year. Add a revocation check via go-api's verify endpoint (go-api issue #2794). stac-auth-proxy v1.1.1 has no config hook for token validation, so inject a montandon_revocation.py module (mounted through the configmap) that wraps EnforceAuthMiddleware.validate_token at app-construction time: - Reject tokens with no jti or a jti go-api reports inactive (401). - Static jti blacklist (env) checked first as a permanent kill-switch. - Positive-only in-memory cache, 300s TTL. - Verify URL derived from OIDC_DISCOVERY_URL; pod fails to start if unset. - Fail policy configurable (deployed fail-open until the verify endpoint is live in prod goadmin, then flip to fail-closed). - Patch guard raises on upstream signature drift so revocation can never be silently disabled. Rename the filters configmap/volume to stac-auth-proxy-modules / custom-modules since it now carries the auth patch alongside the CQL2 filter.
pantierra
reviewed
Jul 23, 2026
Comment on lines
+159
to
+163
| if not _is_active(jti): | ||
| logger.info("Rejecting revoked/unknown jti %s", jti) | ||
| raise _reject("Token has been revoked") | ||
|
|
||
| _cache_put(jti) |
Contributor
There was a problem hiding this comment.
If go-api is down and we let the token through, do we then cache that as “active” for the full TTL? If so, could a revoked token keep working for a bit even after go-api comes back? Would it make sense to only cache when we actually got active: true from go-api?
Member
Author
There was a problem hiding this comment.
Makes sense to skip the full TTL during an outage.
Maybe we use a very short TTL, say M seconds. This way we do not add high latency to every request (we have a timeout of 3 seconds for go-api requests). But we still check it every M seconds.
For now, start with 30 seconds for M.
Cache each verify outcome with its own TTL instead of one positive TTL: - verified active:true -> 300s (GOAPI_TOKEN_VERIFY_TTL) - fail-open allow during a go-api outage -> 30s (GOAPI_TOKEN_VERIFY_OUTAGE_TTL), so revocation resumes within seconds of recovery instead of being masked for a full positive-cache window - definitive deny (go-api reports the jti inactive/unknown) -> 24h (GOAPI_TOKEN_VERIFY_DENY_TTL), so a known-revoked token is not re-checked on every request Only definitive 200 active:false denials are negative-cached; a fail-closed outage rejects but is not cached, so recovery is never masked. The cache now stores the (allowed, expiry) decision and check_revocation honors a cached allow or deny before calling go-api.
Address code-review findings in montandon_revocation.py: - Decode the verify response strictly: only `active is True` allows and only `active is False` is a definitive (cacheable) deny. A 200 without a boolean `active` (field drift / interstitial served as 200) now rejects WITHOUT caching, so a valid token is never locked out for the 24h deny TTL; a stringy `"active":"false"` no longer coerces to allow. - Bound the decision cache with GOAPI_TOKEN_VERIFY_CACHE_MAX (default 50000); on overflow drop expired entries then the soonest-to-expire, so a long-lived pod seeing many distinct jtis can't grow unbounded. - Never let a token check become a 500: _verify wraps the call in a catch-all mapping to the fail policy, and a non-string jti is rejected before it can be used as an unhashable cache key. - Validate numeric env vars (_num_env): clear RuntimeError on non-numeric or negative values instead of an opaque float() crash. Blocking sync httpx on the event loop is retained by design (bounded by VERIFY_TIMEOUT, absorbed by the cache).
thenav56
marked this pull request as ready for review
July 24, 2026 11:06
thenav56
requested review from
batpad and
pantierra
and removed request for
pantierra
July 24, 2026 11:52
batpad
approved these changes
Jul 27, 2026
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.
Related to
stac-auth-proxy validates go-api JWTs locally (signature + exp) only, so a token revoked before its natural expiry stays valid for up to a year. Add a revocation check via go-api's verify endpoint (go-api issue #2794).
stac-auth-proxy v1.1.1 has no config hook for token validation, so inject a montandon_revocation.py module (mounted through the configmap) that wraps EnforceAuthMiddleware.validate_token at app-construction time:
Rename the filters configmap/volume to stac-auth-proxy-modules / custom-modules since it now carries the auth patch alongside the CQL2 filter.