Multi-Tenant Auth as a Service

Authentication that
embeds anywhere

Drop-in iframe authentication for your apps. Email/password, magic links, OTP, and OAuth — fully customizable with your branding. Launch in minutes.

yourapp.com/login

Welcome back

Sign in to continue

or
Getting Started

From sign-up to a verified user in four steps

What a working integration involves, with a link to the guide that covers each step in detail.

  1. 1

    Create an app

    Sign up for free and create an app in the dashboard. You get a client ID, and you choose which sign-in methods the widget offers.

    Getting started
  2. 2

    Add your domains

    List the domains your site runs on. The widget only hands tokens to pages on those domains, so a copied embed code is useless elsewhere.

    Events reference
  3. 3

    Embed the widget

    Paste one iframe and add a message listener. Ready-made components for React, Next.js and Vue handle resizing and events.

    React guide
  4. 4

    Verify on your server

    Check each token's RS256 signature against the JWKS endpoint, then start your own session. No SDK is needed on the server.

    Verify in Node.js

New to embedded authentication? Read What Is Embedded Authentication?, browse the blog, or see how EmbedAuth compares with the alternatives.

The Basics

What is embedded authentication?

A practical explainer for engineers evaluating identity options for their SaaS, B2B dashboard, or white-label product.

Embedded authentication is a pattern where the login experience lives inside your application — typically rendered through an iframe, a web component, or a UI SDK — rather than redirecting users to a separate identity provider domain. Your user never leaves yourapp.com. They type their email, click the magic link, finish OAuth, and stay on your page the entire time. The auth provider hands a signed token back to your application through a secure cross-origin channel, and your app takes it from there.

The alternative is hosted login — the model popularized by Auth0, Okta, and most enterprise IAM vendors. With hosted login, your application redirects the browser to auth.yourprovider.com, the user authenticates there, and the provider redirects them back with an authorization code. It is the safest pattern for credentials — there is no way for your application code to ever see a password — and it is what every OAuth 2.0 textbook recommends. But it comes with real ergonomic costs.

Hosted login breaks the visual continuity of your product. The login page lives on a different domain, with a different URL bar, a different favicon, and frequently a different look-and-feel no matter how carefully you theme it. Single-page applications that want to keep a user in-place — modals, drawers, embedded widgets, white-label dashboards — cannot use a redirect-based flow without unmounting their entire UI state. And for white-label products specifically, sending an end-user to a third-party domain is a deal-breaker: the customer is paying you to look like a first-party experience.

Embedded auth solves the UX problem. The iframe loads the provider's login page, but the user perceives it as part of your app. The frame is fully themable. Forms post directly to the provider, so credentials still never touch your servers. When authentication succeeds, the iframe sends a postMessage back to the parent page containing a signed JWT, and your front-end uses it to make authenticated API calls.

The trade-off is implementation complexity for the provider. Doing embedded auth correctly means solving the third-party-cookie problem, handling postMessage origin validation correctly, isolating the auth context inside an iframe so a compromised parent page can't read credentials as they are typed, and handing tokens only to the sites the application has approved. This is exactly what EmbedAuth is built for — the provider absorbs the hard parts, and you get a one-line embed.

Developers reach for embedded auth in three common situations. First, when conversion matters: a redirect is one more step where a sign-up can be abandoned, especially on mobile where the bounce-back loop is jarring. Second, when the product is itself an embed — a widget, a chat box, a checkout extension — and a full-page redirect would break the parent application's state. Third, when the product is white-labeled: a B2B dashboard sold to enterprises that need their end-users to never see a vendor's domain. In all three cases, the redirect-based hosted flow is the wrong tool, and an embeddable authentication surface is the right one.

EmbedAuth is opinionated about this. We don't ask you to choose between embedded and hosted — we treat embedded as the primary mode and design every feature around it. The iframe surface, the postMessage protocol, the JWKS endpoint, the theming engine, the multi-tenant isolation model: all of it exists so you can put auth inside your product without writing the cross-origin plumbing yourself.

Under The Hood

Technical architecture

How a request flows from the embedded iframe through token exchange to an authenticated API call — and the isolation guarantees at each hop.

Diagram 1 — End-to-end embed flow
PARENT PAGE
yourapp.com
Your SPA or server-rendered app
<iframe src="...">
onMessage(event)
AUTH IFRAME
embedauth.com
Isolated origin
POST /signin
sign RS256 JWT
postMessage(tokens)
YOUR API
api.yourapp.com
Verifies JWT via JWKS
Authorization: Bearer
GET /.well-known/jwks.json
1.Your page embeds the widget with your app's client ID.
2.The user signs in inside the frame; credentials never touch your page.
3.The widget posts the tokens to your page, on an allowed domain only; your API verifies the JWT via JWKS.

Iframe isolation

The login surface runs on a separate origin from your application. Browser same-origin policy prevents the parent from reading the iframe's DOM, cookies, or localStorage, so a compromised parent page cannot read what the user types into the login form. The widget is served with a Content-Security-Policy that refuses every script except its own bundle and, when you enable it, Google's reCAPTCHA.

Token handling

Access tokens are RS256-signed JWTs with a one-hour default lifetime. They includesub,aud,iss, and the app's client ID. Verification is stateless: your API fetches the public JWKS once, caches it, and verifies signatures locally. No round trips back to EmbedAuth on the hot path.

No third-party cookies

Sign-in doesn't depend on a cookie set inside the frame. The widget hands its result to your page as a postMessage, so third-party cookie blocking in Safari, Firefox and Chrome doesn't get in the way. What happens next is yours to decide: keep the tokens in memory, or exchange them for a first-party session on your own domain.

Session management

Refresh tokens are single-use. Posting one to /refresh returns a new access token and a new refresh token, and the old one stops working. If a spent token is ever presented again, EmbedAuth revokes the whole token family — so when a refresh token is copied, the session ends as soon as both copies are used. Sessions can also be revoked one by one from the dashboard.

Cross-domain security

Once you list your app's domains, the message that carries tokens is posted with an explicit target origin — never "*" — and only to a page on one of those domains. A client ID copied onto someone else's site renders the form but hands its tokens to nobody. Your page should still check event.origin before trusting any message.

Diagram 2 — Token lifecycle
Sign in
POST /signin
Access JWT
1 h default TTL
API call
Bearer header
Refresh
POST /refresh
Rotate refresh
one-time-use
Reuse detected
family revoked
The Hard Parts

Why embedded auth is harder than it looks

The browser is actively hostile to most of what embedded authentication needs to do. Here's the terrain.

Browser restrictions

Same-origin policy is the foundation of web security — and the main obstacle to embedding anything that needs identity. Cross-origin iframes can't read parent state, can't share cookies by default, and modern browsers increasingly enforceSameSite=Laxas the default for cookies, breaking naive cross-site session models. Working with these restrictions (instead of trying to defeat them) is the entire game.

Third-party cookie restrictions

Safari (ITP) blocks third-party cookies by default, Firefox partitions them per site, and Chrome lets users block them. Any auth provider relying on a session cookie set on its own domain — read from inside a cross-site iframe — breaks for many of their users. The way out is not to depend on that cookie at all: pass the result of sign-in to the parent page as a message, and let the application keep its own first-party session.

OAuth complexity

OAuth 2.0 has a redirect-based mental model baked in. Doing a Google sign-in from inside an iframe means popping a new window (because Google refuses to be framed), running PKCE, catching the redirect on a callback page, and getting the result back to the original iframe. Each provider has its own quirks — which flows it supports, which profile fields it returns, and whether it says the email address is verified.

Security tradeoffs

Every UX shortcut chips away at the security boundary. Storing tokens in localStorage makes XSS catastrophic. Posting tokens to any page that frames the form hands them to whoever copies your embed code. Loosening script-src to allow inline scripts turns injected markup into running code. The right answer is rarely the simple one: short-lived access tokens, refresh tokens that rotate on every use, strict origin allowlists, and a refusal to ship features that require relaxing any of it.

The honest version: most of the engineering work in an auth provider isn't the login form — it's the cross-origin choreography, the cookie strategy, the OAuth quirks, and the constant browser-vendor moving target. That's the work EmbedAuth absorbs so your team doesn't have to.

Who It's For

Use cases

Where embedded auth pays off the most.

SaaS products

Modern web apps that want a polished login experience without redirecting users off-domain. Email/password, magic links, and social — all themed to match the product.

Project management, analytics, dev tools

B2B dashboards

Customer-facing dashboards and admin consoles that should feel like one product. The sign-in form matches your UI, and auth events and audit logs show who signed in and when.

Admin consoles, billing portals, partner hubs

Embedded widgets

Chat boxes, support widgets, checkout extensions, and other surfaces that live inside someone else's page. A full-page redirect would break the host application.

Intercom-style chat, Stripe-style checkout

White-label products

Software resold under your customer's brand. The form sits inside the customer's page with no EmbedAuth branding, and transactional emails carry the app's own brand name.

Agency platforms, OEM software, reseller portals

Partner portals

Portals for suppliers, partners or customers, where each audience gets its own app: a separate user pool, separate branding and its own allowed domains.

Supplier portals, partner ecosystems

Multi-tenant marketplaces

Platforms where each merchant or workspace needs its own user pool and branding. EmbedAuth's multi-tenant model gives every app isolated users, themes and auth event history.

Marketplaces, communities, course platforms
How We Compare

EmbedAuth vs the alternatives

A direct comparison against the three most common identity options. We've tried to be fair — every product here has things it does well.

EmbedAuth vs Auth0

CapabilityEmbedAuthAuth0
Embedded iframe (first-class)
Primary mode
Universal Login is hosted
Hosted login redirect
Embed-first
Custom domains
Multi-tenant (orgs / apps)
RS256 JWTs + JWKS endpoint
Theming via settings + CSS
Branding API + limited CSS
SAML SSO
MFA / passkeys
Pricing predictability
Flat tiers
MAU-based

EmbedAuth vs Clerk

CapabilityEmbedAuthClerk
Framework-agnostic embed
React-first SDK
Iframe-based isolation
UI components run in-page
React / Next.js components
Copy-paste examples in the docs
White-label / brand removal
Paid tier
JWT verification via JWKS
MFA / passkeys
Pricing for high-volume MAU
Flat tiers
MAU-priced

EmbedAuth vs Firebase Authentication

CapabilityEmbedAuthFirebase Auth
Embedded iframe UI
FirebaseUI is in-page only
Vendor-neutral (no GCP lock-in)
Multi-tenant per-app branding
Identity Platform tier
Custom domains
OIDC / SAML support
Google & Discord OAuth
Identity Platform tier
Tight integration with Firestore / FCM
Per-app theming dashboard
Last updated 2026. Vendor capabilities change frequently — verify on the respective product pages before making a purchase decision.

Everything you need

Powerful auth features, zero backend work.

Embed in 2 Lines

Add an iframe to your HTML and listen for postMessage events. That's it.

Full CSS Control

Match your brand with theme settings, custom CSS, a live preview and a gallery of preset themes.

RS256 JWT + JWKS

Industry-standard signed tokens. Verify with our public JWKS endpoint.

Multi-Tenant

One dashboard, many apps. Each with its own users, themes, and settings.

5 Auth Methods

Email/password, magic links, email OTP, Google OAuth, Discord OAuth — toggle per app.

Security Defaults

Rate limiting, account lockout, audit logs, encrypted secrets, CSP headers.

Frequently Asked

Technical FAQ

Answers to the questions engineers actually ask when evaluating an embedded auth provider.

Is iframe-based authentication secure?
It can be, when the boundaries are drawn carefully. The widget runs on EmbedAuth's origin, so scripts on your page cannot read the form while the user types a password or code. Once you list your app's domains, the widget hands tokens only to a page on one of those domains, so a site that copies your client ID and frames the widget receives nothing. Your backend then verifies every token's RS256 signature before trusting it. What stays your responsibility is your own page: tokens delivered to it need the same protection as in any single-page app.
How does OAuth sign-in work inside an iframe?
Google and Discord refuse to render their login pages inside an iframe, so the widget opens the provider in a popup. EmbedAuth's callback page completes the exchange on the server — with PKCE for Google and a state check for both providers — and hands the result back to the widget, which posts the tokens to your page exactly as it does after a password sign-in.
Which sign-in methods does EmbedAuth support?
Email and password, magic links, six-digit one-time codes sent by email, and OAuth with Google and Discord. Each method is switched on or off per app, and the widget only shows the ones you enable.
Does EmbedAuth support SAML, enterprise SSO or multi-factor authentication?
Not today. EmbedAuth covers passwords, magic links, email codes, and Google and Discord OAuth. There is no SAML, SCIM provisioning, TOTP or passkey support. If you need any of those now, a broader identity platform is the better fit — the comparison pages say where each alternative is stronger.
What does my page receive after a successful sign-in?
An authframe:auth-success message carrying a signed access token (an RS256 JWT), a refresh token, and a small user object with the user's ID, email, name and avatar. When the app has domains configured, the message is posted only to a page on one of them. Send the access token to your backend and verify it there; don't make authorization decisions from the user object.
How do I verify tokens on my backend?
Your backend fetches the public keys from /.well-known/jwks.json?clientId=YOUR_CLIENT_ID — the response may be cached for an hour — and verifies signatures locally with any standard JWT library, checking the issuer, the audience (your client ID) and the expiry. Nothing calls back to EmbedAuth on each request. The docs have complete examples for Node.js, Python and Go.
How long do tokens last?
By default an access token lasts one hour and a refresh token 30 days. Every refresh issues a new refresh token with a fresh 30-day window, so an active user stays signed in while an idle one eventually signs in again. Each app's overview page in the dashboard shows the lifetimes in effect.
How are refresh tokens handled?
A refresh token is a long random string, and EmbedAuth stores only a hash of it. Each one works once: posting it with your client ID to /api/embed/auth/refresh returns a new access token and a new refresh token, and the old one stops working. If a token that was already used is presented again, EmbedAuth assumes it was copied and revokes every token descended from the same sign-in. Because the refresh token is handed to your page, treat it like a password — keep it in memory or pass it to your own server rather than writing it to localStorage.
What happens if a browser blocks third-party cookies?
Sign-in doesn't depend on cookies set inside the frame. The result reaches your page as a postMessage, not as a cookie the browser has to send cross-site, so third-party cookie blocking in Safari, Firefox or Chrome doesn't stop users from signing in.
Can I customize the look and feel of the embed?
Yes. Each app has a theme covering colours, corner radius, fonts and spacing density, and you can start from a gallery of preset themes. Paid plans add a CSS editor for anything the theme settings don't reach; custom CSS is sanitised before it is served. The dashboard shows every change in a live preview of the widget.
Is EmbedAuth multi-tenant?
Yes. An organization can run several apps — how many depends on the plan — and each app has its own users, theme, allowed domains, sign-in methods and OAuth providers, email designs and reCAPTCHA settings. Users of one app are not visible from another.
How do I sign a user out?
Signing out is your app's decision: discard the access and refresh tokens you hold and end your own session. A refresh token you stop using simply expires at the end of its lifetime. To cut a session off on the server — for a lost laptop, say — revoke it from the app's Sessions page in the dashboard.
What about XSS on my own page?
XSS on the page that receives the tokens is dangerous with any browser-based sign-in: a script running there can read whatever your page holds, including the refresh token if you keep it in the browser. Keep tokens in memory rather than localStorage, move the refresh token to your own server where you can, and set a strict Content-Security-Policy. The sign-in form itself stays out of reach, because it runs on EmbedAuth's origin.
Does EmbedAuth support webhooks?
Yes, with a narrow scope today: webhooks fire on email-and-password sign-ups (user.signup) and sign-ins (user.login), when a user changes or deletes their account in the account widget (user.updated, user.deleted), and when a privacy complaint arrives for an app in UK GDPR mode (privacy.complaint_received). Each request is signed with HMAC-SHA256 in the X-Webhook-Signature header, and every delivery is logged with its status code and response. Failed deliveries are not retried, so treat webhooks as notifications rather than as the only record of a sign-up.
How do you handle rate limiting and brute-force attempts?
Every sign-in, sign-up and account-recovery endpoint is rate-limited per IP address, and the endpoints that send email are also limited per email address, so nobody can flood an inbox with codes. After 10 wrong passwords in a row an account is locked for five minutes. Sign-ins — successful and failed — are recorded as auth events, and reCAPTCHA v3 can be switched on per app to screen out automated sign-ups.
What data is in the access JWT?
The registered claims iss, aud (your client ID), sub (the user's ID), iat and exp, plus email, name, avatar, clientId and authMethod, which records how the user signed in. Nothing else is added, which keeps tokens small.
How do magic links and one-time codes work?
A magic link is a single-use sign-in link that expires after 15 minutes; a one-time code is six digits and expires after 10 minutes. Both are sent to the user's email address, both are stored only as hashes, and both stop working once used. Completing either one also confirms that the user owns the address.
Is there an SDK?
No package is needed. The integration is an iframe plus a message listener, so it works with any framework, or none. The docs include ready-to-copy components for React, Next.js and Vue, and token verification code for Node.js, Python and Go.
How does pricing scale?
There is a free plan, and paid plans are tiered by the number of apps and the volume of monthly auth events rather than by monthly active users. Current plans and limits are on the pricing page.
What if I outgrow EmbedAuth?
The code on your side is not tied to EmbedAuth: your backend verifies standard RS256 JWTs against a JWKS URL, which any identity provider can replace. There is no self-serve export of user records yet — if you need a copy of your users' data, contact support.