Last active
August 8, 2023 07:22
-
-
Save prognostikos/aa6c861770a9adac0c2cb0165cd76a54 to your computer and use it in GitHub Desktop.
Ruby validator for Vipps Webhook requests
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "active_support/security_utils" | |
require "digest/sha2" | |
require "openssl/hmac" | |
require "uri" | |
module Vipps | |
# Validates webhook requests, documented at: | |
# https://developer.vippsmobilepay.com/docs/APIs/webhooks-api/request-authentication/ | |
# | |
class ValidatesWebhook | |
def initialize( | |
secret:, | |
url:, | |
request_body:, | |
request_checksum:, | |
date:, | |
authorization: | |
) | |
@secret = secret | |
@url = URI(url) | |
@request_body = request_body | |
@request_checksum = request_checksum | |
@date = date | |
@authorization = authorization | |
end | |
def valid? | |
body_checksum_matches? && | |
authorization_matches? | |
end | |
def body_checksum_matches? | |
ActiveSupport::SecurityUtils.secure_compare( | |
request_checksum, | |
calculated_body_checksum | |
) | |
end | |
def authorization_matches? | |
ActiveSupport::SecurityUtils.secure_compare( | |
authorization, | |
calculated_authorization | |
) | |
end | |
private | |
def calculated_body_checksum | |
Digest::SHA256.base64digest(request_body) | |
end | |
def calculated_authorization | |
"HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=#{calculated_signature}" | |
end | |
def calculated_signature | |
OpenSSL::HMAC.base64digest( | |
"SHA256", | |
secret, | |
string_to_sign | |
) | |
end | |
def string_to_sign | |
"POST\n#{url.path}\n#{date};#{url.authority};#{request_checksum}" | |
end | |
attr_reader :secret, | |
:url, | |
:request_body, | |
:request_checksum, | |
:date, | |
:authorization | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "spec_helper" | |
require_relative "../../../vipps_validates_webhook" | |
RSpec.describe "validating Vipps webhooks" do | |
let(:validator) { | |
Vipps::ValidatesWebhook.new( | |
secret: "A0+AeKBRG2KRGvnNwJpQlb6IJFk48CKXCIcrLoHncVJKDILsQSxS6NWCccwWm6r6FhGKhiHTBsG2wo/xU6FY/A==", | |
url: "https://webhook.site/e2cee29b-012e-4f1d-8ef4-e95fd74a7a63", | |
request_body: '{"some-unique-content":"ee6e441b-cc4a-46f8-895d-a5af79bcc233/hello-world"}', | |
request_checksum: "lNlsp1XA03N34HrQsVzPgJKtC+r7l/RBF4V3JQUWMj4=", | |
date: "Thu, 30 Mar 2023 08:38:32 GMT", | |
authorization: "HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=agAiSyogQbDHpeucoNwYz+yAr5nJ+v+zasdkSbqzv+U=" | |
) | |
} | |
it "can calculate and compare a checksum for the body" do | |
expect(validator.body_checksum_matches?).to be(true) | |
end | |
it "can verify the authorization for the request" do | |
expect(validator.authorization_matches?).to be(true) | |
end | |
it "can verify the request" do | |
expect(validator.valid?).to be(true) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment