Class: Philiprehberger::WebhookSignature::Verifier
- Inherits:
-
Object
- Object
- Philiprehberger::WebhookSignature::Verifier
- Defined in:
- lib/philiprehberger/webhook_signature/verifier.rb
Overview
Verifies HMAC webhook signatures with replay prevention.
Constant Summary collapse
- DEFAULT_TOLERANCE =
5 minutes
300- SUPPORTED_ALGORITHMS =
{ sha256: 'SHA256', sha512: 'SHA512' }.freeze
Instance Method Summary collapse
-
#initialize(secret, algorithm: :sha256) ⇒ Verifier
constructor
A new instance of Verifier.
-
#valid?(payload, timestamp:, signature:, tolerance: DEFAULT_TOLERANCE) ⇒ Boolean
Boolean wrapper around verify!.
-
#valid_header?(payload, header:, tolerance: DEFAULT_TOLERANCE) ⇒ Boolean
Boolean wrapper around verify_header!.
-
#verify(payload, timestamp:, signature:, tolerance: DEFAULT_TOLERANCE) ⇒ Boolean
Verify a payload against a signature.
-
#verify!(payload, timestamp:, signature:, tolerance: DEFAULT_TOLERANCE) ⇒ true
Verify and raise on failure.
-
#verify_header(payload, header:, tolerance: DEFAULT_TOLERANCE) ⇒ Boolean
Verify a signature header string.
-
#verify_header!(payload, header:, tolerance: DEFAULT_TOLERANCE) ⇒ true
Verify a header string or raise on failure.
Constructor Details
#initialize(secret, algorithm: :sha256) ⇒ Verifier
Returns a new instance of Verifier.
18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/philiprehberger/webhook_signature/verifier.rb', line 18 def initialize(secret, algorithm: :sha256) raise ArgumentError, 'Secret must be a non-empty string' if secret.nil? || secret.empty? unless SUPPORTED_ALGORITHMS.key?(algorithm) raise ArgumentError, "Unsupported algorithm: #{algorithm.inspect}. Supported algorithms: #{SUPPORTED_ALGORITHMS.keys.map(&:inspect).join(', ')}" end @secret = secret @algorithm = algorithm @digest_name = SUPPORTED_ALGORITHMS.fetch(algorithm) end |
Instance Method Details
#valid?(payload, timestamp:, signature:, tolerance: DEFAULT_TOLERANCE) ⇒ Boolean
Boolean wrapper around verify!.
92 93 94 95 96 |
# File 'lib/philiprehberger/webhook_signature/verifier.rb', line 92 def valid?(payload, timestamp:, signature:, tolerance: DEFAULT_TOLERANCE) verify!(payload, timestamp: , signature: signature, tolerance: tolerance) rescue VerificationError false end |
#valid_header?(payload, header:, tolerance: DEFAULT_TOLERANCE) ⇒ Boolean
Boolean wrapper around verify_header!.
104 105 106 107 108 |
# File 'lib/philiprehberger/webhook_signature/verifier.rb', line 104 def valid_header?(payload, header:, tolerance: DEFAULT_TOLERANCE) verify_header!(payload, header: header, tolerance: tolerance) rescue VerificationError false end |
#verify(payload, timestamp:, signature:, tolerance: DEFAULT_TOLERANCE) ⇒ Boolean
Verify a payload against a signature.
38 39 40 41 42 43 |
# File 'lib/philiprehberger/webhook_signature/verifier.rb', line 38 def verify(payload, timestamp:, signature:, tolerance: DEFAULT_TOLERANCE) return false if tolerance && stale?(, tolerance) expected = compute_signature(payload, ) secure_compare(expected, signature) end |
#verify!(payload, timestamp:, signature:, tolerance: DEFAULT_TOLERANCE) ⇒ true
Verify and raise on failure.
63 64 65 66 67 68 69 70 71 72 |
# File 'lib/philiprehberger/webhook_signature/verifier.rb', line 63 def verify!(payload, timestamp:, signature:, tolerance: DEFAULT_TOLERANCE) if tolerance && stale?(, tolerance) raise VerificationError, "Signature timestamp is too old (tolerance: #{tolerance}s)" end expected = compute_signature(payload, ) raise VerificationError, 'Signature mismatch' unless secure_compare(expected, signature) true end |
#verify_header(payload, header:, tolerance: DEFAULT_TOLERANCE) ⇒ Boolean
Verify a signature header string.
51 52 53 54 55 56 |
# File 'lib/philiprehberger/webhook_signature/verifier.rb', line 51 def verify_header(payload, header:, tolerance: DEFAULT_TOLERANCE) parsed = parse_header(header) return false unless parsed verify(payload, timestamp: parsed[:timestamp], signature: parsed[:signature], tolerance: tolerance) end |
#verify_header!(payload, header:, tolerance: DEFAULT_TOLERANCE) ⇒ true
Verify a header string or raise on failure.
81 82 83 84 85 86 |
# File 'lib/philiprehberger/webhook_signature/verifier.rb', line 81 def verify_header!(payload, header:, tolerance: DEFAULT_TOLERANCE) parsed = parse_header(header) raise VerificationError, 'Invalid header format' unless parsed verify!(payload, timestamp: parsed[:timestamp], signature: parsed[:signature], tolerance: tolerance) end |