Skip to content

Add in-memory caching for token introspection results#3298

Open
jlowin wants to merge 8 commits intomainfrom
claude/slack-investigate-issue-229m0
Open

Add in-memory caching for token introspection results#3298
jlowin wants to merge 8 commits intomainfrom
claude/slack-investigate-issue-229m0

Conversation

@jlowin
Copy link
Member

@jlowin jlowin commented Feb 25, 2026

Token introspection validates every request against the authorization server, which can become a bottleneck under load. This adds optional in-memory caching to IntrospectionTokenVerifier that reduces introspection calls while respecting token lifecycles.

Caching is disabled by default to preserve real-time revocation semantics—if you need instant token invalidation, the default behavior is unchanged. When enabled, the cache respects the token's own expiration claim, so a token expiring in 30 seconds won't be cached for 5 minutes.

# Default: every request hits the introspection endpoint
verifier = IntrospectionTokenVerifier(
    introspection_url="https://auth.example.com/introspect",
    client_id="my-client",
    client_secret="secret",
)

# Opt-in: cache results for 5 minutes (max 10k tokens)
verifier = IntrospectionTokenVerifier(
    introspection_url="https://auth.example.com/introspect",
    client_id="my-client",
    client_secret="secret",
    cache_ttl_seconds=300,
    max_cache_size=10000,
)

Closes #3285

Note: for the moment, this is an in-memory cache, not full pykeyvalue compliance.

@marvin-context-protocol marvin-context-protocol bot added enhancement Improvement to existing functionality. For issues and smaller PR improvements. auth Related to authentication (Bearer, JWT, OAuth, WorkOS) for client or server. labels Feb 25, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f0e353c5db

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +219 to +223
if len(self._cache) >= self._max_cache_size:
self._cleanup_expired_cache()
# If still at limit after cleanup, evict oldest entry
if len(self._cache) >= self._max_cache_size:
oldest_key = next(iter(self._cache))

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject non-positive max_cache_size values

When caching is enabled and max_cache_size is set to 0 (or any negative value), the size check is immediately true even for an empty cache, so next(iter(self._cache)) raises StopIteration. That exception is then swallowed by the broad except Exception in verify_token, causing otherwise valid introspection responses to return None and reject tokens. Validate max_cache_size as a positive integer (or treat non-positive values as cache-disabled) before entering this eviction path.

Useful? React with 👍 / 👎.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fc8a7ca860

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 331 to 334
if not introspection_data.get("active", False):
self.logger.debug("Token introspection returned active=false")
self._set_cached(token, None)
return None

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid caching every active=false introspection result

This branch caches None for any active=false response, and later calls short-circuit on that cache entry without re-introspecting. In environments where a token can transition from inactive to active (for example, tokens with a future nbf or short-lived authorization-server propagation delays), the verifier will continue rejecting an otherwise valid token for the full cache TTL. Consider not caching active=false results, or at least bounding that cache lifetime by a claim like nbf when present.

Useful? React with 👍 / 👎.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3dbb4e4977

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

del self._cache[cache_key]
return (False, None) # Expired

return (True, entry.result)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return a copy of cached AccessToken values

When caching is enabled, this returns the exact same AccessToken instance for every cache hit. AccessToken contains mutable fields (claims dict and scopes list), so if any request-path code mutates them (for example adding derived claims), that mutation is persisted in the cache and leaks into later requests for the same bearer token, which can change downstream authorization behavior. Please return a defensive copy (or cache an immutable representation) instead of sharing the same object instance across requests.

Useful? React with 👍 / 👎.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: be4c9074c7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 369 to 370
# Cache scope failures - token is valid but doesn't have required scopes
self._set_cached(token, None)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid caching scope-mismatch denials

verify_token now caches None when required_scopes are missing, and later _get_cached short-circuits without re-introspection, so a token that is initially denied due to transient introspection inconsistency (or dynamic scope/permission propagation) will keep failing for the full cache TTL even after the auth server would return sufficient scopes. This creates sticky false negatives in deployments where introspection data can change shortly after issuance.

Useful? React with 👍 / 👎.

Never cache None - failures (inactive, expired, missing scopes, errors)
should not create sticky false negatives. The cache now only stores
valid AccessToken results.

🤖 Generated with Claude Code

https://claude.ai/code/session_0172r9LV6i22XgFyLmziaKhk
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auth Related to authentication (Bearer, JWT, OAuth, WorkOS) for client or server. enhancement Improvement to existing functionality. For issues and smaller PR improvements.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cache result of IntrospectionTokenVerifier

2 participants