Embed Widgets: Authentication
Three auth modes — anonymous, bridge token, magic link — with full code samples for Node.js, Python, and Go. Choose the right mode for your portal.
Which mode should I use?#
The plugin tier supports three authentication modes. Pick the one that matches your portal's existing auth setup:
Anonymous mode#
The simplest mode. Drop a PricingCard with just tenantSlug and embedKey. No backend changes needed.
Anonymous mode is enforced server-side: the headless config endpoint only returns offerings marked PUBLISHED, and the embed key's domain allowlist must include the requesting page's origin.
Bridge token mode#
For widgets that need to know which customer the page belongs to. Your backend mints a short-lived signed token; the widget exchanges it for an in-memory session JWT. Your signing key never reaches the browser.
Step 1 — Get your signing key
In Embed Studio, navigate to Auth Bridge, click Mint a new signing key, and choose HS256 or RS256. Store the key material in your secret manager (HSM, KMS, Vault). Never commit it to source.
Step 2 — Mint bridge tokens on your backend
Bridge tokens are short-lived (5 minutes recommended) JWTs signed with your key. The payload carries the customer identity Aforo should associate with this session.
Step 3 — Pass the token to your widget
Step 4 — Aforo exchanges the bridge token for a session JWT
On mount, the widget calls AforoSession.getSessionJwt(bridgeToken) which POSTs to Aforo's bridge endpoint. Aforo verifies the signature against the registered public key (RS256) or shared secret (HS256), maps your external_customer_id to an internal Aforo customer (creating one on first sight), and returns a session JWT bound to that customer + tenant.
The session JWT lives in memory only — never in localStorage, never in cookies. It expires after 1 hour and is auto-refreshed 5 minutes before expiry. If your customer changes (e.g. logout/login), call AforoSession.destroy() and remount the widget to start a fresh exchange.
Magic link mode#
For portals that don't have their own session yet. The customer types their email, gets an Aforo-branded email, clicks the link, and lands back on your page with a session JWT. Aforo handles the entire flow — your backend doesn't need to mint anything.
On mount, the widget renders an email input. After the customer submits, they see a Check your inbox confirmation. The link in the email returns the customer to the same page with ?aforo_magic_token=… in the URL — the SDK detects it, exchanges it for a session JWT, and renders the actual invoice list. The URL parameter is stripped via history.replaceState so it doesn't end up in the page's analytics.
Anti-enumeration posture
The magic-link request endpoint always returns the same 202 Accepted envelope regardless of whether the email is registered. Attackers cannot use it as a user-registration probe.
Rate limits
- Per IP — 10 magic-link requests per minute. Excess requests get a silent 202 (no error, but no email sent).
- Per email — 5 magic-link requests per hour. Same silent 202 behavior past the cap.
- Token validity — 15 minutes, one-time use, pessimistic-lock enforced server-side so the same link cannot be redeemed twice even if the customer clicks fast.
Security boundaries#
See the for the full security model, CSP guidance, and how to report vulnerabilities.