Files
skills/docs/communication/messaging/sms-providers/src/aws-sns/aws-sns.md
2026-05-29 08:20:56 +01:00

4.5 KiB

AWS SNS — @suaveplan/sms-providers

(no submodules)

Purpose

Real AWS SNS SMS provider implementing SmsProvider from @suaveplan/sms. POSTs Action=Publish form bodies to the regional SNS endpoint (https://sns.<region>.amazonaws.com/) signed with inline AWS Signature V4 (see ./sigv4); SMS-specific message attributes (AWS.SNS.SMS.SMSType and AWS.SNS.SMS.SenderID) are forwarded when supplied on the provider config. Inbound SNS HTTP/S notifications — subscription confirmation, delivery callbacks, unsubscribe confirmation — are validated through verifyNotification which delegates the RSA-SHA1 verification step to an injectable callback so this package never imports node:crypto.

The class holds no in-flight state and routes every HTTP call through the injectable FetchLike seam. Tests stand up a node:http server on a random port and inject a fetch adapter that points at it.

Features

  • Inline AWS Signature V4 signing (no @aws-sdk dep tree).
  • AWS.SNS.SMS.SMSType defaults to Transactional; override via config.smsType: "Promotional".
  • AWS.SNS.SMS.SenderID forwarded from the message from or the config senderId.
  • Optional sessionToken for STS-issued credentials (X-Amz-Security-Token).
  • Status mapping covers pending / success / failure / optout.
  • verifyNotification(payload, rsaVerify) validates the documented SNS signing chain — fetches the signing cert URL through the injectable fetch, builds the canonical string-to-sign (different field order for Notification vs SubscriptionConfirmation/UnsubscribeConfirmation), and delegates the RSA verification to the supplied callback.
  • parseInbound discriminates between delivery-receipt notifications (Subject = "AWS SMS Delivery Receipt") and inbound mobile-originated SMS payloads; recognises CTIA opt-out keywords on the inbound body.
  • OTel — counter + histogram on every send + verify.

Usage: Basic

import { AwsSnsProvider } from "@suaveplan/sms-providers";

const sns = new AwsSnsProvider({
    region: "us-east-1",
    accessKeyId: "AKIA…",
    secretAccessKey: "…",
    senderId: "SUAVEPLAN",
});
const r = await sns.send({ to: "+15551234567", body: "Hello" });

Usage: Advanced

import { AwsSnsProvider } from "@suaveplan/sms-providers";
import { createVerify } from "node:crypto";

const sns = new AwsSnsProvider({
    region: "us-east-1",
    accessKeyId: "AKIA…",
    secretAccessKey: "…",
    sessionToken: "FwoGZXIvYXdz…",
    smsType: "Transactional",
});

// Node-side RSA-SHA1 verifier callback.
const rsaVerify = async (pem: string, msg: string, sigB64: string) =>
    createVerify("RSA-SHA1").update(msg).end().verify(pem, sigB64, "base64");

const ok = await sns.verifyNotification(
    /* payload */ {
        Type: "Notification",
        MessageId: "abc",
        TopicArn: "arn:aws:sns:…",
        Subject: "AWS SMS Delivery Receipt",
        Message: JSON.stringify({ status: "SUCCESS" }),
        Timestamp: new Date().toISOString(),
        Signature: "<base64>",
        SignatureVersion: "1",
        SigningCertURL:
            "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-XXX.pem",
    },
    rsaVerify
);

API Reference

Symbol Kind Notes
AwsSnsProvider class Implements SmsProvider.
send(message, options?) method SigV4-signed POST Action=Publish.
getStatus(id) method Returns a synthetic queued report (SNS does not expose per-message status).
mapStatus(raw) method Canonical mapping.
verifyNotification(payload, rsaVerify) method Delegates RSA-SHA1 verification to the supplied callback.
parseInbound(payload) method Discriminates into InboundSmsEvent.
buildSnsStringToSign(payload) helper Exported for advanced use.

Implementation Notes

getStatus deliberately returns a synthetic queued report rather than synthesise a fake delivered — SNS reports SMS lifecycle exclusively via the configured delivery-status SNS topic (typically wired into CloudWatch Logs in production), and lying about provider behaviour would mislead consumers. The recommended production pipeline is to subscribe to the delivery-status topic, route the notifications back through the provider's parseInbound, and feed the resulting delivery_receipt events to @suaveplan/sms's DeliveryTracker. The senderId config overrides the per-message from; this matches the AWS console UX where the account-level Sender ID is the production default.