Technical

Secure Agent Embedding: No API Keys in the Browser

Learn why exposing API keys in agent embeds is dangerous and how hiroi secures widgets with domain safelists and HMAC-signed tokens.

Secure Agent Embedding: No API Keys in the Browser

Open your browser's developer tools on any website with an agent widget. View the page source or inspect network requests. There is a disturbingly good chance you will find an API key sitting in plain text — in a script tag attribute, in a JavaScript variable, or in the headers of outgoing requests.

This is not a theoretical risk. Exposed API keys are actively exploited. Bots scrape public repositories and websites for keys. Once found, your API key can be used to run up charges on your account, access your data, or impersonate your service.

hiroi was designed from the start to never expose secrets in the browser. Here is how it works and why it matters.

The Problem with API Key Embeds

Many agent platforms give you an embed snippet that looks like this:

<!-- DON'T DO THIS -->
<script src="https://example.com/widget.js"
        data-api-key="sk-abc123secretkey456"></script>

That API key is now:

  • Visible in your page source — anyone can View Source and copy it
  • Visible in network requests — developer tools show it in request headers
  • Committed to version control — if your site code is on GitHub, the key is in your repo
  • Cached by CDNs and proxies — intermediate servers may log the key
  • Indexed by scrapers — automated tools scan the web specifically for exposed keys

Once someone has your API key, they can make requests as if they were your application. Depending on the platform, this might mean generating AI responses on your dime, accessing your conversation history, or modifying your agent's configuration.

How hiroi Secures Widget Authentication

hiroi offers two authentication methods. Neither exposes secrets in the browser.

Method 1: Domain Safelist (No Backend Required)

This is the default method and the right choice for most websites. Your embed code contains only a site ID — a public identifier, not a secret:

<script src="https://hiroi.ai/widget.js"
        data-site-id="your-site-uuid-here"></script>

The site ID is safe to share publicly. It is just an identifier, like a username. It cannot be used to authenticate requests on its own.

How authentication works:

  1. The widget makes a request to the hiroi API
  2. The browser automatically includes the Origin header (e.g., https://yourdomain.com)
  3. The server checks the Origin against your configured domain safelist
  4. If the origin matches an allowed domain, the request proceeds
  5. If it does not match, the request is rejected

Why this is secure:

  • Browsers enforce the Origin header — JavaScript cannot spoof it
  • The Origin header is set by the browser itself, not by application code
  • Cross-origin requests from unauthorized domains are rejected server-side
  • Even if someone copies your site ID, they cannot use it from a different domain

Important detail: hiroi uses exact domain matching, not wildcards. You need to add both example.com and www.example.com if your site is accessible at both. This prevents subdomain takeover attacks where an attacker could create malicious.example.com and match a wildcard pattern.

Method 2: Session-Signed Tokens (For Apps with Auth)

If your application has user authentication (a SaaS product, a members-only site, a logged-in experience), session-signed tokens let you tie agent conversations to specific users.

The flow:

  1. Your backend calls the hiroi API with a server secret to create a session token
  2. hiroi returns a short-lived, HMAC-SHA256 signed token
  3. Your backend passes the token to your frontend
  4. The widget uses the token for authentication
// Your backend (Node.js example)
const response = await fetch('https://hiroi.ai/api/widget/session/create', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Server-Key': 'ss_your_server_secret'  // NEVER in the browser
  },
  body: JSON.stringify({
    user_id: 'user123',
    user_name: 'Jane Smith',
    ttl_minutes: 20
  })
});
const { session_token } = await response.json();
// Pass session_token to your frontend template
<!-- Frontend — token only, no secrets -->
<script src="https://hiroi.ai/widget.js"
        data-session-token="TOKEN_FROM_YOUR_BACKEND"></script>

Security properties of session tokens:

  • Server secret never reaches the browser — it stays on your backend
  • Tokens are HMAC-SHA256 signed — they cannot be forged or tampered with
  • Tokens are site-specific — a token for Site A cannot be used on Site B
  • Tokens expire — configurable TTL from 1 to 120 minutes (default 20)
  • Tokens are single-use — once a session is established, the token cannot be reused

Choosing Between the Two

Consideration Domain Safelist Session Tokens
Backend required No Yes
User attribution Anonymous (visitor ID) Explicit (user_id, user_name)
Setup complexity Paste script tag Backend API call
Best for Public sites, blogs, landing pages SaaS, member portals, apps
Security model Origin header validation HMAC-signed tokens

Most sites should start with the domain safelist. It requires zero backend work and is secure for public-facing websites. Move to session tokens when you need to associate conversations with authenticated users.

How Competitors Handle This (Poorly)

A survey of popular agent widgets reveals common anti-patterns:

  • API key in script tag — the key is in the HTML, visible to anyone
  • API key in JavaScript bundle — slightly hidden but trivially extractable
  • "Publishable" keys with no server validation — the key is meant to be public but still grants API access
  • Client-side token generation — the secret is embedded in client JavaScript to generate tokens locally

Some platforms acknowledge the risk and recommend "restricting" the key to certain domains — but this restriction is enforced by the platform's API, not by the browser. An attacker can use curl or Postman from any machine and include a spoofed Referer header. Only the Origin header, enforced by browsers at the protocol level, provides reliable domain validation.

Practical Security Checklist

If you are embedding any third-party widget (not just AI agents), verify these:

  • [ ] No API keys, secrets, or tokens in your HTML source
  • [ ] No secrets in JavaScript variables or config objects
  • [ ] Network requests do not include secrets in URLs or headers visible to client code
  • [ ] Authentication is validated server-side, not client-side
  • [ ] Tokens expire and cannot be reused indefinitely
  • [ ] Domain restrictions use Origin header validation, not Referer

The Bottom Line

Your agent widget should not be a security liability. Embedding a script tag on your site should not mean broadcasting your API credentials to the world.

hiroi's approach is simple: secrets stay on the server, always. The browser gets either a public identifier validated against the Origin header, or a short-lived signed token generated by your backend. No API keys in the source, no secrets in network requests, no credentials to rotate when someone views your page source.

Security should not be an advanced feature. It should be the default.

Try hiroi free.

Deploy an AI agent across chat, voice, email, and SMS — no credit card required.