PrefectHQ/fastmcp · error · AuthenticationError

Missing client_assertion

Error message

Missing client_assertion

What it means

This AuthenticationError is raised during OAuth client authentication when a client registered with token_endpoint_auth_method='private_key_jwt' sends a token request with a valid client_assertion_type but an absent, empty, or non-string client_assertion form field. The server cannot verify the client's identity without the signed JWT assertion, so the request is rejected before JWT validation is attempted.

Source

Thrown at fastmcp_slim/fastmcp/server/auth/auth.py:282

            return await super().authenticate_request(request)

        client = await self.provider.get_client(str(client_id))
        if not client:
            raise AuthenticationError("Invalid client_id")

        # Handle private_key_jwt authentication for CIMD clients
        if client.token_endpoint_auth_method == "private_key_jwt":
            # Validate assertion parameters
            assertion_type = form_data.get("client_assertion_type")
            assertion = form_data.get("client_assertion")

            if assertion_type != JWT_BEARER_ASSERTION_TYPE:
                raise AuthenticationError(
                    f"Invalid client_assertion_type: expected {JWT_BEARER_ASSERTION_TYPE}"
                )

            if not assertion or not isinstance(assertion, str):
                raise AuthenticationError("Missing client_assertion")

            # Validate the JWT assertion using CIMD manager
            try:
                await self._cimd_manager.validate_private_key_jwt(
                    assertion=assertion,
                    client=client,
                    token_endpoint=self._token_endpoint_url,
                )
            except ValueError as e:
                raise AuthenticationError(f"Invalid client assertion: {e}") from e

            return client

        # Delegate to SDK for other authentication methods
        return await super().authenticate_request(request)


class AuthProvider(TokenVerifierProtocol):

View on GitHub (pinned to 1f02114297)

Solutions

  1. Set the client_assertion form field to a properly signed JWT (signed with the client's private key per its CIMD jwks).
  2. Ensure the assertion is sent as a plain string form value, not as a file or repeated field.
  3. Verify client_assertion_type is exactly urn:ietf:params:oauth:client-assertion-type:jwt-bearer and both fields travel together in the same form-encoded POST.
  4. Regenerate or fix the client's token-request code so private_key_jwt clients always attach the assertion.

Example fix

// before
curl -X POST /token -d 'client_id=https://client.example.com&grant_type=authorization_code&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
// after
curl -X POST /token -d 'client_id=https://client.example.com&grant_type=authorization_code&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=<signed-jwt>'
Defensive patterns

Strategy: validation

Validate before calling

form = token_request_form  # dict of form fields
assert form.get('client_assertion_type') == 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
assertion = form.get('client_assertion')
if not isinstance(assertion, str) or not assertion:
    raise ValueError('client_assertion form field must be a non-empty string JWT')

Type guard

def has_client_assertion(form: dict) -> bool:
    a = form.get('client_assertion')
    return isinstance(a, str) and len(a) > 0

Prevention

When it happens

Trigger: POST to the token endpoint with a client_id whose CIMD document declares private_key_jwt, client_assertion_type set to urn:ietf:params:oauth:client-assertion-type:jwt-bearer, but the form field client_assertion is missing, empty, or not a string (e.g. a file part or repeated form value).

Common situations: Misconfigured OAuth client libraries that send assertion_type but forget the assertion; hand-rolled curl/token scripts missing the client_assertion parameter; form-encoding issues where the assertion arrives as a non-string part; template token-request code that never fills in the assertion.

Related errors


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