Module: Stripe::Webhook::Signature
- Defined in:
- lib/stripe/webhook.rb
Constant Summary collapse
- EXPECTED_SCHEME =
"v1"
Class Method Summary collapse
-
.compute_signature(timestamp, payload, secret) ⇒ Object
Computes a webhook signature given a time (probably the current time), a payload, and a signing secret.
-
.generate_header(timestamp, signature, scheme: EXPECTED_SCHEME) ⇒ Object
Compute the
Stripe-Signatureheader for a given webhook body & secret. -
.verify_header(payload, header, secret, tolerance: nil) ⇒ Object
Verifies the authenticity (and recency) of a webhook, raising a
SignatureVerificationErrorif there's a mismatch.
Class Method Details
.compute_signature(timestamp, payload, secret) ⇒ Object
Computes a webhook signature given a time (probably the current time), a payload, and a signing secret.
76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/stripe/webhook.rb', line 76 def self.compute_signature(, payload, secret) raise ArgumentError, "timestamp should be an instance of Time" \ unless .is_a?(Time) raise ArgumentError, "payload should be a string" \ unless payload.is_a?(String) raise ArgumentError, "secret should be a string" \ unless secret.is_a?(String) = "#{.to_i}.#{payload}" OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), secret, ) end |
.generate_header(timestamp, signature, scheme: EXPECTED_SCHEME) ⇒ Object
Compute the Stripe-Signature header for a given webhook body & secret. Useful for signing
payloads in unit tests.
91 92 93 94 95 96 97 98 99 100 |
# File 'lib/stripe/webhook.rb', line 91 def self.generate_header(, signature, scheme: EXPECTED_SCHEME) raise ArgumentError, "timestamp should be an instance of Time" \ unless .is_a?(Time) raise ArgumentError, "signature should be a string" \ unless signature.is_a?(String) raise ArgumentError, "scheme should be a string" \ unless scheme.is_a?(String) "t=#{.to_i},#{scheme}=#{signature}" end |
.verify_header(payload, header, secret, tolerance: nil) ⇒ Object
Verifies the authenticity (and recency) of a webhook, raising a SignatureVerificationError if
there's a mismatch. Useful for quickly validating incoming webhooks before storing them for later
processing (at which time you can use the *_without_verification methods for parsing).
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/stripe/webhook.rb', line 115 def self.verify_header(payload, header, secret, tolerance: nil) begin , signatures = (header, EXPECTED_SCHEME) # TODO: Try to knock over this blanket rescue as it can unintentionally # swallow many valid errors. Instead, try to validate an incoming # header one piece at a time, and error with a known exception class if # any part is found to be invalid. Rescue that class here. rescue StandardError raise SignatureVerificationError.new( "Unable to extract timestamp and signatures from header", header, http_body: payload ) end if signatures.empty? raise SignatureVerificationError.new( "No signatures found with expected scheme #{EXPECTED_SCHEME}", header, http_body: payload ) end expected_sig = compute_signature(, payload, secret) unless signatures.any? { |s| Util.secure_compare(expected_sig, s) } raise SignatureVerificationError.new( "No signatures found matching the expected signature for payload", header, http_body: payload ) end if tolerance && < Time.now - tolerance = Time.at().strftime("%F %T") raise SignatureVerificationError.new( "Timestamp outside the tolerance zone (#{})", header, http_body: payload ) end true end |