arsduo/koala · error · Koala::Facebook::AppSecretNotDefinedError
You must init RealtimeUpdates with your app secret in order
Error message
You must init RealtimeUpdates with your app secret in order to validate updates
What it means
RealtimeUpdates#validate_update verifies webhook deliveries by computing HMAC-SHA1 over the raw body keyed with the application secret and comparing it against the X-Hub-Signature header (or HTTP_X_HUB_SIGNATURE). An app access token can manage subscriptions but cannot produce that HMAC, so when the instance was initialized with only :app_access_token and @secret is nil, the method raises Koala::Facebook::AppSecretNotDefinedError before any crypto runs.
Source
Thrown at lib/koala/realtime_updates.rb:133
false
end
end
# Public: As a security measure, all updates from facebook are signed using
# X-Hub-Signature: sha1=XXXX where XXX is the sha1 of the json payload
# using your application secret as the key.
#
# Example:
# # in Rails controller
# # @oauth being a previously defined Koala::Facebook::OAuth instance
# def receive_update
# if @oauth.validate_update(request.body, headers)
# ...
# end
# end
def validate_update(body, headers)
unless @secret
raise AppSecretNotDefinedError, "You must init RealtimeUpdates with your app secret in order to validate updates"
end
request_signature = headers['X-Hub-Signature'] || headers['HTTP_X_HUB_SIGNATURE']
return unless request_signature
signature_parts = request_signature.split("sha1=")
request_signature = signature_parts[1]
calculated_signature = OpenSSL::HMAC.hexdigest('sha1', @secret, body)
calculated_signature == request_signature
end
# The Facebook subscription management URL for your application.
def subscription_path
@subscription_path ||= "#{@app_id}/subscriptions"
end
end
end
endView on GitHub (pinned to 47d052063e)
Solutions
- Initialize with the secret in addition to the token: RealtimeUpdates.new(app_id: id, app_access_token: token, secret: ENV["FACEBOOK_APP_SECRET"]); subscriptions keep using the token.
- Or ensure Koala.config.app_secret is set for the process that handles webhook callbacks.
- Read the raw body into a string once and pass that same string to validate_update; some servers do not rewind request.body.
Example fix
// before
@rtu = Koala::Facebook::RealtimeUpdates.new(app_id: APP_ID, app_access_token: APP_TOKEN)
verified = @rtu.validate_update(request.body.read, request.headers)
// after
@rtu = Koala::Facebook::RealtimeUpdates.new(
app_id: APP_ID,
app_access_token: APP_TOKEN,
secret: ENV.fetch("FACEBOOK_APP_SECRET")
)
body = request.body.read
verified = @rtu.validate_update(body, request.headers) Defensive patterns
Strategy: validation
Validate before calling
def webhook_client
secret = ENV["FACEBOOK_APP_SECRET"].to_s
raise ArgumentError, "FACEBOOK_APP_SECRET required for webhook validation" if secret.empty?
Koala::Facebook::RealtimeUpdates.new(app_id: ENV.fetch("FACEBOOK_APP_ID"), secret: secret)
end Type guard
def can_validate_updates?(rtu) rtu.respond_to?(:secret) && !rtu.secret.to_s.empty? end
Try / catch
begin verified = @rtu.validate_update(body, request.headers) rescue Koala::Facebook::AppSecretNotDefinedError head :internal_server_error # config gap: alert; never accept unverified updates end
Prevention
- Always pass :secret alongside :app_access_token when the same instance will validate webhooks; a token cannot sign
- Assert the secret is present in a boot-time config check on webhook-serving processes
- Remember validate_update returns nil when the header is absent; treat that as unverified, not accepted
When it happens
Trigger: RealtimeUpdates.new(app_id: id, app_access_token: token), the token-only setup that suffices for subscribe and list_subscriptions, followed by validate_update(request.body.read, request.headers) in a webhook controller. Also when Koala.config supplies app_access_token but no app_secret for the process handling webhooks.
Common situations: Webhook verification added after the subscription code was already written against a token; per-environment config that deliberately withholds the secret from web workers; substituting an OAuth app token where the secret is required.
Related errors
- Invalid signature
- Invalid (incomplete) signature data
- Unsupported algorithm #{envelope['algorithm']}
- Initialize must receive a hash with :app_id and either :app_
- generate_client_code received an error: empty response body
AI-assisted analysis of arsduo/koala@47d052063e (2026-08-23).
Data as JSON: /api/errors/9d182cc9cb42a1ac.
Report an issue: GitHub.