Initial Commit
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
# MessageBird — `@suaveplan/sms-providers`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Real MessageBird SMS provider implementing `SmsProvider` from `@suaveplan/sms`. POSTs to `/messages` with `Authorization: AccessKey <key>`; status fetched via `GET /messages/{id}`. Inbound webhook signature verification follows the MessageBird Signed Webhooks v2 (`Messagebird-Signature-Jwt`) spec — a JWT HS256 carrying `iat`/`exp`/`nbf`/`payload_hash` claims; the verifier checks the timestamp window (default ±5 minutes), re-MACs the canonical `header.payload`, and constant-time compares the base64url-decoded signature.
|
||||
|
||||
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
|
||||
|
||||
- `AccessKey` auth header per MessageBird's documented scheme.
|
||||
- JSON body with `recipients[]`, `originator`, `body`.
|
||||
- Status mapping covers `scheduled` / `sent` / `buffered` / `delivered` / `expired` / `delivery_failed`.
|
||||
- JWT HS256 webhook verifier with `iat` / `exp` / `nbf` window enforcement and optional `payload_hash` cross-check.
|
||||
- Injectable `nowMs` + `toleranceMs` so tests pin the clock and the production deployment can widen the freshness window if needed.
|
||||
- `parseInbound` discriminates between inbound messages and DLRs, recognising the standard CTIA opt-out keywords.
|
||||
- OTel — `send_total` + `send_duration_ms` + `signature_failures_total` + `webhook_verify_duration_ms`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { MessageBirdProvider } from "@suaveplan/sms-providers";
|
||||
|
||||
const mb = new MessageBirdProvider({ accessKey: "test_…" });
|
||||
await mb.send({ to: "+15551234567", from: "Suaveplan", body: "hi" });
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { MessageBirdProvider } from "@suaveplan/sms-providers";
|
||||
|
||||
const mb = new MessageBirdProvider({ accessKey: "live_…" });
|
||||
const ok = await mb.verifyWebhookSignature(
|
||||
"<header>.<payload>.<sig>",
|
||||
"shared-signing-key",
|
||||
{ bodyHashHex: "<sha256-hex>", nowMs: Date.now(), toleranceMs: 60_000 }
|
||||
);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
| Symbol | Kind | Notes |
|
||||
|---|---|---|
|
||||
| `MessageBirdProvider` | class | Implements `SmsProvider`. |
|
||||
| `send(message, options?)` | method | `POST /messages`, returns `SmsDeliveryReport`. |
|
||||
| `getStatus(id)` | method | `GET /messages/{id}`. |
|
||||
| `mapStatus(raw)` | method | Canonical mapping. |
|
||||
| `verifyWebhookSignature(token, signingKey, options?)` | method | JWT HS256 verifier; throws `SIGNATURE_INVALID` on failure. |
|
||||
| `parseInbound(body)` | method | Discriminates into `InboundSmsEvent`. |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The verifier handles both standard Base-64 and base64url (`-`/`_` → `+`/`/`) inputs because MessageBird's JWT uses the URL-safe variant. `payload_hash` is checked only when both the JWT carries it and the caller supplies a `bodyHashHex`; missing on either side is treated as "skip" so callers can disable the body binding when their proxy strips bodies.
|
||||
Reference in New Issue
Block a user