arsduo/koala · error · Koala::Facebook::OAuthSignatureError
Invalid signature
Error message
Invalid signature
What it means
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.
Source
Thrown at lib/koala/oauth.rb:249
# Parses a signed request string provided by Facebook to canvas apps or in a secure cookie.
#
# @param input the signed request from Facebook
#
# @raise OAuthSignatureError if the signature is incomplete, invalid, or using an unsupported algorithm
#
# @return a hash of the validated request information
def parse_signed_request(input)
encoded_sig, encoded_envelope = input.split('.', 2)
raise OAuthSignatureError, 'Invalid (incomplete) signature data' unless encoded_sig && encoded_envelope
signature = base64_url_decode(encoded_sig).unpack("H*").first
envelope = JSON.parse(base64_url_decode(encoded_envelope))
raise OAuthSignatureError, "Unsupported algorithm #{envelope['algorithm']}" if envelope['algorithm'] != 'HMAC-SHA256'
# now see if the signature is valid (digest, key, data)
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @app_secret, encoded_envelope)
raise OAuthSignatureError, 'Invalid signature' if (signature != hmac)
envelope
end
protected
def get_token_from_server(args, post = false, options = {})
# fetch the result from Facebook's servers
response = fetch_token_string(args, post, "access_token", options)
parse_access_token(response)
end
def parse_access_token(response_text)
JSON.parse(response_text)
rescue JSON::ParserError
response_text.split("&").inject({}) do |hash, bit|
key, value = bit.split("=")
hash.merge!(key => value)View on GitHub (pinned to 47d052063e)
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.
Example fix
// before
@oauth = Koala::Facebook::OAuth.new(ENV["FB_APP_ID"], ENV["FB_APP_SECRET"])
auth = @oauth.get_user_info_from_cookies(cookies.to_h)
// after
@oauth = Koala::Facebook::OAuth.new(
ENV.fetch("FB_APP_ID").strip,
ENV.fetch("FB_APP_SECRET").strip,
ENV.fetch("FB_CALLBACK_URL")
)
begin
auth = @oauth.get_user_info_from_cookies(cookies.to_h)
rescue Koala::Facebook::OAuthSignatureError
cookies.delete("fbsr_#{ENV.fetch("FB_APP_ID")}")
auth = nil
end Defensive patterns
Strategy: try-catch
Validate before calling
def facebook_oauth
app_id = ENV.fetch("FB_APP_ID").strip
secret = ENV.fetch("FB_APP_SECRET").strip
raise ArgumentError, "Facebook credentials incomplete" if app_id.empty? || secret.empty?
Koala::Facebook::OAuth.new(app_id, secret, ENV.fetch("FB_CALLBACK_URL"))
end Try / catch
begin
session[:fb_auth] = @oauth.get_user_info_from_cookies(cookies.to_h)
rescue Koala::Facebook::OAuthSignatureError
cookies.delete("fbsr_#{APP_ID}") # stale or foreign cookie: force fresh login
session[:fb_auth] = nil
end Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Invalid (incomplete) signature data
- You must init RealtimeUpdates with your app secret in order
- Unsupported algorithm #{envelope['algorithm']}
- Koala::Utils.logger.warn("Signed cookie didn't contain Faceb
- generate_client_code received an error: empty response body
AI-assisted analysis of arsduo/koala@47d052063e (2026-08-23).
Data as JSON: /api/errors/5d7c916c3b9b8aa2.
Report an issue: GitHub.