{"record":{"id":"5dd3a67cb29877a6","repo":"we-promise/sure","slug":"invalid-credential-payload","errorCode":null,"errorMessage":"invalid credential payload","messagePattern":"invalid credential payload","errorType":"http","errorClass":"ActionController::BadRequest","httpStatus":400,"severity":"error","filePath":"app/controllers/concerns/webauthn_relying_party.rb","lineNumber":29,"sourceCode":"        name: \"Sure\",\n        id: webauthn_config.rp_id,\n        allowed_origins: webauthn_config.allowed_origins,\n        # Accept consumer passkeys/security keys without attesting device vendor\n        # identity; this keeps MFA registration broad for self-hosted users.\n        verify_attestation_statement: false\n      )\n    end\n\n    def webauthn_credential_payload\n      payload = params.require(:credential)\n      payload = JSON.parse(payload) if payload.is_a?(String)\n\n      payload = payload.to_unsafe_h if payload.respond_to?(:to_unsafe_h)\n      raise ActionController::BadRequest, \"credential must be an object\" unless payload.is_a?(Hash)\n\n      payload\n    rescue JSON::ParserError, TypeError, ArgumentError\n      raise ActionController::BadRequest, \"invalid credential payload\"\n    end\nend\n","sourceCodeStart":11,"sourceCodeEnd":32,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/controllers/concerns/webauthn_relying_party.rb#L11-L32","documentation":"This is the companion guard to \"credential must be an object\" in webauthn_credential_payload. When the credential param arrives as a String, the helper runs JSON.parse on it; if parsing blows up it is rescued (JSON::ParserError, TypeError, ArgumentError) and re-raised as ActionController::BadRequest (HTTP 400) with \"invalid credential payload\". It fires specifically when the string is present and shaped like a string but is not syntactically valid JSON — malformed structure, not wrong shape after parsing.","triggerScenarios":"credential param sent as a truncated JSON string (missing closing brace, e.g. from a URL-truncated form value); single quotes instead of double quotes (credential=\"{'id': 1}\"); unescaped newlines/control characters inside the JSON; a client sending url-encoded JSON that got double-escaped (%7B%22id%22…) so JSON.parse sees percent-encoded junk; nil propagated into JSON.parse raises TypeError and lands here too.","commonSituations":"Hand-rolled mobile clients or curl scripts with shell quoting bugs; proxies/WAFs that rewrite or truncate long request bodies (passkey payloads are large); frontend code that concatenates the credential JSON into a FormData value without encoding; a test that builds the string with string interpolation producing invalid JSON.","solutions":["Validate the string client-side before sending: JSON.parse(credentialStr) must succeed and yield an object","Send via fetch with JSON.stringify so escaping is handled by the serializer, never by string concatenation","Check for body size limits / WAF rules if payloads are large passkey responses getting truncated mid-flight","In Ruby clients, generate the payload with JSON.generate rather than heredocs or interpolation"],"exampleFix":"# before (broken client)\nbody = \"credential=\" + json.to_s.tr(\"\\\"\", \"'\") # single-quoted pseudo-JSON\n# => JSON::ParserError rescued -> 400 \"invalid credential payload\"\n\n# after\nbody = { credential: JSON.generate(json) }.to_json\n# server: JSON.parse succeeds -> Hash -> proceeds to WebAuthn verification","handlingStrategy":"validation","validationCode":"# Ruby client, before sending\nrequire \"json\"\n\ndef valid_credential_json?(str)\n  JSON.parse(str).is_a?(Hash)\nrescue JSON::ParserError, TypeError\n  false\nend\n\npayload = JSON.generate(credential) # serialize, never concatenate strings","typeGuard":"def valid_credential_json?(str)\n  JSON.parse(str).is_a?(Hash)\nrescue JSON::ParserError, TypeError\n  false\nend","tryCatchPattern":"rescue ActionController::BadRequest => e\n  # 400 with \"invalid credential payload\": the string was syntactically broken.\n  # Re-serialize client-side with JSON.generate and retry once.\n  head :bad_request\nend","preventionTips":["Generate JSON with a serializer (JSON.generate / JSON.stringify), never string interpolation","Avoid shell heredocs for payloads; use files written by the serializer","Watch for proxies/WAFs truncating large passkey payloads","Retry once only after re-serializing; identical retries of broken JSON always fail"],"tags":["rails","webauthn","json","malformed-payload","bad-request"],"backgroundTag":"malformed-json-payload","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}