How to Prevent Key Leakage in Embedded AI Avatar Kiosks with iframe Auth and Signed URLs

Prevent API key leakage in embedded AI avatar kiosks with iframe isolation, signed short-lived URLs, origin allowlists, and server-side auth.
Introduction
Embedding a realtime AI avatar kiosk looks simple from the outside: render a video face, connect it to an agent, and let users talk. The security problem is that the browser is the worst place to keep long-lived secrets. If you put an API key in frontend JavaScript, it will leak sooner or later via source maps, browser extensions, debugging tools, copied bundles, or a compromised page origin.
This is especially risky for kiosks and in-browser interactive agents, where the UI is often embedded in third-party sites, run on shared devices, or deployed across many customer domains. By the end of this post, you should be able to reason about where keys can leak, design an iframe-based embed that never exposes your upstream API key, and use signed, short-lived URLs to authorize access without turning your frontend into a secret store.
Why browser-exposed API keys are the wrong primitive
The core issue is that an API key is a bearer credential. Anyone who has it can usually act as you until it is revoked or expires. In a browser app, you are fighting the platform:
The JavaScript bundle is public.
Network calls can be inspected and replayed.
Third-party scripts can read your DOM and JS state.
Users can copy the page, view source, or instrument requests.
For an embedded avatar kiosk, that means you should avoid using your upstream service key directly in the page, even if the UI is “internal” or “only used in a controlled environment.” Shared devices are not trusted hosts.
The better model is to split authorization into two layers:
Backend-to-provider auth: your server talks to the avatar API with a real API key.
Browser-to-embed auth: the browser gets only a narrowly scoped, short-lived token or signed URL for one specific embed session.
That distinction matters because the browser never needs the same privileges as your backend.
Use an iframe boundary as the security boundary
An iframe is not just a rendering trick. It gives you a clean origin boundary. If the avatar experience is hosted from a dedicated origin, the parent page cannot directly read its storage, intercept its JS variables, or casually scrape the token used to bootstrap the session.
For kiosk-style embeds, the usual pattern is:
The parent application requests an embed session from your backend.
Your backend verifies the parent origin or customer identity.
Your backend returns a signed URL for a single iframe session.
The iframe loads the avatar experience from its own origin and uses that signed URL to initialize the session.
The important property is that the browser never sees a reusable upstream API key. It only sees a session-scoped artifact that is useless outside the allowed origin, time window, or IP range.
When this is done properly, the iframe becomes the trust boundary, not a cosmetic wrapper.
What signed URLs should actually protect
Signed URLs are often described too loosely. In practice, they should encode the minimum claims needed for the embed to start, and nothing more. For avatar kiosks, that usually means:
Origin allowlist: only approved parent origins may embed or initialize the session.
Expiration: the token should die quickly, usually in minutes.
Scope: one token should map to one embed, session, or kiosk instance.
Rate limits: constrain per-IP, per-origin, and per-embed usage where possible.
Instructions and voice: if you pass custom behavior into the avatar, treat that as scoped configuration, not as a secret.
Do not confuse “signed” with “encrypted.” A signed URL can still be read by the client. Its security comes from integrity and expiry, not confidentiality. That is fine for parameters like avatar ID, allowed origin, or session ID. It is not fine for upstream secrets.
A practical embed flow
Here is the flow I would recommend for a customer-managed kiosk embed:
Your app backend authenticates the user or kiosk operator.
Your backend calls your avatar platform to create or authorize a session.
The platform returns a signed iframe URL or a session bootstrap token.
Your frontend renders an iframe with that URL.
The iframe establishes the realtime media session from its own origin.
That architecture lets you enforce authorization once, on the server, and then move only scoped credentials to the browser.
Short-lived session example
Below is a minimal example of server-side session creation using a Python SDK. The exact field names depend on the SDK version and the session model in the docs, but the pattern is what matters: create a session on the backend, then pass only a signed URL or ephemeral token to the browser.
Then on the frontend:
The key point is that the browser only receives the scoped session artifact. Your upstream API key stays on the server, where it belongs.
Where people still leak keys
Even teams that avoid hardcoding the key in the client still leak it through the surrounding system. Common failure modes include:
Logging full Authorization headers in reverse proxies or application logs.
Embedding secrets in query strings that get captured by analytics or referer logs.
Shipping long-lived tokens to the browser “just for the kiosk.”
Reusing one token across customers or across environments.
Trusting the parent page to police itself without validating origin server-side.
The browser is not the only risk. Once a token can be replayed, it can leak through logs, support tools, screenshots, or misconfigured observability pipelines. Signed URLs help, but only if you keep them short-lived and single-purpose.
How to think about origin checks and replay resistance
Origin allowlists are useful, but they are not a complete security model. A malicious page can still try to load your iframe if it knows the URL. That is why the token itself must be tied to the allowed origin and must expire quickly.
For stronger replay resistance, combine multiple controls:
Validate the parent origin on your backend before issuing the embed token.
Bind the token to a single session identifier.
Use a short TTL so stolen tokens go stale fast.
Reject reuse if your workload can tolerate single-use tokens.
Rate limit session bootstrap requests by IP and customer account.
If you are building a kiosk, also assume that the device itself is semi-trusted at best. A kiosk can be rebooted, inspected, or proxied. Your security should degrade gracefully if the box is compromised.
Where Protoface fits
This is the exact kind of problem Protoface is designed to solve on the embed side: customer-managed iframe experiences with no backend API key in the browser, parent-origin allowlisting, per-embed voice and instructions, and rate limits by IP and duration. That means you can expose an interactive avatar in a kiosk without distributing your upstream credentials to every client.
If you are wiring this into a backend workflow, the REST API and Python SDK are the pieces to look at first, because they let you create and manage avatars and sessions server-side and then hand the browser only a scoped embed URL or token. The implementation details are in the docs, but the security model is straightforward: server holds the real key, browser holds a temporary capability.
For the broader developer setup, the docs at docs.protoface.com are the place to confirm the exact session fields and embed parameters. If you are using the LiveKit path, the plugin flow is also relevant: the avatar is attached to a voice agent server-side, which keeps the media/control plane out of the browser entirely.
Trade-offs and operational advice
Signed URLs and iframe auth are not magic. They trade a little complexity for a much better security posture. A few practical notes:
Clock skew matters: keep expirations generous enough to survive normal skew, but still short.
Debugging can be harder: when a token fails, check origin, expiry, and replay logs first.
Observability must be scrubbed: strip tokens from logs and tracing spans.
Don’t over-scope: if a token can create sessions, it should not also manage billing or keys.
There is also a human factor: if your team can create arbitrary long-lived embed URLs during development, those patterns will migrate to production. Make the secure path the easy path.
Conclusion
The right way to prevent key leakage in embedded AI avatar kiosks is to stop treating the browser like a trusted secret holder. Keep the upstream API key on the server, issue short-lived signed URLs or session tokens, bind them to allowed origins, and use the iframe as the isolation boundary.
If you need a concrete implementation, start with the docs, then wire the backend session creation into your embed flow and test the failure cases: expired tokens, disallowed origins, and replayed URLs. That is the difference between a demo that works and a kiosk you can actually ship.
