{"record":{"id":"4b38e890e0a0817e","repo":"PrefectHQ/fastmcp","slug":"assertion-payload-is-not-a-json-object","errorCode":null,"errorMessage":"Assertion payload is not a JSON object","messagePattern":"Assertion payload is not a JSON object","errorType":"exception","errorClass":"IdentityAssertionError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/identity_assertion.py","lineNumber":370,"sourceCode":"            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\")\n        nbf = _numeric_date_claim(claims, \"nbf\")\n        if exp is None:","sourceCodeStart":352,"sourceCodeEnd":388,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/identity_assertion.py#L352-L388","documentation":"The assertion payload decoded as JSON but is not an object (array, string, number, etc.), so claim lookup via .get() would fail. FastMCP guards this to return invalid_grant rather than a 500.","triggerScenarios":"validate() receives an assertion whose claims segment decodes to a non-dict JSON value — `if not isinstance(unverified_claims, dict)` fires.","commonSituations":"Custom/broken token minters that serialize claims as a list or string; fuzzed or maliciously crafted tokens; corrupted stored tokens.","solutions":["Re-mint the assertion so its payload is a JSON object containing iss, aud, exp, sub, etc.","Base64url-decode the payload segment and confirm it is `{...}`.","Fix custom token-generation code that json-dumps a non-dict claims value.","If a third party issues such tokens, reject upstream and report to that provider."],"exampleFix":"// before (invalid claims payload)\n[\"sub\",\"alice\"]\n// after\n{\"iss\": \"https://idp.example.com\", \"sub\": \"alice\", \"exp\": 1735689600}","handlingStrategy":"type-guard","validationCode":"import base64, json\nclaims = json.loads(base64.urlsafe_b64decode(assertion.split('.')[1] + '=='))\nassert isinstance(claims, dict), 'claims must be a JSON object'","typeGuard":"def claims_are_object(token: str) -> bool:\n    import base64, json\n    c = json.loads(base64.urlsafe_b64decode(token.split('.')[1] + '=='))\n    return isinstance(c, dict)","tryCatchPattern":"try:\n    await provider.validate(assertion)\nexcept IdentityAssertionError as e:\n    if 'payload is not a JSON object' in str(e):\n        log.warning('malformed claims; reject token at source')\n    raise","preventionTips":["Mint claims as a dict with standard JWT libraries","Validate claims shape before sending","Reject non-object payloads in client-side checks","Audit custom token-minting code for json.dumps of non-dicts"],"tags":["jwt","malformed","json","schema"],"backgroundTag":"malformed-jwt-payload","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}