Module: Angarium::Signature
- Defined in:
- lib/angarium/signature.rb
Class Method Summary collapse
-
.parse(header) ⇒ Object
webhook-signature is space-delimited
v1,<base64>tokens; keep the base64 payload of each v1 token. - .secure_compare(a, b) ⇒ Object
-
.sign(payload:, id:, timestamp:, secret:) ⇒ Object
secret may be a String or Array of secrets (dual-secret rotation grace); produces one
v1,<base64>token per secret, space-delimited. - .signature_for(secret, id, timestamp, payload) ⇒ Object
-
.verify(secret:, request: nil, payload: nil, id: nil, timestamp: nil, signature: nil, tolerance: 300, now: Time.now.to_i) ⇒ Object
Verify a Standard Webhooks signature.
Class Method Details
.parse(header) ⇒ Object
webhook-signature is space-delimited v1,<base64> tokens; keep the base64
payload of each v1 token.
44 45 46 47 48 49 |
# File 'lib/angarium/signature.rb', line 44 def parse(header) header.to_s.split(" ").filter_map { |token| version, sig = token.split(",", 2) sig if version == "v1" && sig && !sig.empty? } end |
.secure_compare(a, b) ⇒ Object
51 52 53 54 55 |
# File 'lib/angarium/signature.rb', line 51 def secure_compare(a, b) ActiveSupport::SecurityUtils.secure_compare(a, b) rescue ArgumentError false end |
.sign(payload:, id:, timestamp:, secret:) ⇒ Object
secret may be a String or Array of secrets (dual-secret rotation grace);
produces one v1,<base64> token per secret, space-delimited.
10 11 12 |
# File 'lib/angarium/signature.rb', line 10 def sign(payload:, id:, timestamp:, secret:) Array(secret).map { |s| "v1,#{signature_for(s, id, , payload)}" }.join(" ") end |
.signature_for(secret, id, timestamp, payload) ⇒ Object
36 37 38 39 40 |
# File 'lib/angarium/signature.rb', line 36 def signature_for(secret, id, , payload) key = Base64.decode64(secret.to_s.delete_prefix("whsec_")) digest = OpenSSL::HMAC.digest("SHA256", key, "#{id}.#{}.#{payload}") Base64.strict_encode64(digest) end |
.verify(secret:, request: nil, payload: nil, id: nil, timestamp: nil, signature: nil, tolerance: 300, now: Time.now.to_i) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/angarium/signature.rb', line 20 def verify(secret:, request: nil, payload: nil, id: nil, timestamp: nil, signature: nil, tolerance: 300, now: Time.now.to_i) if request payload ||= request.raw_post id ||= request.headers["webhook-id"] ||= request.headers["webhook-timestamp"] signature ||= request.headers["webhook-signature"] end return false unless .to_s.match?(/\A\d+\z/) return false if (now - .to_i).abs > tolerance expected = signature_for(secret, id, .to_i, payload) parse(signature).any? { |sig| secure_compare(expected, sig) } end |