Hex.pm CI Hex.pm Hex Docs

Verify the authenticity of AWS SNS HTTPS messages in Elixir. RSA-SHA256 signature verification for Notification, SubscriptionConfirmation, and UnsubscribeConfirmation payloads — the Elixir equivalent of Ruby's Aws::SNS::MessageVerifier.

Features

RSA-SHA256 verification — validates signatures using AWS's SigningCertURL
🔒 URL hardening — enforces HTTPS, host whitelist (sns.<region>.amazonaws.com), no credentials in URL, .pem extension for certs
Replay protection — configurable timestamp window (default: 1 hour)
📋 Topic allowlist — restricts which TopicArn values are accepted
🧩 Pluggable HTTP client — swap in Tesla, Req, Finch, or :httpc default
💾 Automatic cert caching:persistent_term-backed with 24h TTL
⚠️ Descriptive error reasons — atoms like :signature_invalid, :topic_not_allowed, :timestamp_out_of_window

Installation

Add ex_aws_sns_verifier to your mix.exs:

def deps do
  [
    {:ex_aws_sns_verifier, "~> 0.1.0"}
  ]
end

Usage

One-shot verification

raw_body = ~s({"Type": "Notification", ...})
opts = [allowed_topic_arns: ["arn:aws:sns:us-east-1:123456789012:MyTopic"]]

{:ok, payload} = ExAwsSnsVerifier.verify(raw_body, opts)
# or
payload = ExAwsSnsVerifier.verify!(raw_body, opts)

With Verifier struct

verifier = ExAwsSnsVerifier.new(
  allowed_topic_arns: ["arn:aws:sns:us-east-1:123456789012:MyTopic"],
  timestamp_window_seconds: 300  # 5 minutes
)

{:ok, payload} = ExAwsSnsVerifier.verify(verifier, raw_body)

Plug integration

Use with Phoenix controllers or Plug pipelines:

# In a router or pipeline
plug ExAwsSnsVerifier.Plug, allowed_topic_arns: ["arn:aws:sns:..."]

Configuration

OptionDefaultDescription
allowed_topic_arns(required)List of allowed SNS topic ARNs
allowed_regionsAll commercial regionsAWS regions for cert URL validation
timestamp_window_seconds3600Replay protection window
http_client:httpcModule implementing get/1
cert_cache:persistent_termModule implementing get/1 and put/2

Custom HTTP client

defmodule MyApp.HttpClient do
  @behaviour ExAwsSnsVerifier.Cert.HttpClientBehaviour

  def get(url) do
    # Your HTTP implementation (Req, Finch, Tesla, etc.)
    {:ok, body}
  end
end

ExAwsSnsVerifier.new(
  allowed_topic_arns: ["..."],
  http_client: MyApp.HttpClient
)

Error Reasons

ErrorMeaning
:invalid_jsonBody is not valid JSON
:unknown_message_typeType is not Notification/SubscriptionConfirmation/UnsubscribeConfirmation
:missing_signature_versionNo SignatureVersion field
:unsupported_signature_versionOnly Version 2 (SHA256) supported
:missing_timestampNo Timestamp field
:invalid_timestampTimestamp is not valid ISO 8601
:timestamp_out_of_windowMessage is outside the replay window
:missing_topic_arnNo TopicArn field
:no_allowed_topicsAllowlist is empty
:topic_not_allowedTopicArn not in allowlist
:missing_signatureNo Signature field
:invalid_signature_encodingSignature is not valid Base64
:invalid_cert_urlSigningCertURL failed host/path validation
:missing_signing_cert_urlNo SigningCertURL field
:signature_invalidRSA-SHA256 signature does not verify

Development

git clone https://github.com/GustavoZiaugra/ex_aws_sns_verifier.git
cd ex_aws_sns_verifier
mix deps.get
mix test
mix credo --strict
mix dialyzer

License

MIT © Gustavo Ziaugra