Your build runs inside a frame on the /games/:slug page — the player never leaves avogames. For that to be worth anything, the frame has to arrive knowing who the player is; otherwise every visit is anonymous and your game has to show its own login screen. This page explains what avogames passes into the iframe, what each value is for, and the two places your game must use it: the backend, then the frontend.
What arrives in the iframe URL
When a signed-in player opens your game, the play page appends five parameters to the iframe URL you registered on Upload New Version:
https://<your-game>.example.com/play
?user_id=507f1f77bcf86cd799439011
&login_session=$2a$10$N9qo8uLOickgx2ZMRZoMye.qXjzLpQ9xDdLVsqRfX1YcD6R0dYzXy
&token=4f8b2c1a9d3e5f7a8b9c0d1e
&platform=avogames
&from_global=1
user_id— the player's avogames account id. You bind your own account to it (store it asglobal_id) so the same avogames account always lands on the same game account.login_session— a bcrypt hash ofuser_id + SESSION_KEY, minted at login. It proves the identity was issued by avogames, and your server can verify it offline — no network call — because you hold the sharedSESSION_KEYas yourbcrypt_hash_key.token— a random value stored on the account and replaced on every login. It proves this URL came from a live session rather than a replayed old link; as soon as the player signs in again, a leaked URL stops working.platform=avogames— which publisher issued the identity, so your server knows where the account actually lives. The same build can be reachable from several publishers.from_global=1— tells your server to enter its platform-login path when it sees these parameters.
A signed-out visitor gets the iframe URL without any of this, and your game shows whatever it shows anonymously. In practice avogames promotes first-time visitors to a guest account on Play, so the frame rarely loads with no identity at all.
Your backend: verify, then bind
The request that loads your page receives these parameters on the query string. A minimal implementation, in the order every game should follow:
// A minimal backend, in the order every game should follow.
// bcrypt_hash_key must be avogames' SESSION_KEY; secret_key must be SECRET_KEY.
// 1. Verify the identity offline - no network call.
const valid = await bcrypt.compare(user_id + bcrypt_hash_key, login_session);
if (!valid) return reject_login("not minted by avogames");
// 2. A returning player already has a local account bound by global_id.
let local = await User.findOne({ global_id: user_id });
if (!local) {
// 3. First visit: fetch the authoritative account from avogames.
const resp = await axios.post("https://avogames.com/get_user_data", {
api_key: secret_key,
user_id: user_id,
token: token, // proves this URL came from a live session
});
if (resp.data.error) return reject_login("no such account or stale token");
// 4. Create your account from resp.data.user (or link it onto an
// existing one by email / steam_id) and bind it:
local = await User.create(
Object.assign(pick(resp.data.user, ["username", "email"]), {
global_id: user_id,
})
);
}
// 5. Mint your own session and hand it to the frontend.
const session_id = await bcrypt.hash(server_session_secret + local.id, 10);
render_page({ user_id: local.id, session_id: session_id });
The account fetch in step 3 is the one server-to-server call your backend makes. Its contract:
POST /get_user_data (also aliased /validate_global_token)
in: { api_key, user_id, token }
out: { user: { id, username, email, ... } } success
{ error: 1 } wrong api_key or missing user_id
{ error: 2 } unknown user, or token is not current
Your frontend: carry the session
Render the server's session into the page (hidden inputs are the usual way) and have the boot code read them into the request params every request makes. Your backend re-validates them each time. That is the second half of the session: the backend established it on the request that loaded the page; the frontend carries it on every request after.
<!-- your play page renders the session the backend just minted -->
<input type="hidden" id="user_id" value="<%= req.session.user_id %>">
<input type="hidden" id="session_id" value="<%= req.session.session_id %>">
// boot code reads them once and sends them with every request
var socket_params = {
user_id: document.getElementById("user_id").value,
session_id: document.getElementById("session_id").value,
};
No cookies cross the domain boundary. The frame is a different origin, so the browser would drop avogames' cookies anyway (SameSite). The identity travels in the URL and is verified server-to-server — keep your own session cookie on your own domain with normal SameSite settings.
The two shared secrets
SESSION_KEY(avogames) = yourbcrypt_hash_key— verifieslogin_sessionwithout a network call.SECRET_KEY(avogames) = yourapi_key— authenticates your server-to-server/get_user_datacall.
Related articles
Still stuck? Submit a request and include your game's dashboard link.