[v1.x] Add AuthSettings.validate_token_resource to check a bearer token's resource (#3451)
Some checks failed
Main branch checks / checks (push) Has been cancelled

This commit is contained in:
Max 2026-09-05 03:52:13 +01:00 • committed by GitHub
commit 8c2fa6eaba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 175 additions and 9 deletions

View file

@ -75,6 +75,7 @@ def create_resource_server(settings: ResourceServerSettings) -> FastMCP:
issuer_url=settings.auth_server_url,
required_scopes=[settings.mcp_scope],
resource_server_url=settings.server_url,
validate_token_resource=True, # tokens must be reported as issued for server_url
),
)

View file

@ -69,12 +69,19 @@ class IntrospectionTokenVerifier(TokenVerifier):
logger.warning(f"Token resource validation failed. Expected: {self.resource_url}")
return None
# `aud` may be a string or a list; report the entry naming this server when there is
# one, otherwise what the token was issued for, so the server can compare it.
aud: str | list[str] | None = data.get("aud")
audiences = aud if isinstance(aud, list) else [aud] if aud else []
own = self.resource_url.rstrip("/")
resource = next((a for a in audiences if a.rstrip("/") == own), audiences[0] if audiences else None)
return AccessToken(
token=token,
client_id=data.get("client_id", "unknown"),
scopes=data.get("scope", "").split() if data.get("scope") else [],
expires_at=data.get("exp"),
resource=data.get("aud"), # Include resource in token
resource=resource,
subject=data.get("sub"), # RFC 7662 subject (resource owner)
claims=data,
)

View file

@ -26,8 +26,9 @@ mcp = FastMCP(
# Auth settings for RFC 9728 Protected Resource Metadata
auth=AuthSettings(
issuer_url=AnyHttpUrl("https://auth.example.com"), # Authorization Server URL
resource_server_url=AnyHttpUrl("http://localhost:3001"), # This server's URL
resource_server_url=AnyHttpUrl("http://127.0.0.1:8000/mcp"), # This server's URL (mcp.run() default)
required_scopes=["user"],
validate_token_resource=True,
),
)