fix(auth): address PR #758 follow-up review
Six findings from the latest claude-bot review on PR #758: - JWKS cache had no kid-miss refresh path (Medium): on IdP key rotation every login failed for up to _OIDC_CACHE_TTL. Evict and refetch once before raising, per OIDC core §10.1.1. - _should_use_secure_cookies fell back to nextcloud_host scheme, but the cookie is issued by the MCP server. Switch to settings.nextcloud_mcp_server_url so split-scheme deployments get the right Secure flag. - _origin_matches_self compared raw netloc strings, which include the port. Browsers omit default ports per RFC 6454 §6.2; an mcp_server_url like :443 falsely 403'd every legitimate logout. Normalise (scheme, host, port) tuples with default ports stripped. - delete_oauth_session exists in storage.py — drop the stale "we don't have this method" comment and call it eagerly so replays can't be processed and the table doesn't accumulate completed-but-not-yet-expired browser-login rows. - extract_user_id_from_token's unused ctx param renamed to _ctx to signal "intentionally unused" at the signature level. - provisioning_decorator instantiated RefreshTokenStorage per call. Switch to get_shared_storage() for the lock-protected process-wide singleton. Plus pre-push self-review catch: lazy-logging on the unchanged except arm in session_backend.py. Adds 5 regression tests: - JWKS rotation: success on refetch - JWKS rotation: still-missing-kid surfaces original error - JWKS rotation: network error during refresh wrapped as IdTokenVerificationError - default-port CSRF: explicit :443 in config + portless Origin - default-port CSRF: portless config + explicit :443 in Origin - scheme-mismatch CSRF: same host, different scheme rejected Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
af25c281bf
commit
2d340a5a6b
@@ -115,10 +115,24 @@ async def verify_id_token(
|
||||
raise IdTokenVerificationError("ID token header missing 'kid'")
|
||||
try:
|
||||
signing_key = jwks[kid]
|
||||
except KeyError as e:
|
||||
raise IdTokenVerificationError(
|
||||
f"No JWKS key matches ID token kid {kid!r}"
|
||||
) from e
|
||||
except KeyError:
|
||||
# Cache miss may indicate IdP key rotation. Refresh JWKS once
|
||||
# before giving up, per OIDC core §10.1.1: when an unrecognised
|
||||
# `kid` arrives the relying party should refetch the JWKS rather
|
||||
# than waiting for cache TTL to elapse.
|
||||
_jwks_cache.pop(jwks_uri, None)
|
||||
try:
|
||||
jwks_data = await _get_cached(_jwks_cache, jwks_uri)
|
||||
jwks = PyJWKSet.from_dict(jwks_data)
|
||||
signing_key = jwks[kid]
|
||||
except KeyError as e:
|
||||
raise IdTokenVerificationError(
|
||||
f"No JWKS key matches ID token kid {kid!r}"
|
||||
) from e
|
||||
except Exception as e:
|
||||
raise IdTokenVerificationError(
|
||||
f"Failed to refresh JWKS after kid miss: {e}"
|
||||
) from e
|
||||
|
||||
# PyJWT verifies the JWT with the algorithm declared in its header,
|
||||
# cross-checked against this allowlist (so an attacker can't downgrade
|
||||
@@ -158,7 +172,7 @@ async def verify_id_token(
|
||||
return payload
|
||||
|
||||
|
||||
async def extract_user_id_from_token(ctx: Context) -> str:
|
||||
async def extract_user_id_from_token(_ctx: Context) -> str:
|
||||
"""Extract user_id from the verified MCP access token.
|
||||
|
||||
Reads the `sub` claim from `AccessToken.resource`, which is populated by
|
||||
@@ -168,7 +182,10 @@ async def extract_user_id_from_token(ctx: Context) -> str:
|
||||
identity claim.
|
||||
|
||||
Args:
|
||||
ctx: MCP context with access token (unused — kept for the public API)
|
||||
_ctx: MCP context with access token. Intentionally unused — kept on
|
||||
the public signature so call sites can pass the FastMCP Context
|
||||
they already hold without rewriting; identity is read from the
|
||||
verifier-populated AccessToken via get_access_token().
|
||||
|
||||
Returns:
|
||||
user_id from the verified token, or "default_user" when no token is
|
||||
|
||||
Reference in New Issue
Block a user