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 processing

View on GitHub (pinned to 1f02114297)

Solutions

  1. Set the client's resource parameter to the exact resource URL the server is configured with
  2. Reconfigure the server's resource URL (base_url/resource parameter) to match what clients send
  3. 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

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


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/fb170f5d06d754da. Report an issue: GitHub.