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
end

View on GitHub (pinned to 47d052063e)

Solutions

  1. 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.
  2. Or ensure Koala.config.app_secret is set for the process that handles webhook callbacks.
  3. 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

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


AI-assisted analysis of arsduo/koala@47d052063e (2026-08-23). Data as JSON: /api/errors/9d182cc9cb42a1ac. Report an issue: GitHub.