{"record":{"id":"a1a4751bd4893880","repo":"PrefectHQ/fastmcp","slug":"malformed-assertion-payload-e","errorCode":null,"errorMessage":"Malformed assertion payload: {e}","messagePattern":"Malformed assertion payload: (.+?)","errorType":"exception","errorClass":"IdentityAssertionError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/identity_assertion.py","lineNumber":368,"sourceCode":"        # 1. typ header MUST be oauth-id-jag+jwt (SEP-990 §5.1).\n        try:\n            header = decode_jwt_header(assertion)\n        except (ValueError, KeyError, IndexError) as e:\n            raise IdentityAssertionError(f\"Malformed assertion header: {e}\") from e\n        if not isinstance(header, dict):\n            # A JSON-array/scalar header is valid JSON but not a JOSE header;\n            # guard before .get() so this maps to invalid_grant, not a 500.\n            raise IdentityAssertionError(\"Assertion JOSE header must be a JSON object\")\n        if header.get(\"typ\") != ID_JAG_TYP:\n            raise IdentityAssertionError(\n                f\"Assertion typ must be {ID_JAG_TYP!r}, got {header.get('typ')!r}\"\n            )\n\n        # 2. iss must be a trusted issuer before we fetch any keys for it.\n        try:\n            unverified_claims = _decode_unverified_claims(assertion)\n        except (ValueError, KeyError, IndexError) as e:\n            raise IdentityAssertionError(f\"Malformed assertion payload: {e}\") from e\n        if not isinstance(unverified_claims, dict):\n            raise IdentityAssertionError(\"Assertion payload is not a JSON object\")\n        iss = unverified_claims.get(\"iss\")\n        if not iss or iss not in self.config.trusted_issuers:\n            raise IdentityAssertionError(f\"Untrusted assertion issuer: {iss!r}\")\n\n        # 3. Verify signature, iss, aud, and exp via JWTVerifier.\n        verifier = await self._get_verifier(iss)\n        access_token = await verifier.load_access_token(assertion)\n        if access_token is None:\n            raise IdentityAssertionError(\n                \"Assertion failed signature/issuer/audience/expiry validation\"\n            )\n        claims = access_token.claims\n\n        now = time.time()\n        exp = _numeric_date_claim(claims, \"exp\")\n        iat = _numeric_date_claim(claims, \"iat\")","sourceCodeStart":350,"sourceCodeEnd":386,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/identity_assertion.py#L350-L386","documentation":"The assertion's payload (claims) segment could not be decoded as a JSON object: base64/JSON decode failed or the required keys were absent while extracting claims unverified. FastMCP must read `iss` from the payload to select a trusted issuer before verifying the signature.","triggerScenarios":"validate() receives an assertion whose second dot-delimited segment is not valid base64url JSON, or _decode_unverified_claims raises ValueError/KeyError/IndexError.","commonSituations":"Truncated or corrupted tokens in transit; the wrong token type passed as the assertion; tokens mangled by logging/serialization round-trips; hand-built test tokens.","solutions":["Regenerate the assertion client-side with a standard JWT library and resend; check for truncation in transport (header size limits, proxy rewriting).","Confirm the client sends the complete id-jag assertion, not a fragment or a different token.","Decode the payload locally (jwt.io or PyJWT) to see what is malformed.","Check middleware/proxies that might rewrite or truncate Authorization/body fields."],"exampleFix":"// before: re-encoding the token\nassertion = base64.b64encode(raw_token).decode()\n// after: pass the JWT string as-is\nassertion = raw_token.decode()  # original 'header.payload.signature' string","handlingStrategy":"validation","validationCode":"import base64, json\ndef payload_parses(t: str) -> bool:\n    try:\n        json.loads(base64.urlsafe_b64decode(t.split('.')[1] + '=='))\n        return True\n    except Exception:\n        return False","typeGuard":"def has_valid_payload_segment(token: str) -> bool:\n    import base64, json\n    parts = token.split('.')\n    if len(parts) != 3:\n        return False\n    try:\n        return isinstance(json.loads(base64.urlsafe_b64decode(parts[1] + '==')), dict)\n    except Exception:\n        return False","tryCatchPattern":"try:\n    await provider.validate(assertion)\nexcept IdentityAssertionError as e:\n    if 'Malformed assertion payload' in str(e):\n        log.warning('assertion payload unreadable; request fresh token from client')\n    raise","preventionTips":["Avoid token transformations in transit (no re-encoding, splitting, trimming)","Check proxies/middleware for rewriting token bodies","Request a fresh assertion when payloads fail to decode","Round-trip test tokens through your exact transport"],"tags":["jwt","malformed","parsing"],"backgroundTag":"malformed-jwt-payload","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}