Skip to content

Authorize tools from trusted identities - #7827

Merged
Aias00 merged 1 commit into
apache:masterfrom
BobSong-dev:feat/key-auth-verified-result
Oct 20, 2026
Merged

Aias00 merged 1 commit into
apache:masterfrom
BobSong-dev:feat/key-auth-verified-result

Conversation

@BobSong-dev

@BobSong-dev BobSong-dev commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Background

The Agent Gateway MCP endpoint can list and call configured tools, but it does not yet bind tool access to a gateway-verified identity. An authenticated request therefore cannot receive tenant- or subject-specific tool grants, and applications cannot reuse their existing Spring Security authentication result for MCP authorization.

Changes

  • Added a verified, credential-free KeyAuthAuthentication result for successful API-key authentication, with request binding and rule attribution.
  • Added Key-auth identity mapping from the verified key to tenant, subject, tool grants, and tool denials.
  • Added principal identity mapping for already authenticated JWT, opaque-token, and OAuth2 login principals, without re-implementing signature verification, introspection, login state, session, or CSRF handling.
  • Evaluated tool permissions as (grants - denials) intersect rule allowedTools, with denial taking precedence and one immutable authorization snapshot shared by tools/list and tools/call.
  • Kept the feature disabled by default, froze configuration at startup, and added length/count limits plus fail-closed handling for missing identities, conflicting identity sources, multiple resolvers, mismatched issuers/audiences/registrations, and invalid or unknown configuration.
  • Documented Key-auth and principal authorization configuration and semantics.

Verification

  • Windows/JDK17 ./mvnw clean verify for shenyu-plugin-security/shenyu-plugin-key-auth, shenyu-plugin/shenyu-plugin-agent-gateway, and their Spring Boot starter: 659 tests, 0 failures, 0 errors, 0 skipped; Checkstyle and RAT passed.
  • RISC-V/QEMU ./mvnw clean verify for the same three-module reactor: 659 tests, 0 failures, 0 errors, 0 skipped; Checkstyle and RAT passed.
  • git diff --check: passed.

@BobSong-dev

Copy link
Copy Markdown
Contributor Author

P0-02

@Aias00 Aias00 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the full diff (21 files, +3052 / -5). This is a well-engineered, security-focused feature with strong design and excellent test coverage. A few non-blocking suggestions below.

Strengths

  • Fail-closed by default. Feature disabled by default, frozen at startup, strict validation (length/count limits, no wildcards, ignoreUnknownFields = false), and one immutable authorization snapshot shared by tools/list and tools/call.
  • Strong request isolation. KeyAuthAuthentication is a credential-free reference bound to the request's Reactor Context and verified against exchange.getAttributes(); it cannot be borrowed across requests or inherited by disabled/unmatched key-auth execution. The Mono.defer + contextWrite composition (outer clear, inner put) is correct.
  • No secret leakage. KeyAuthAuthentication does not override toString, and tests assert the API key never appears in output; forged headers/attributes are ignored. Adapter never logs tokens/claims or copies inbound identity into outbound credentials.
  • Optional dependency hygiene. shenyu-plugin-key-auth and the Spring Security modules are optional, guarded by @ConditionalOnClass / ClassUtils.isPresent to avoid hard classpath coupling. Exaclty-one-resolver enforcement (>1 throws) prevents conflicting authorities.
  • Excellent tests. Real loopback HTTP (JSON + SSE), concurrent tenant isolation, key rotation, cross-request isolation, error/cancellation, conflicting key-auth/principal sources, and scope vs scp conflicts are all covered.

Non-blocking suggestions

  1. aud single-string handling. tokenAuthority calls PrincipalMcpSecurityResolver.names(attributes.get("aud"), false). RFC 7519 allows aud to be a string or array, and Spring Security's getTokenAttributes().get("aud") can be a single String; in that case names(...) throws and the predicate fails closed, silently rejecting a valid token. Consider accepting both String and Collection for aud (and iss).
  2. OAuth2-login sub vs principal name. PrincipalMcpSecurityResolver.resolve requires subject == authentication.getName(). For OAuth2AuthenticationToken the name defaults to the name attribute, which may differ from the sub claim in some IdPs. This is documented, but an explicit attribute-name mapping for sub on the login source would avoid silent rejection.
  3. AgentMcpIdentity constructor ergonomics. Two public constructors with different parameter orders ((subject, grants) vs (tenant, subject, grants, denials)) are easy to confuse. Consider a single builder, or @Deprecated on the legacy 2-arg constructor with a clear note on which callers need the tenant.
  4. Hardcoded FQCN strings. AgentGatewayPluginConfiguration.agentGatewayPlugin mixes "org.apache...PrincipalMcpSecurityResolver".equals(resolver.getClass().getName()) with instanceof KeyAuthMcpSecurityResolver. Use instanceof consistently for readability and refactor safety.

None of these are correctness or security blockers. The change is safe to merge once the maintainers are comfortable with the aud/sub behaviour for their IdP setups.

Approved.

@Aias00
Aias00 merged commit dce6c01 into apache:master Oct 20, 2026
28 checks passed
@Aias00 Aias00 added this to the 2.7.2 milestone Nov 13, 2026
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.

2 participants