Class: Axn::Webhooks::Verify

Inherits:
Object
  • Object
show all
Includes:
Axn, VendorFacet
Defined in:
lib/axn/webhooks/verify.rb

Overview

The verify stage, as an Axn. A signature mismatch is a quiet failure (fail! → 401 later, no on_exception page); a verifier that raises is a loud exception (reported to Axn.config.on_exception). The first two rows of the staged-outcome model.

Constant Summary collapse

MESSAGES =

Why the request was rejected, keyed by Signature::REASONS. The HTTP response is a bare 401 in every case — this exists so the two causes are separable in logs and metrics (PRO-3141: a replay-window miss and an HMAC mismatch were byte-identical in the logs, and the replay case reported "signature mismatch" for a request whose signature was valid).

{
  replay_window: ->(check) { "replay window exceeded (timestamp skew #{check.skew}s)" },
  replay_timestamp_invalid: ->(_check) { "replay timestamp missing or unparseable" },
  signature_missing: ->(_check) { "signature missing" },
  signature_mismatch: ->(_check) { "signature mismatch" },
  # A genuine anomaly, and worth alerting on: the client presented an `Authorization` header,
  # so it meant to authenticate, but not a Basic one. The bare handshake leg — which used to
  # land here, once per *successful* webhook, making this the highest-volume value of the
  # dimension — is answered with the challenge before Verify runs at all now (PRO-3148), so
  # the message names what is actually left rather than the leg it no longer reports.
  credentials_missing: lambda { |_check|
    "no Basic credentials offered — a non-Basic Authorization scheme (the bare handshake leg " \
      "is challenged before Verify)"
  },
  credentials_mismatch: ->(_check) { "Basic credentials rejected" },
}.freeze

Instance Method Summary collapse

Methods included from VendorFacet

included

Instance Method Details

#callObject



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/axn/webhooks/verify.rb', line 60

def call
  check = verifier.call(request)
  return if verified?(check)

  # Set before fail! so the result-phase dimension resolvers can read them.
  rejection = check.is_a?(Signature::Check) ? check : Signature::MISMATCH
  @reason = rejection.reason
  @skew = rejection.skew
  @suggested_unit = rejection.suggested_unit
  fail!(message_for(rejection), reason: @reason, skew: @skew, suggested_unit: @suggested_unit)
end