{"record":{"id":"6489a83a774c7a31","repo":"basecamp/fizzy","slug":"authenticator-data-is-too-short","errorCode":null,"errorMessage":"Authenticator data is too short","messagePattern":"Authenticator data is too short","errorType":"exception","errorClass":"ActionPack::WebAuthn::InvalidResponseError","httpStatus":null,"severity":"error","filePath":"lib/action_pack/web_authn/authenticator/data.rb","lineNumber":83,"sourceCode":"    def wrap(data)\n      if data.is_a?(self)\n        data\n      else\n        data = Base64.urlsafe_decode64(data) unless data.encoding == Encoding::BINARY\n        decode(data)\n      end\n    rescue ArgumentError\n      raise ActionPack::WebAuthn::InvalidResponseError, \"Invalid base64 encoding in authenticator data\"\n    end\n\n    # Decodes raw authenticator data bytes into a Data instance, parsing the\n    # RP ID hash, flags, sign count, and (if present) attested credential data.\n    def decode(bytes)\n      bytes = bytes.bytes if bytes.is_a?(String)\n\n      minimum_length = RELYING_PARTY_ID_HASH_LENGTH + FLAGS_LENGTH + SIGN_COUNT_LENGTH\n      if bytes.length < minimum_length\n        raise ActionPack::WebAuthn::InvalidResponseError, \"Authenticator data is too short\"\n      end\n\n      position = 0\n\n      relying_party_id_hash = bytes[position, RELYING_PARTY_ID_HASH_LENGTH].pack(\"C*\")\n      position += RELYING_PARTY_ID_HASH_LENGTH\n\n      flags = bytes[position]\n      position += FLAGS_LENGTH\n\n      sign_count = bytes[position, SIGN_COUNT_LENGTH].pack(\"C*\").unpack1(\"N\")\n      position += SIGN_COUNT_LENGTH\n\n      aaguid = nil\n      credential_id = nil\n      public_key_bytes = nil\n\n      if flags & ATTESTED_CREDENTIAL_DATA_FLAG != 0","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/basecamp/fizzy/blob/7aabe7458060d8a1759a53b7ede39e74e6c0b20d/lib/action_pack/web_authn/authenticator/data.rb#L65-L101","documentation":"Authenticator::Data.decode requires at least 37 bytes: 32-byte RP ID hash + 1-byte flags + 4-byte sign count (RELYING_PARTY_ID_HASH_LENGTH + FLAGS_LENGTH + SIGN_COUNT_LENGTH). Input below that cannot be valid authenticator data, so the parser rejects it immediately with InvalidResponseError.","triggerScenarios":"Passing a base64url string that decodes to fewer than 37 bytes — empty string, a truncated value cut by a client or proxy, a lone SHA-256 hash, or handing the wrong field (e.g. the challenge or the RP ID hash) where authenticator data is expected.","commonSituations":"Unit-test fixtures fabricated from short strings instead of a real WebAuthn ceremony; a frontend that slices/substring's the authenticatorData buffer; JSON payloads truncated by request-size limits; double-decoding (decoding base64 twice yields short garbage).","solutions":["Check the decoded byte length in console: Base64.urlsafe_decode64(param).bytesize must be >= 37 (assertion) or >= 37+18+credential-id+key bytes (registration).","Log bytesize at the trust boundary to confirm where truncation happens (client vs transport vs server).","Regenerate fixtures from a real browser passkey ceremony (Chrome DevTools or a WebAuthn test harness) instead of hand-writing strings.","Verify you are passing the authenticatorData field itself, not challenge, userHandle, or clientDataJSON."],"exampleFix":"# before\ndata = ActionPack::WebAuthn::Authenticator::Data.wrap(params[:authenticator_data])\n\n# after — cheap pre-flight length check\nraw = Base64.urlsafe_decode64(params[:authenticator_data].to_s)\nif raw.bytesize < 37\n  return render json: { error: \"authenticator data truncated\" }, status: :bad_request\nend\ndata = ActionPack::WebAuthn::Authenticator::Data.wrap(raw)","handlingStrategy":"validation","validationCode":"raw = Base64.urlsafe_decode64(params[:authenticator_data].to_s)\nreturn render(json: { error: 'authenticator data too short' }, status: :bad_request) if raw.bytesize < 37","typeGuard":"def plausible_authenticator_data?(value)\n  return false unless (decoded = Base64.urlsafe_decode64(value) rescue nil)\n  decoded.bytesize >= 37\nend","tryCatchPattern":"begin\n  data = ActionPack::WebAuthn::Authenticator::Data.wrap(raw)\nrescue ActionPack::WebAuthn::InvalidResponseError => e\n  render json: { error: e.message }, status: :bad_request\nend","preventionTips":["Log decoded bytesize for every WebAuthn request during development to catch truncation at the edge.","Keep fixtures generated from real ceremonies, never hand-typed strings.","Map every InvalidResponseError to HTTP 400 with the library message — client input is not retryable."],"tags":["webauthn","binary-parsing","truncated-data","passkeys"],"backgroundTag":"webauthn-malformed-authenticator-data","analyzedSha":"7aabe7458060d8a1759a53b7ede39e74e6c0b20d","analyzedAt":"2026-08-21T18:33:25.349Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}