Sha256: c0620919914c3bf1f91930b126e746c922142bcc1b90a5b9203cbdb21352a366

Contents?: true

Size: 1.79 KB

Versions: 32

Compression:

Stored size: 1.79 KB

Contents

module Recurly
  module Webhooks
    DEFAULT_TOLERANCE = 5 * 60 * 1000

    # Verify webhook signature
    #
    # @param header [String] recurly-signature header from request
    # @param secret [String] Shared secret for notification endpoint
    # @param body [String] Request POST body
    # @param tolerance [Integer] Allowed notification time drift in milliseconds
    # @example
    #   begin
    #     Recurly::Webhooks.verify_signature(header,
    #                                        ENV['WEBHOOKS_KEY'],
    #                                        request.body)
    #   rescue Recurly::Errors::SignatureVerificationError => e
    #     puts e.message
    #   end
    #
    def self.verify_signature(header, secret, body, tolerance: DEFAULT_TOLERANCE)
      s_timestamp, *signatures = header.split(",")
      timestamp = Integer(s_timestamp)
      now = (Time.now.to_f * 1000).to_i

      if (now - timestamp).abs > tolerance
        raise Recurly::Errors::SignatureVerificationError.new(
          "Notification (#{Time.at(timestamp / 1000.0)}) is more than #{tolerance / 1000.0}s out of date"
        )
      end

      expected = OpenSSL::HMAC.hexdigest("sha256", secret, "#{timestamp}.#{body}")

      unless signatures.any? { |s| secure_compare(expected, s) }
        raise Recurly::Errors::SignatureVerificationError.new(
          "No matching signatures found for payload"
        )
      end
    end

    # https://github.com/rack/rack/blob/2-2-stable/lib/rack/utils.rb#L374
    # https://github.com/heartcombo/devise/blob/4-1-stable/lib/devise.rb#L477
    def self.secure_compare(a, b)
      return false if a.bytesize != b.bytesize
      l = a.unpack("C#{a.bytesize}")

      res = 0
      b.each_byte { |byte| res |= byte ^ l.shift }
      res == 0
    end
    private_class_method :secure_compare
  end
end

Version data entries

32 entries across 32 versions & 1 rubygems

Version Path
recurly-4.56.0 lib/recurly/webhooks.rb
recurly-4.55.0 lib/recurly/webhooks.rb
recurly-4.54.0 lib/recurly/webhooks.rb
recurly-4.53.0 lib/recurly/webhooks.rb
recurly-4.52.0 lib/recurly/webhooks.rb
recurly-4.49.0 lib/recurly/webhooks.rb
recurly-4.48.1 lib/recurly/webhooks.rb
recurly-4.48.0 lib/recurly/webhooks.rb
recurly-4.47.0 lib/recurly/webhooks.rb
recurly-4.46.0 lib/recurly/webhooks.rb
recurly-4.45.0 lib/recurly/webhooks.rb
recurly-4.44.0 lib/recurly/webhooks.rb
recurly-4.43.0 lib/recurly/webhooks.rb
recurly-4.42.0 lib/recurly/webhooks.rb
recurly-4.41.0 lib/recurly/webhooks.rb
recurly-4.40.0 lib/recurly/webhooks.rb
recurly-4.39.0 lib/recurly/webhooks.rb
recurly-4.38.0 lib/recurly/webhooks.rb
recurly-4.37.0 lib/recurly/webhooks.rb
recurly-4.36.0 lib/recurly/webhooks.rb