Keeping your API key off the browser

For the Eagle 3D Streaming Web SDK on a page the public can reach. The demo puts your API key in the browser, which is fine while you are building and not fine once anyone can visit. This is the one change that fixes it.

What the problem actually is

Anything in a web page can be read by anyone who visits it. Your API key sits in scripts/sdk-config.js, so it is one click of View Source away — and it does not expire.

What someone can do with it

Start streaming sessions billed to your account, from their own page, for as long as the key exists. They do not need your app, your Control Panel login or anything else. The first sign is usually the bill, or your concurrent-user limit being used up by people you cannot identify.

The fix, in one sentence

Your server holds the key and asks Eagle 3D Streaming for a token; the browser only ever receives the token.

Browser                Your server              Eagle 3D Streaming
   │  "I'd like to stream" │                            │
   ├──────────────────────▶│  API key + app details     │
   │                       ├───────────────────────────▶│
   │                       │        short-lived token   │
   │   just the token      │◀───────────────────────────┤
   │◀──────────────────────┤                            │
   │  start the session with that token                 │
   ├───────────────────────────────────────────────────▶│
Why the token is safe to send and the key is not

A token expires in about a minute and covers a single session. Someone who copies one gets, at most, the session you were already going to give them. A key has no expiry and no limit.

What you change

One function. requestSessionToken() in scripts/sdk-token.js currently calls the Eagle 3D Streaming token endpoint directly, with your key in the header. Point it at your own endpoint instead:

async function requestSessionToken() {
    // Your server. It knows the key; this page never does.
    const response = await fetch("/api/streaming-token", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ /* whatever your server needs to decide */ })
    });
    if (!response.ok) return null;
    return await response.json();
}

Nothing else moves. main(), your callbacks, your controls and your markup are untouched — they receive the same token they always did, from a different place.

What your server does

It makes the request the browser used to make, with the key it holds privately, and returns the answer:

// POST https://token.eagle3dstreaming.com/api/v2/token/create
//   Authorization: Auth <your streaming API key>
{
  "object": {
    "core": { domain, userName, appName, configurationName, version },
    "configurationToOverride": {}
  },
  "expiry": 60000,
  "client": "your-username"   // the same account username as core.userName
}

Who is allowed to start a stream at all

This is the part that is easy to miss, because it does not look like a security question until somebody asks it: who, exactly, can start a session billed to your account? The answer depends entirely on how you embedded the stream, and the three methods are not close.

How you embedded itWho can start a stream
Streaming URL Anyone with the link. You can add a password or restrict the link in the configuration, but the link itself is what grants access — and you cannot revoke it for one person.
iframe The same. Your page can require a login before it shows the frame, but the streaming URL inside is still a working link to anyone who reads your page source.
Web SDK Your backend decides. The session starts from a short-lived token, so your server chooses who gets one, when, and for which app.

Checking you have actually done it

Open your page and search the source for your key

View Source, then the Network tab. Your key must appear in neither. If it is still in sdk-config.js because the file was copied across, the page will work perfectly and still be leaking it — working is not the test here, absence is.

Last updated