PrefectHQ/fastmcp · error · AuthorizeError
invalid_target
invalid_target
Error message
Resource does not match this server
What it means
OAuth 2.0 resource indicator (RFC 8707) checking: when the authorization request includes a resource parameter, OAuthProxy compares it to the server's configured resource URL. A mismatch means the client is asking for tokens for a different audience, so authorize rejects the request with AuthorizeError code invalid_target.
Source
Thrown at fastmcp_slim/fastmcp/server/auth/oauth_proxy/proxy.py:1160
server_url = str(self._resource_url)
client_url = str(client_resource)
if server_url_has_query(server_url):
# Server has query params - require exact match for security
urls_match = client_url.rstrip("/") == server_url.rstrip("/")
else:
# Server has no query params - normalize both for comparison
urls_match = normalize_resource_url(
client_url
) == normalize_resource_url(server_url)
if not urls_match:
logger.warning(
"Resource mismatch: client requested %s but server is %s",
client_resource,
self._resource_url,
)
raise AuthorizeError(
error="invalid_target", # type: ignore[arg-type]
error_description="Resource does not match this server",
)
# Generate transaction ID for this authorization request
txn_id = secrets.token_urlsafe(32)
# Generate proxy's own PKCE parameters if forwarding is enabled
proxy_code_verifier = None
proxy_code_challenge = None
if self._forward_pkce and params.code_challenge:
proxy_code_verifier, proxy_code_challenge = self._generate_pkce_pair()
logger.debug(
"Generated proxy PKCE for transaction %s (forwarding client PKCE to upstream)",
txn_id,
)
# Store transaction data for IdP callback processingView on GitHub (pinned to 1f02114297)
Solutions
- Set the client's resource parameter to the exact resource URL the server is configured with
- Reconfigure the server's resource URL (base_url/resource parameter) to match what clients send
- Normalize URL differences (scheme/host/port/trailing slash) on the client side
Example fix
// before GET /authorize?...&resource=https://old.example.com/mcp // after GET /authorize?...&resource=https://api.example.com/mcp # matches server resource URL
Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlparse
def resource_matches(requested: str, server_resource: str) -> bool:
a, b = urlparse(requested), urlparse(server_resource)
return (a.scheme, a.netloc, a.path.rstrip("/")) == (b.scheme, b.netloc, b.path.rstrip("/"))
assert resource_matches(my_resource_param, SERVER_RESOURCE_URL) Type guard
def has_valid_resource(params: dict, server: str) -> bool:
return "resource" not in params or resource_matches(params["resource"], server) Try / catch
# AuthorizeError surfaces as a redirect back to client with error=invalid_target
async def handle_authorize_error(e: AuthorizeError):
if e.error == "invalid_target":
logger.error("resource param does not match server resource URL: %s", e.error_description)
return RedirectResponse(add_query(client_redirect_uri, {"error": "invalid_target"}))
raise Prevention
- Configure clients with the exact resource URL the proxy advertises
- Re-check the resource parameter after changing domains, ports, or reverse-proxy paths
- Prefer reading the resource URL from server metadata rather than hardcoding
When it happens
Trigger: Calling authorize (via _start_flow, i.e. hitting the /authorize endpoint) with a resource query parameter that does not equal the configured _resource_url (e.g. wrong scheme, host, port, or path).
Common situations: Clients hardcoding an old or different server URL in the resource parameter; server deployed behind a proxy/domain different from what clients request; trailing-slash or path differences; clients that omit resource vs. those sending the wrong one.
Related errors
- The device authorization request expired
- The device authorization request was denied
- Device authorization failed
- OAuth client not found - cached credentials may be stale
- Unexpected authorization response: {response.status_code}
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/fb170f5d06d754da.
Report an issue: GitHub.