Verifying Requests from ALO

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

Last updated

Logo

ALO.ai

ALO.ai

© Copyright 2025 ALO.ai, Inc. • All Rights Reserved