How do I authenticate the embed with JWT?
Sign a JWT to secure your embed and keep it loading in Safari and privacy-blocking browsers.
Updated
Orshot Embed typically relies on Referrer Headers to ensure that your embed is only loaded on allowed domains. However, some browsers (like Safari) or privacy extensions may strip these headers, causing the embed to fail validation.
To solve this, Orshot supports JWT (JSON Web Token) authentication. By signing a token on your server and passing it to the embed, you can securely authorize access without relying on browser headers.
When to use this?#
- If you have users on Safari facing "Access Denied" errors.
- If you are embedding inside an invalid environment (e.g. some mobile webviews).
- If you want an extra layer of security beyond domain whitelisting.
Setup#
1. Generate Signing Secret#
Open Orshot Embed, expand Authentication (JWT), and click the refresh button next to Signing Secret to generate one.
2. Sign a Token (Server-Side)#
On your backend, sign a JWT with this secret. Any payload works. Optional sub and permissions claims identify the user and lock actions (see below).
Example (Node.js):
import jwt from "jsonwebtoken";
const SIGNING_SECRET = "your-signing-secret-from-dashboard";
// Generate a token (valid for 1 hour)
const token = jwt.sign(
{
embedId: "your-embed-id", // Optional context
nonce: Math.random(), // Prevent replay attacks (optional)
},
SIGNING_SECRET,
{ expiresIn: "1h" }, // Short expiry recommended
);
const embedUrl = `https://orshot.com/embeds/YOUR_EMBED_ID?token=${token}`;3. Pass Token to Embed#
Append the token query parameter to your embed URL.
<iframe
src="https://orshot.com/embeds/12345?token=YOUR_GENERATED_TOKEN"
title="Orshot Embed"
width="100%"
height="600"
style="border: none;"
allow="clipboard-write"
></iframe>Identify the User and Lock Actions#
Add either optional claim to the payload:
| Claim | What it does |
|---|---|
sub | Your user's id, used in place of ?userId=. Only read when Require signed token is on. Follows the userId rules: up to 256 letters, numbers, _, -, @ or . (numbers work too) |
permissions | Actions to lock for this session, for example { "template/delete": true } |
const token = jwt.sign(
{
sub: "user_123",
permissions: { "template/delete": true },
},
SIGNING_SECRET,
{ expiresIn: "15m" },
);See User Permissions for every permission key and how token locks combine with the ones saved in the dashboard.
Require Signed Token#
Turn on Require signed token under Authentication (JWT) and click Save Settings. The embed then:
- Refuses to load without a valid token, and the token must have an expiry (
exp) - Takes the user from the token's
suband ignores?userId= - Refuses an invalid
sub, and a missing one when per-user data is on
The Preview in your dashboard keeps working.
Turn it on only once every page that loads the embed passes a token. Loads without one are refused.
Fallback Behavior#
With Require signed token off, an invalid or expired token falls back to the allowed-domains check, so users on an allowed domain still get in. With it on, there is no fallback: the embed shows Access Denied instead.
Security Best Practices#
- Short Expiry: Set a short expiration time (e.g.,
1hor even5m) for your tokens. The token is only needed for the initial load. - Backend Only: NEVER expose your Signing Secret on the client-side. Always generate the token on your server.
Ready to automate?
Start rendering images, PDFs and videos from your templates in under 2 minutes. Free plan, no credit card.
Get your API key- Image, PDF and video generation via API
- Visual editor with AI and smart layouts
- Zapier, Make, MCP and 40+ integrations
- White-label embed for your own app
- 100 free credits a month, no credit card required