{"record":{"id":"9a05366f4d4e4367","repo":"arsduo/koala","slug":"invalid-incomplete-signature-data","errorCode":null,"errorMessage":"Invalid (incomplete) signature data","messagePattern":"Invalid \\(incomplete\\) signature data","errorType":"validation","errorClass":"Koala::Facebook::OAuthSignatureError","httpStatus":null,"severity":"error","filePath":"lib/koala/oauth.rb","lineNumber":240,"sourceCode":"      # @param (see #exchange_access_token_info)\n      #\n      # @return A new access token or the existing one, set to expire in 60 days.\n      def exchange_access_token(access_token, options = {})\n        if info = exchange_access_token_info(access_token, options)\n          info[\"access_token\"]\n        end\n      end\n\n      # Parses a signed request string provided by Facebook to canvas apps or in a secure cookie.\n      #\n      # @param input the signed request from Facebook\n      #\n      # @raise OAuthSignatureError if the signature is incomplete, invalid, or using an unsupported algorithm\n      #\n      # @return a hash of the validated request information\n      def parse_signed_request(input)\n        encoded_sig, encoded_envelope = input.split('.', 2)\n        raise OAuthSignatureError, 'Invalid (incomplete) signature data' unless encoded_sig && encoded_envelope\n\n        signature = base64_url_decode(encoded_sig).unpack(\"H*\").first\n        envelope = JSON.parse(base64_url_decode(encoded_envelope))\n\n        raise OAuthSignatureError, \"Unsupported algorithm #{envelope['algorithm']}\" if envelope['algorithm'] != 'HMAC-SHA256'\n\n        # now see if the signature is valid (digest, key, data)\n        hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @app_secret, encoded_envelope)\n        raise OAuthSignatureError, 'Invalid signature' if (signature != hmac)\n\n        envelope\n      end\n\n      protected\n\n      def get_token_from_server(args, post = false, options = {})\n        # fetch the result from Facebook's servers\n        response = fetch_token_string(args, post, \"access_token\", options)","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/arsduo/koala/blob/47d052063ef8b5644fb59e279da0b52687999f55/lib/koala/oauth.rb#L222-L258","documentation":"Koala::Facebook::OAuthSignatureError is raised by OAuth#parse_signed_request when the input lacks the two-part signature.payload shape: input.split(\".\", 2) must yield both an encoded signature and an encoded envelope. If the string contains no dot, encoded_envelope is nil and the method raises Invalid (incomplete) signature data rather than guessing. This is the entry guard for every Facebook signed_request value, including the fbsr_<app_id> cookie parsed by parse_signed_cookie and get_user_info_from_cookies.","triggerScenarios":"Passing a string without a dot separator to parse_signed_request or get_user_info_from_cookies: a truncated fbsr_<app_id> cookie (proxy mangling or cookie-size limits), the wrong cookie value (for example the unsigned fbs_ cookie), a signed_request that was URL-decoded or otherwise altered in transit, or a test fixture that is not a real signature.envelope pair. Nil input raises NoMethodError instead; this error specifically means a non-empty string missing the dot.","commonSituations":"Cookie values truncated between browser and app; passing the whole cookie hash instead of the fbsr_ value; double URL-encoding when relaying signed_request through internal routes; recorded fixtures that predate a Facebook format change; monitoring that groups this with tampering attempts when it is usually client-side corruption.","solutions":["Pass the untouched value of cookies[\"fbsr_#{@app_id}\"] or the raw signed_request parameter straight to the parser; do not pre-decode, strip, or re-encode it.","Validate the shape before parsing: the value must match the pattern of base64url characters joined by exactly one dot.","Log the offending value (length and prefix) to find where it gets mangled in transit.","Rescue Koala::Facebook::OAuthSignatureError in the login flow and treat the session as anonymous."],"exampleFix":"// before\nauth = @oauth.parse_signed_request(params[:signed_request])\n\n// after\nraw = params[:signed_request].to_s\nif raw =~ /\\A[\\w\\-]+\\.[\\w\\-]+\\z/\n  auth = @oauth.parse_signed_request(raw)\nelse\n  auth = nil # malformed payload: treat as signed out\nend","handlingStrategy":"validation","validationCode":"cookie = cookies[\"fbsr_#{APP_ID}\"]\nhead :unauthorized unless cookie.is_a?(String) && cookie =~ /\\A[\\w\\-]+\\.[\\w\\-]+\\z/","typeGuard":"def plausible_signed_request?(input)\n  input.is_a?(String) && !input.empty? && input.match?(\"\\A[\\w\\-]+\\.[\\w\\-]+\\z\")\nend","tryCatchPattern":"begin\n  data = @oauth.parse_signed_request(raw)\nrescue Koala::Facebook::OAuthSignatureError\n  render json: { error: \"invalid signed_request\" }, status: :unauthorized\nend","preventionTips":["Hand the raw fbsr_ cookie value to get_user_info_from_cookies; never split or decode it yourself","Reject mangled payloads at the routing layer with a shape constraint before they reach auth code","Keep the cookie jar small enough that the fbsr_ cookie is never dropped or truncated"],"tags":["oauth","signed-request","cookie","signature","koala","facebook"],"backgroundTag":"malformed-signed-request","analyzedSha":"47d052063ef8b5644fb59e279da0b52687999f55","analyzedAt":"2026-08-23T10:19:03.891Z","schemaVersion":2},"datasetVersion":"2026-08-23T13:39:53.451Z"}