fix(auth): document introspection-error fall-through, drop misleading userinfo metric

Address claude-review round 4 on #919:
- Functional concern: document that _introspect_token returns None for both an
  active=false response (the cross-client case we must handle) AND a network
  error, so both fall through to userinfo. This is safe — userinfo is itself an
  authoritative live check, so a flapping introspection endpoint can't cause an
  invalid token to be accepted.
- Observability nit: only record a ("userinfo", ...) metric when userinfo was
  actually attempted (userinfo_uri configured); a no-validators-configured
  opaque token now returns None without a misleading userinfo-failure metric.
  Added test_opaque_rejected_when_no_validators_configured.
- Added a comment on the post-validation cache re-read explaining why the entry
  is always present (write-then-read with no await between).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-06-17 20:18:21 +02:00
co-authored by Claude Opus 4.8
parent bafe82c897
commit ed32519563
2 changed files with 33 additions and 4 deletions
+14
View File
@@ -741,6 +741,20 @@ class TestUserinfoFallback:
assert result.resource == "testuser"
introspect_mock.assert_not_called() # skipped when unconfigured
async def test_opaque_rejected_when_no_validators_configured(self, base_settings):
"""With neither introspection nor userinfo configured, an opaque token is
rejected without recording a misleading userinfo-failure metric."""
base_settings.introspection_uri = None
base_settings.userinfo_uri = None
verifier = UnifiedTokenVerifier(base_settings)
assert verifier.introspection_uri is None
assert verifier.userinfo_uri is None
result = await verifier._verify_without_audience_check(
"opaque-no-validator", "mgmt:none"
)
assert result is None
async def test_mgmt_userinfo_not_called_when_introspection_succeeds(
self, monkeypatch, userinfo_settings
):