PrefectHQ/fastmcp · error · IdentityAssertionError
Assertion typ must be {ID_JAG_TYP!r}, got {header.get('typ')
Error message
Assertion typ must be {ID_JAG_TYP!r}, got {header.get('typ')!r} What it means
The assertion header is a valid JSON object but its `typ` claim does not equal the required id-jag type marker `oauth-id-jag+jwt` (SEP-990 §5.1). FastMCP enforces this so ordinary access tokens or other JWTs cannot be used as identity assertions.
Source
Thrown at fastmcp_slim/fastmcp/server/auth/identity_assertion.py:360
Returns:
The verified claims (including `sub`, `iss`, and any `resource`/`scope`).
Raises:
IdentityAssertionError: If the assertion is invalid for any reason.
"""
self._maybe_cleanup()
# 1. typ header MUST be oauth-id-jag+jwt (SEP-990 §5.1).
try:
header = decode_jwt_header(assertion)
except (ValueError, KeyError, IndexError) as e:
raise IdentityAssertionError(f"Malformed assertion header: {e}") from e
if not isinstance(header, dict):
# A JSON-array/scalar header is valid JSON but not a JOSE header;
# guard before .get() so this maps to invalid_grant, not a 500.
raise IdentityAssertionError("Assertion JOSE header must be a JSON object")
if header.get("typ") != ID_JAG_TYP:
raise IdentityAssertionError(
f"Assertion typ must be {ID_JAG_TYP!r}, got {header.get('typ')!r}"
)
# 2. iss must be a trusted issuer before we fetch any keys for it.
try:
unverified_claims = _decode_unverified_claims(assertion)
except (ValueError, KeyError, IndexError) as e:
raise IdentityAssertionError(f"Malformed assertion payload: {e}") from e
if not isinstance(unverified_claims, dict):
raise IdentityAssertionError("Assertion payload is not a JSON object")
iss = unverified_claims.get("iss")
if not iss or iss not in self.config.trusted_issuers:
raise IdentityAssertionError(f"Untrusted assertion issuer: {iss!r}")
# 3. Verify signature, iss, aud, and exp via JWTVerifier.
verifier = await self._get_verifier(iss)
access_token = await verifier.load_access_token(assertion)
if access_token is None:View on GitHub (pinned to 1f02114297)
Solutions
- Ensure the client performs the id-jag exchange and sends the resulting assertion (typ oauth-id-jag+jwt), not the access token.
- Check the token-minting code or library sets header typ='oauth-id-jag+jwt' when creating the assertion.
- Upgrade the client SDK/IdP integration if it predates SEP-990 typ requirements.
- Inspect the header with a JWT decoder to confirm typ before debugging server-side.
Example fix
// before
header = {"alg": "RS256", "typ": "JWT"}
// after
header = {"alg": "RS256", "typ": "oauth-id-jag+jwt"} Defensive patterns
Strategy: validation
Validate before calling
import base64, json
h = json.loads(base64.urlsafe_b64decode(assertion.split('.')[0] + '=='))
assert h.get('typ') == 'oauth-id-jag+jwt', f"bad typ: {h.get('typ')!r}" Type guard
def has_idjag_typ(token: str) -> bool:
import base64, json
h = json.loads(base64.urlsafe_b64decode(token.split('.')[0] + '=='))
return isinstance(h, dict) and h.get('typ') == 'oauth-id-jag+jwt' Try / catch
try:
await provider.validate(assertion)
except IdentityAssertionError as e:
if 'typ must be' in str(e):
log.warning('wrong token type sent as assertion (typ=%s)', e)
raise Prevention
- Ensure clients send the id-jag assertion, not an access token
- Set typ='oauth-id-jag+jwt' in the minting header per SEP-990
- Keep client SDKs current with the spec
- Inspect token typ during client debugging
When it happens
Trigger: validate() receives an assertion whose header dict's typ is missing or is anything other than 'oauth-id-jag+jwt' — e.g. 'JWT', 'at+jwt', or absent.
Common situations: Client accidentally sends a regular access token or ID token instead of the identity assertion; an IdP or client SDK version that stamps typ=JWT; manual token construction omitting the typ header.
Related errors
- Invalid client_assertion_type: expected {JWT_BEARER_ASSERTIO
- Missing client_assertion
- Invalid client assertion: {e}
- Unsupported JWK key type: {key_type!r}
- CIMD document must have jwks_uri or jwks for private_key_jwt
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/f3ac2b2b6629ad7c.
Report an issue: GitHub.