Skip to content

feat(fastmcp,mcp): expose fail_closed through the adapter auth factories - #21

Open
RobertoIskandarani wants to merge 8 commits into
mainfrom
feat/fastmcp-fail-closed
Open

feat(fastmcp,mcp): expose fail_closed through the adapter auth factories#21
RobertoIskandarani wants to merge 8 commits into
mainfrom
feat/fastmcp-fail-closed

Conversation

@RobertoIskandarani

@RobertoIskandarani RobertoIskandarani commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Closes #20.

What

Both adapter factories — authplane_fastmcp.authplane_auth() and authplane_mcp.authplane_mcp_auth() — now accept fail_closed: bool = False and forward it to AuthplaneClient.resource(...). The core SDK already implemented fail_closed; the factories simply never exposed it, so integrations opting into IntrospectionRevocation() were locked into fail-open behavior on introspection/revocation-check outages unless they abandoned the one-call factory and wired the resource manually.

authplane-mcp had the same gap as authplane-fastmcp, so both factories are covered in this PR to keep them in parity with the core API.

Changes

  • fail_closed: bool = False keyword on both factories, forwarded at the client.resource(...) boundary. Default preserves today's fail-open behavior; fully backward compatible.
  • Core test coverage for fail_closed=True (previously untested): a crashing custom checker and an introspection HTTP 500 both reject with TokenRevokedError.
  • Forwarding tests for both adapters (default False, explicit True).
  • User guides for both packages: new "Failure Policy: Fail-Open vs Fail-Closed" section documenting the availability/security trade-off, the credential requirement for authenticated introspection, the circuit-breaker interaction (once the breaker opens, all tokens are rejected until the cooldown elapses), and that the flag is a no-op without a revocation_checker.
  • Changelog entry under Unreleased.

Testing

  • pytest tests (core): 522 passed
  • pytest tests in authplane-fastmcp: 55 passed
  • pytest tests in authplane-mcp: 55 passed
  • ruff check / ruff format --check: clean
  • pyright: 0 errors

@RobertoIskandarani
RobertoIskandarani requested a review from a team as a code owner July 28, 2026 19:05
@RobertoIskandarani RobertoIskandarani self-assigned this Jul 28, 2026
The core SDK already implements fail_closed on
AuthplaneClient.resource(...), but neither authplane_auth() nor
authplane_mcp_auth() forwarded it, so factory users opting into
revocation checking were locked into fail-open behavior on
introspection/revocation-check outages.

Both factories now accept fail_closed: bool = False and pass it through
at the client.resource(...) boundary. The default preserves today's
fail-open behavior.

Also adds the previously missing core coverage for fail_closed=True
(crashing custom checker and introspection HTTP 500 both reject with
TokenRevokedError), forwarding tests for both adapters, and user-guide
docs covering the availability/security trade-off, the
authenticated-introspection credential requirement, and the
circuit-breaker interaction.

Requested in #20.
ruff 0.16.0 started formatting Python code blocks inside Markdown
files, so CI (which installs the latest ruff satisfying >=0.8) rejects
the previously formatted docs. Blank-line normalization only, no
content changes.
@RobertoIskandarani
RobertoIskandarani force-pushed the feat/fastmcp-fail-closed branch from b6a4e4b to 0520af7 Compare July 28, 2026 19:23
…ases

- AuthplaneClient.resource() logs a warning when fail_closed=True is
  combined with revocation_checker=None. The flag is a security-relevant
  no-op in that combination, and the misconfiguration should be
  observable in production, not only documented. The warning lives in
  the core factory rather than the adapters so direct resource() users
  get it too.
- New core tests pin two fail-closed failure modes that were emergent:
  AS metadata without introspection_endpoint rejects every token
  (permanently, since it is a configuration property rather than an
  outage), and an open circuit breaker rejects all traffic without
  further HTTP calls (TokenRevokedError with CircuitOpenError as cause).
- Both user guides gain the metadata trade-off bullet and note the new
  warning.
@RobertoIskandarani
RobertoIskandarani force-pushed the feat/fastmcp-fail-closed branch from 40ee361 to f0442bd Compare July 28, 2026 19:51
muralx
muralx previously approved these changes Jul 29, 2026

@muralx muralx left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks, please pin ruff version so we avoid formatting changes from happening during feature development and bug fixes.

Comment thread CHANGELOG.md Outdated
An unpinned ruff means CI silently adopts each new minor's lint and
formatting behavior mid-development (0.16.0 started formatting Markdown
code blocks, which is what forced the docs reformat on this branch).
Pinning to the current minor keeps patch fixes and makes format checks
reproducible; bumping the minor becomes a deliberate change.
mcp 1.27.2, the newest release the previous <1.28.0 ceiling allowed,
carries PYSEC-2026-3483; the fix ships in 1.28.1. Adapter tests pass
against 1.28.1.
muralx
muralx previously approved these changes Jul 29, 2026
The <1.29.0 ceiling let mcp 1.28 resolve, but 1.28 renamed the elicitation
field to snake_case elicitation_id, which the adapter's url_elicitation path
does not handle — every consent-driven exchange would raise a pydantic
ValidationError instead of -32042. Keep the ceiling below 1.28 until the
adapter is migrated (README already documents the <1.28.0 range).

Also document the fail_closed-without-revocation_checker warning in the core
user-guide (the direct client.resource() audience); both adapter guides
already cover it.
The adapter pins mcp <1.28.0 (1.28 renamed elicitationId -> elicitation_id,
breaking url_elicitation.py), so pip-audit resolves the vulnerable mcp 1.27.2.
Suppress the finding with a comment until the adapter is migrated to 1.28.1.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Expose fail_closed through authplane_fastmcp.authplane_auth

2 participants