Romuloux
Romuloux

Reputation: 1183

Stripe Webhook Invalid Signature in Test

Stripe::Webhook.construct_event is raising Stripe::SignatureVerificationError when running request specs with rspec.

Using byebug Stripe::Webhook::Signature.verify_header is returning true. After continuing, the exception Stripe::SignatureVerificationError is raised.

From reviewing the source, it seems that the first call in Stripe::Webhook.construct_event is Stripe::Webhook::Signature.verify_header.

Why would the call in the debug console return true but apparently return false when it is called in .construct_event?

Webhook Controller

class WebHooks::StripeController < WebHooksController

  # Entry point for Stripe webhooks. This method
  # will verify the signature and dispatch to the
  # appropriate method. It will log warning if
  # the webhook type is unknown. The method dispatched is the
  # webhook type with underscores instead of dots.
  def create
    payload = request.body.read
    sig_header = request.headers['Stripe-Signature']
    event = nil
    byebug
    # Byebug Console
    Stripe::Webhook::Signature.verify_header(payload, sig_header, Rails.application.credentials.stripe[:signing_secret]) 
    # => True, this returns true

    begin
      event = Stripe::Webhook.construct_event(
        payload, sig_header, Rails.application.credentials.stripe[:signing_secret]
      )
    rescue JSON::ParserError => e
      # Invalid payload
      head :unprocessable_entity
      return
    rescue Stripe::SignatureVerificationError => e
      # Invalid signature
      Rails.logger.error("⚠️  Stripe signature verification failed.")
      head :unauthorized
      return
    end

    type = event.type.gsub('.', '_')

    begin
      public_send(type)
    rescue NoMethodError
      Rails.logger.warn("Unknown webhook type: #{params[:type]}")
      head :unprocessable_entity
    end
  end

end

Spec

require 'rails_helper'

RSpec.describe "WebHooks::Stripe::Signature", type: :request do

  context "with a valid signature" do
    it "returns 200" do
      event = { type: "not_implemented" }
      headers = {
        "Stripe-Signature" => stripe_event_signature(event.to_json)
      }
      post "/web_hooks/stripe", params: event.to_json, headers: headers
      expect(response).to have_http_status(200) # This fails
    end
  end

  context "an invalid signature" do
    it "returns 401" do
      post "/web_hooks/stripe", params: { type: "not_implemented" }
      expect(response).to have_http_status(401)
    end
  end

end

Stripe Helper

module StripeTestHelper
  def stripe_event_signature(payload)
    time = Time.now
    secret = Rails.application.credentials.stripe[:signing_secret]
    signature = Stripe::Webhook::Signature.compute_signature(time, payload, secret)
    Stripe::Webhook::Signature.generate_header(
      time,
      signature,
      scheme: Stripe::Webhook::Signature::EXPECTED_SCHEME
    )
  end

end

Upvotes: 1

Views: 2557

Answers (2)

Red Rojas
Red Rojas

Reputation: 1

Double-check your webhook signing secret, must start with a whsec_...

Upvotes: 0

pgs
pgs

Reputation: 1201

There are a couple reasons as to why you might be getting this, Stripe::SignatureVerificationError error. The first being that a wrong value for your webhook signing secret, which would look something like whsec_, was used. You can retrieve the correct one from your Dashboard by clicking on ‘Reveal’.

If you've confirmed that you've used the right secret, then the issue might be on the payload's content which likely is the issue here. This answer on another SO talks about this in depth. The summary is that Rails for Ruby will tamper with the raw payload and if it's not identical to what Stripe sent you, the signature won't be a match.

As for next steps, you'd want try request.raw_post or find a similar solution to get to the exact raw JSON sent to you.

Upvotes: 3

Related Questions