Getting Started with EmbedAuth
Add a complete, hosted authentication UI to any web app with an iframe and a few lines of JavaScript — then verify tokens on your server.
EmbedAuth gives your application a full authentication experience — sign in, sign up, password reset, magic links, OTP codes, and OAuth — without you building or maintaining any of it. You drop an iframe into your page, listen for a single event, and receive a signed JWT you can trust on your backend.
This guide takes you from an empty page to a working, verified sign-in in about five minutes.
Prerequisites
- An EmbedAuth account and an app created in the dashboard. Each app has its own
clientId, allowed domains, and theme. - A page served over HTTPS (localhost is fine for development).
- A backend that can verify a JWT — any language works, and later guides cover Node, Python, and Go.
Step 1 — Create an app and add your domain
In the dashboard, create a new app. On the app's Domains page, add the domain your page is served from — the bare hostname, such as app.yoursite.com, without https:// or a path. An entry matches that exact host, not its subdomains, so add each one you embed on.
The widget will still render on a site that is not on the list, but it will not hand that site any tokens: the auth-success message is posted only to a page on one of your domains. That is what stops another site that copies your embed code from receiving your users' tokens. Until an app has at least one domain, tokens go to whichever page frames the widget, so add yours before you go live.
Copy the app's clientId. You will use it in the iframe URL.
Step 2 — Add the iframe
Place the embed anywhere in your markup. The mode parameter selects which form appears first.
<iframe
id="authframe"
src="https://embedauth.com/embed/YOUR_CLIENT_ID?mode=signin"
width="420"
height="600"
frameborder="0"
style="border: none;"
></iframe>
The embed is fully responsive and reports its own height (see PostMessage Events), so you can size the frame dynamically instead of hard-coding 600.
Step 3 — Listen for the result
The iframe talks to your page through window.postMessage. When authentication succeeds you receive a JWT, a refresh token and the user's profile. Always verify event.origin first — this is the single most important line in your integration.
The origin to compare against is https://cdn.embedauth.com, not https://embedauth.com: the iframe URL above redirects to the widget's copy on EmbedAuth's CDN, and messages come from the page that actually loaded.
<script>
window.addEventListener('message', (event) => {
if (event.origin !== 'https://cdn.embedauth.com') return;
if (event.data.type === 'authframe:auth-success') {
const { token, user } = event.data.data;
// Hand the token to your backend, or store it for API calls
sessionStorage.setItem('authframe_token', token);
window.location.href = '/dashboard';
}
});
</script>
That is a complete client integration. But the token is only meaningful once your server confirms it is genuine.
Step 4 — Verify the token on your server
The JWT is signed with RS256, an asymmetric algorithm. EmbedAuth holds the private key; you verify with the matching public key, published at a JWKS endpoint:
https://embedauth.com/.well-known/jwks.json?clientId=YOUR_CLIENT_ID
Because verification uses a public key, your server never needs a shared secret. A minimal Node example:
import { createRemoteJWKSet, jwtVerify } from 'jose';
const JWKS = createRemoteJWKSet(
new URL('https://embedauth.com/.well-known/jwks.json?clientId=YOUR_CLIENT_ID')
);
export async function verifyToken(token) {
const { payload } = await jwtVerify(token, JWKS, {
issuer: 'https://embedauth.com',
audience: 'YOUR_CLIENT_ID',
});
return payload; // { sub, email, name, clientId, iat, exp }
}
Full backend walkthroughs live in Verifying JWTs in Node.js, Python, and Go.
What's in the token
| Claim | Meaning |
|---|---|
iss | The issuer, https://embedauth.com |
aud | Your app's client ID |
sub | Stable end-user ID — use this as your foreign key |
email | The user's email address |
name / avatar | Display name and avatar URL, if known |
clientId | Your app's client ID again, for convenience |
authMethod | How the user signed in, such as email_password or magic_link |
sid | The session the token belongs to; the same across refreshes of one sign-in |
iat / exp | Issued-at and expiry timestamps; access tokens last one hour by default |
Never trust these values until jwtVerify (or your language's equivalent) has succeeded. A JWT is only a claim; the signature is what makes it a fact.
Next steps
- Framework integration — React, Next.js, or Vue.
- How the pieces fit — read How EmbedAuth Works for the full architecture.
- Going to production — configure your Content Security Policy so browsers allow the iframe.
- Something not loading? — the Troubleshooting guide covers the common failures.