Sign in →
Embed Widgets1 min read

Embed Widgets: Security & Compliance

CSP directives, SRI hash pinning, sub-processor declaration, data residency, GDPR posture, and how to report vulnerabilities.

Updated 2026-06-15Suggest edits
Docs Embed Widgets Security

Content-Security-Policy#

Add the following directives to your CSP. The widgets work without unsafe-inline or unsafe-eval — they don't use eval(), new Function(), or dynamic import() of untrusted URLs.

Content-Security-Policy header
script-src 'self' https://embed.aforo.ai;
connect-src 'self' https://api.aforo.ai https://embed.aforo.ai;
frame-src https://embed.aforo.ai https://js.stripe.com https://checkout.razorpay.com https://www.paypal.com;
img-src 'self' https://embed.aforo.ai data:;
style-src 'self' 'unsafe-inline';  # widgets use inline <style> via JS — required
INFO
Why style-src 'unsafe-inline'? The widgets inject scoped <style> tags at mount time (namespaced with aforo-w-*classes) so customers don't need to import a separate CSS file. We're actively evaluating a CSP-nonce path for v1.1 — track FR-SEC-17 in the roadmap.

SRI hash pinning#

Subresource Integrity (SRI) hashes are published per version. Pin the loader script via the integrity= attribute so the browser refuses to execute a tampered bundle.

Live SRI manifest

Fetch the current hashes from embed.aforo.ai/v1/sri.json. Shape:

https://embed.aforo.ai/v1/sri.json
{
  "version": "0.1.0",
  "generatedAt": "2026-05-29T…Z",
  "bundles": {
    "loader.umd.js": "sha384-…",
    "widgets/pricing-card.js": "sha384-…",
    "widgets/subscribe-button.js": "sha384-…",
    "widgets/invoice-list.js": "sha384-…",
    "widgets/checkout-flow.js": "sha384-…"
  }
}

Pinning

your-page.html
<script
  src="https://embed.aforo.ai/v1/loader.js"
  integrity="sha384-…"
  crossorigin="anonymous"
  async
></script>

Per-widget bundles are lazy-fetched by the loader using the hashes embedded in the SRI manifest — you don't need to pin them individually. If the manifest itself were tampered, the loader-level integrity check would still fail.

HTTPS only#

The loader refuses to bootstrap on http:// pages — you'll see a console error and no widgets will mount. This is a hard guard (FR-TIER-12) to prevent token leakage over plaintext.

Localhost is exempt (so http://localhost:3000 works during dev) but any other plaintext host is rejected.

Sub-processors#

For GDPR Article 28 record-keeping, the plugin tier introduces these sub-processors beyond Aforo's platform-wide list:

Sub-processorPurposeData shared
AWS CloudFrontCDN for embed.aforo.ai bundlesCustomer IP, User-Agent (standard CDN access logs)
AWS S3 (us-east-1)Bundle originNo customer-identifiable data
npm RegistryDistribution of @aforoai/storefront-widgetsNo customer data — public package
Stripe, Razorpay, PayPalPayment provider iframes (only when CheckoutFlow is mounted)See provider DPA — Aforo passes through their iframe with the embed key and customer billing email

The full Aforo sub-processor list is at .

Data residency#

The CDN serving embed.aforo.ai uses CloudFront edge locations globally — your customers fetch bundles from their nearest edge. The bundle content is identical worldwide; only static JavaScript is served from the CDN.

Authenticated API traffic (api.aforo.ai) and customer data are processed in Aforo's primary region per your tenant's configured residency. Today: US East (Virginia). EU and APAC residency are documented in .

GDPR + RTBF#

The widget SDK is a thin client — it doesn't persist customer data in the browser beyond the in-memory session JWT (cleared on tab close) and a 30-second result cache in InvoiceList (cleared on unmount). When you submit a Right-to-be-Forgotten request via Aforo's DSR queue, the customer's data is purged platform-wide; the next time their browser mounts a widget, the bridge exchange creates a fresh (or rejects the request, depending on your privacy mode).

See for the full DSR lifecycle.

Vulnerability reporting#

Report vulnerabilities privately to security@aforo.ai. Do not file public GitHub issues for security reports.

  • Acknowledgement within 2 business days
  • Initial triage within 5 business days
  • Critical fixes ship in <72h, high within 14 days
  • Coordinated disclosure with 90-day default embargo

Full security policy: SECURITY.md.