Player sign-in
Three ways
| Way | For whom | What you do |
|---|---|---|
| Email code | Anyone. They type their email and the 6-digit code we send: no password, no new tab. | Nothing. It's the default. |
| Anyone with a Google account, in a small popup, once Google sign-in is switched on for Ocheso. Hidden inside apps, where Google refuses webviews. | Nothing. | |
| Your login | Your own users, already signed in to your site or app. They're never asked anything. | One endpoint on your server and one call on your pages. Below. |
Each tournament has an identity setting: accounts (email code, and Google when switched on; the default), platform (your users only) or any (both). It is set when the tournament is made and frozen once anyone has entered.
Your login, step by step
Your server vouches for the user; Ocheso trusts it for ten minutes. The widget never sees your secret key, and your pages never see anything but a short-lived token.
- 1. A player presses Join in the widget.
- 2. The loader calls your fetchToken(), which asks your server.
- 3. Your server: POST /v1/platform/player-tokens with ochs_… → ochp_…
- 4. The loader hands ochp_… to the widget by postMessage, never in a URL.
- 5. The widget trades it for its own session and joins.
Make a secret key
In Studio, Developers → Keys → New key, with the Players scope. It starts
ochs_and is shown once. Store it on your server asOCHESO_SECRET_KEY, never in a page or an app: the API refuses it from a browser.Add an endpoint that hands out player tokens
For whoever is signed in to your site.
external_idis your id for them and must never change: the same id is the same Ocheso player, with their rating and history. Answer 401 when nobody is signed in.// Your server. The secret key stays in your environment - never in a page or an app. // Ocheso.identify() on your pages calls this for whoever is signed in to YOUR site. app.get("/ocheso-token", async (req, res) => { if (!req.user) return res.status(401).end(); // not signed in here: Ocheso shows your login link const r = await fetch("https://api.ocheso.com/v1/platform/player-tokens", { method: "POST", headers: { Authorization: `Bearer ${process.env.OCHESO_SECRET_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ external_id: String(req.user.id), // your id for them; it never changes name: req.user.displayName, }), }); if (!r.ok) return res.status(502).json({ error: (await r.json()).detail }); const { token } = await r.json(); // ochp_…, good for 10 minutes res.set("Cache-Control", "no-store").json({ token }); });Tell the widget where to ask
On pages where your users can be signed in. The loader asks only when someone joins or opens their game, and one token serves every widget on the page.
loginUrlis where the widget's Sign in button sends people your endpoint doesn't know.HTML<script>!function(w){var o=w.Ocheso=w.Ocheso||{q:[]};["ready","mount","identify","logout","setTheme","on","scan"].forEach(function(m){o[m]=o[m]||function(){o.q.push([m,arguments])}})}(window)</script> <script> Ocheso.identify(async function () { // Your endpoint (the server snippet): a token for whoever is signed in to your site. const res = await fetch("/ocheso-token", { credentials: "same-origin" }); if (!res.ok) return null; // not signed in: Ocheso offers your sign-in page return (await res.json()).token; }, { loginUrl: "/login" }); </script> <script async src="https://www.ocheso.com/embed/v1.js"></script>Test it
Studio's Developerspage has a box to paste a token from your endpoint: it checks the token and throws it away. Then set a tournament's sign-in to Your platform's login and press Join on your own page.
Tokens and how long they last
| Token | Lives | Where |
|---|---|---|
| ochs_… | until revoked | Your server only. Mints player tokens and calls the platform API. |
| ochp_… | 10 minutes | Your page, briefly. Reusable until it expires, so one fetch serves every widget. |
| oche_… | 12 h (your users) · 7 days (accounts) | Inside the widget. Ocheso's own session; you never handle it. |
- The loader keeps a token for 8 minutes, so it is never used with less than two left. If the widget is refused anyway, it asks again with reason
expiredand the loader skips its copy. - Return
null(or a 401 from your endpoint) when nobody is signed in. The widget then offers Sign in, which goes to yourloginUrl, and anidentityRequiredevent fires in case your page would rather open its own sign-in. - When your user signs out, call
Ocheso.logout()so the widget forgets them too. - Serve tokens with
Cache-Control: no-store. A token cached by a CDN would sign the next visitor in as the last one.
Your users on Ocheso
Each user you sign in becomes a player that belongs to your organisation only. They have no Ocheso password, their ratings count inside your organisation, and they appear only in your tournaments. What you send is what we keep:
| Field | Notes |
|---|---|
| external_id | Required, 1–128 characters. Your stable id for them. |
| name | Required, up to 60 characters. Shown on boards and tables; send the current one with each token. |
| Optional. Shown to your admins only. Never used to send mail or to match an Ocheso account. | |
| avatar_url · country · rating | Optional. https URL, two-letter country code, and a starting rating (100–3500). |
| tournament · enter | Optional. Limit the token to one tournament, and enter the player at the same time. |
To erase someone, call DELETE /v1/platform/players/{external_id}: their name becomes "Deleted player" in past results and the link to your id is gone. See the HTTP API.
Sign-in and allowed domains
A widget checks which page it is on before it accepts anyone. On a domain that isn't in your allowed list it still shows the tournament, but read-only, with a link to join on the hosted page. That keeps a copied snippet on someone else's site from signing your users in.
Not working?
Most sign-in problems are a domain missing from the list (www versus the bare domain, a port on a test server) or a cached token. Troubleshooting has both.