PrefectHQ/fastmcp · error · AuthenticationError
Invalid client_assertion_type: expected {JWT_BEARER_ASSERTIO
Error message
Invalid client_assertion_type: expected {JWT_BEARER_ASSERTION_TYPE} What it means
When a client authenticates with token_endpoint_auth_method == 'private_key_jwt', the token request must carry client_assertion_type equal to the JWT-bearer IETF value (urn:ietf:params:oauth:client-assertion-type:jwt-bearer). If the form field is missing or has any other value, AuthenticationError('Invalid client_assertion_type: expected ...') is raised.
Source
Thrown at fastmcp_slim/fastmcp/server/auth/auth.py:277
client_id = form_data.get("client_id")
# If client_id is not in form data, delegate to SDK
# This handles client_secret_basic which sends credentials in Authorization header
if not client_id:
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
View on GitHub (pinned to 1f02114297)
Solutions
- Send `client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer` in the token request form body.
- Also include a signed `client_assertion` JWT (next check in the same flow) so the request passes subsequent validation.
- If the client shouldn't use private_key_jwt, register/configure it with the intended auth method (e.g. client_secret_post).
- Use a conformant OAuth client library instead of a hand-built token request.
Example fix
// before
requests.post(token_url, data={"client_id": cid, "client_assertion": jwt})
// after
requests.post(token_url, data={
"client_id": cid,
"client_assertion": jwt,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
}) Defensive patterns
Strategy: validation
Validate before calling
JWT_BEARER = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
assert form['client_assertion_type'] == JWT_BEARER, 'invalid client_assertion_type'
assert isinstance(form.get('client_assertion'), str) and form['client_assertion'] Try / catch
# client side
if response.status_code == 401 and response.json().get('error') == 'invalid_client':
logger.error('check client_assertion_type and client_assertion fields') Prevention
- Always send both client_assertion_type and client_assertion for private_key_jwt.
- Use the exact IETF urn value — no abbreviations.
- Prefer a maintained OAuth client library over hand-rolled token requests.
When it happens
Trigger: Token request with client_id in form data for a private_key_jwt client where form_data['client_assertion_type'] is absent or not 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer' (auth.py:276-279).
Common situations: Custom OAuth clients hand-rolling the token request and omitting the assertion_type field; libraries sending client_secret_post-style bodies while the server expects private_key_jwt; misconfigured client registered as private_key_jwt but actually using another flow.
Related errors
- CIMD document must have jwks_uri or jwks for private_key_jwt
- Invalid JWT assertion
- Assertion must include exp claim
- Assertion lifetime too long: {exp - iat}s (max {self.MAX_ASS
- Missing client_assertion
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/31f9ab7b68b91d8e.
Report an issue: GitHub.