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
Generates a value that would be added to a ‘Stripe-Signature` for a given webhook payload.
-
.verify_header(payload, header, secret, tolerance: nil) ⇒ Object
Verifies the signature header for a given payload.
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.
29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/stripe/webhook.rb', line 29 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
Generates a value that would be added to a ‘Stripe-Signature` for a given webhook payload.
Note that this isn’t needed to verify webhooks in any way, and is mainly here for use in test cases (those that are both within this project and without).
48 49 50 51 52 53 54 55 56 57 |
# File 'lib/stripe/webhook.rb', line 48 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 signature header for a given payload.
Raises a SignatureVerificationError in the following cases:
-
the header does not match the expected format
-
no signatures found with the expected scheme
-
no signatures matching the expected signature
-
a tolerance is provided and the timestamp is not within the tolerance
Returns true otherwise
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/stripe/webhook.rb', line 79 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 |