{"record":{"id":"5d7c916c3b9b8aa2","repo":"arsduo/koala","slug":"invalid-signature","errorCode":null,"errorMessage":"Invalid signature","messagePattern":"Invalid signature","errorType":"validation","errorClass":"Koala::Facebook::OAuthSignatureError","httpStatus":null,"severity":"error","filePath":"lib/koala/oauth.rb","lineNumber":249,"sourceCode":"      # 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)\n        parse_access_token(response)\n      end\n\n      def parse_access_token(response_text)\n        JSON.parse(response_text)\n      rescue JSON::ParserError\n        response_text.split(\"&\").inject({}) do |hash, bit|\n          key, value = bit.split(\"=\")\n          hash.merge!(key => value)","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/arsduo/koala/blob/47d052063ef8b5644fb59e279da0b52687999f55/lib/koala/oauth.rb#L231-L267","documentation":"The last step of OAuth#parse_signed_request recomputes HMAC-SHA256 over the exact encoded envelope string using @app_secret and compares it to the decoded signature. A mismatch raises OAuthSignatureError with message Invalid signature: the payload was not signed by the secret this OAuth object holds. Tampering is one cause, but in practice the common cause is a credential mismatch: the signed_request (the fbsr_<app_id> cookie) was issued for a different app than the app_id and app_secret pair used to build the OAuth object.","triggerScenarios":"get_user_info_from_cookies or parse_signed_request with an fbsr_ cookie from app A while OAuth was constructed with the secret of app B; a secret with a typo, trailing whitespace, or pasted quotes; a secret rotated in the Facebook app dashboard but not yet in your config; a signed_request whose envelope was modified (even letter-case changes break the HMAC because it covers the exact encoded bytes).","commonSituations":"Dev and production credentials crossed through ENV; several Facebook apps in one codebase; a browser holding an fbsr_ cookie from an older app after an app-id migration; secrets loaded from YAML with stray characters.","solutions":["Confirm the app the cookie belongs to (the cookie name embeds it as fbsr_<app_id>) matches the app_id passed to Koala::Facebook::OAuth.new, and that the secret is the current secret of that same app.","Check how the secret is loaded: strip whitespace and quotes, and log its length (never its value) to confirm which ENV variable was used.","If the secret was rotated in the dashboard, roll the new value out and redeploy.","Rescue OAuthSignatureError, clear the stale fbsr_ cookie, and let the Facebook JS SDK write a fresh one at the next login."],"exampleFix":"// before\n@oauth = Koala::Facebook::OAuth.new(ENV[\"FB_APP_ID\"], ENV[\"FB_APP_SECRET\"])\nauth = @oauth.get_user_info_from_cookies(cookies.to_h)\n\n// after\n@oauth = Koala::Facebook::OAuth.new(\n  ENV.fetch(\"FB_APP_ID\").strip,\n  ENV.fetch(\"FB_APP_SECRET\").strip,\n  ENV.fetch(\"FB_CALLBACK_URL\")\n)\nbegin\n  auth = @oauth.get_user_info_from_cookies(cookies.to_h)\nrescue Koala::Facebook::OAuthSignatureError\n  cookies.delete(\"fbsr_#{ENV.fetch(\"FB_APP_ID\")}\")\n  auth = nil\nend","handlingStrategy":"try-catch","validationCode":"def facebook_oauth\n  app_id = ENV.fetch(\"FB_APP_ID\").strip\n  secret = ENV.fetch(\"FB_APP_SECRET\").strip\n  raise ArgumentError, \"Facebook credentials incomplete\" if app_id.empty? || secret.empty?\n  Koala::Facebook::OAuth.new(app_id, secret, ENV.fetch(\"FB_CALLBACK_URL\"))\nend","typeGuard":null,"tryCatchPattern":"begin\n  session[:fb_auth] = @oauth.get_user_info_from_cookies(cookies.to_h)\nrescue Koala::Facebook::OAuthSignatureError\n  cookies.delete(\"fbsr_#{APP_ID}\") # stale or foreign cookie: force fresh login\n  session[:fb_auth] = nil\nend","preventionTips":["Load Facebook credentials once at boot from a single stripped, validated source and fail fast on empty values","Key OAuth instances by app_id when running multiple apps, and pick the instance from the incoming cookie name","Never normalize, decode, or downcase a signed_request before verification; the HMAC covers the exact envelope bytes"],"tags":["oauth","hmac","signature","app-secret","cookie","koala","facebook"],"backgroundTag":"hmac-signature-mismatch","analyzedSha":"47d052063ef8b5644fb59e279da0b52687999f55","analyzedAt":"2026-08-23T10:19:03.891Z","schemaVersion":2},"datasetVersion":"2026-08-23T13:39:53.451Z"}