ALO signs its request using a secret unique to your app. This signed secret is provided once you create you app.
With signed requests you can be confident that the request you endpoint is receiving is coming from ALO.
The signature is created by hashing the request body with the SHA-256 function, and combining it with an HMAC signing secret. The resulting signature is unique to each request and doesn't contain any secret information, keeping your app secure.
Validating a Request
Requests from ALO will contain 2 header used for validation:
Header
Description
X-ALO-Signature
Contains the HMAC-SHA256 signature from ALO
X-ALO-Request-Timestamp
The Unix timestamp of the request. Your server can decide to ignore request that exceed the age of your choice
Example
Below is an example of how you would verify an ALO signed request in a Ruby on Rails controller.
class AloWebhookController < ApplicationController
skip_before_action :your_custom_verification_process
# Replace with your actual ALO signing secret
SIGNING_SECRET = Rails.application.credentials.dig(:alo, :signing_secret)
def create
# Step 1: Extract ALO headers
alo_signature = request.headers['X-ALO-Signature']
alo_timestamp = request.headers['X-ALO-Request-Timestamp']
# Step 2: Prevent replay attacks
if timestamp_too_old?(alo_timestamp)
render plain: 'Request is too old', status: :forbidden and return
end
# Step 3: Construct the base string
base_string = "v0:#{alo_timestamp}:#{request.raw_post}"
# Step 4: Generate the HMAC signature
computed_signature = generate_signature(base_string)
# Step 5: Compare the computed signature with ALO's signature
unless valid_signature?(computed_signature, alo_signature)
render plain: 'Invalid request signature', status: :forbidden and return
end
# Handle the event payload
render plain: 'Request validated successfully', status: :ok
end
private
def timestamp_too_old?(timestamp)
current_time = Time.now.to_i
(current_time - timestamp.to_i).abs > 300 # 5 minutes
end
def generate_signature(base_string)
digest = OpenSSL::Digest.new('sha256')
'v0=' + OpenSSL::HMAC.hexdigest(digest, SIGNING_SECRET, base_string)
end
def valid_signature?(computed_signature, alo_signature)
ActiveSupport::SecurityUtils.secure_compare(computed_signature, alo_signature)
end
end