PrefectHQ/fastmcp · error · IdentityAssertionError

Assertion is missing resource claim

Error message

Assertion is missing resource claim

What it means

When the token exchange specifies a `resource` (resource indicator, RFC 8707), the assertion must carry a matching signed `resource` claim. FastMCP raises this error when the claim is absent, not a string, or empty — the assertion is not bound to this server, so it cannot be exchanged for a token targeting it.

Source

Thrown at fastmcp_slim/fastmcp/server/auth/identity_assertion.py:432

                raise IdentityAssertionError(
                    f"Assertion missing required scopes: {sorted(missing)}"
                )

        # 6. The signed client_id and resource claims bind the assertion to the
        # presenting client and this server. Checked here — before jti is
        # recorded as consumed below — so an assertion presented with the
        # wrong binding is rejected without burning replay protection for
        # whichever client/server it actually belongs to.
        assertion_client_id = claims.get("client_id")
        if not assertion_client_id or assertion_client_id != client_id:
            raise IdentityAssertionError(
                f"Assertion client_id {assertion_client_id!r} does not match "
                f"authenticated client {client_id!r}"
            )
        if resource_url is not None:
            assertion_resource = claims.get("resource")
            if not isinstance(assertion_resource, str) or not assertion_resource:
                raise IdentityAssertionError("Assertion is missing resource claim")
            if server_url_has_query(resource_url):
                claim_matches = assertion_resource.rstrip("/") == resource_url.rstrip(
                    "/"
                )
            else:
                claim_matches = normalize_resource_url(
                    assertion_resource
                ) == normalize_resource_url(resource_url)
            if not claim_matches:
                raise IdentityAssertionError(
                    f"Assertion resource {assertion_resource!r} does not match "
                    f"this server {resource_url!r}"
                )

        # 7. jti replay rejection (RFC 7523 §3). Must be a non-empty string —
        # an array/object jti is unhashable and would raise TypeError on the
        # cache lookup (a 500) instead of a clean invalid_grant.
        jti = claims.get("jti")

View on GitHub (pinned to 1f02114297)

Solutions

  1. Configure the IdP/issuer to include the `resource` claim (the MCP server's URL) in assertions.
  2. Ensure the claim is a plain non-empty string, not an array or null.
  3. If the deployment doesn't use resource indicators, perform the exchange without a resource parameter so this check is skipped.
  4. Upgrade the issuer if it predates RFC 8707 resource-indicator support.

Example fix

// before
claims = {"iss": iss, "sub": sub, "aud": aud, "exp": now + 300}
// after
claims = {"iss": iss, "sub": sub, "aud": aud, "resource": server_url, "exp": now + 300}
Defensive patterns

Strategy: validation

Validate before calling

def has_resource_claim(claims: dict) -> bool:
    r = claims.get("resource")
    return isinstance(r, str) and bool(r)

Type guard

def is_string_resource(claims: dict) -> bool:
    return isinstance(claims.get("resource"), str) and len(claims["resource"]) > 0

Try / catch

try:
    token = await exchange(assertion, resource=SERVER_URL)
except IdentityAssertionError as e:
    if "missing resource claim" in str(e):
        raise ValueError("Issuer does not support RFC 8707 resource indicators") from e
    raise

Prevention

When it happens

Trigger: Calling `validate()` with resource_url set while the assertion payload has no `resource` key, `resource: null`, a non-string (e.g. a list of audiences), or an empty string.

Common situations: IdP not configured to include RFC 8707 resource indicators in assertions; an older issuer predating resource-indicator support; issuer emitting `aud`-style arrays under `resource`; client requesting a token exchange with a resource parameter while its IdP assertion template omits the claim.

Related errors


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