# Signed-in players

The widgets show a signed-in player their own progress, items and rewards. They learn who the player
is from your site, once per page, without GA ever seeing your passwords or your session cookies.

## 3.1 How it works

1. The resolver calls **your token endpoint** on your own origin with a plain `GET`. The browser
   sends your session cookies with it, because it is your origin.
2. Your endpoint answers with a short-lived **operator token** for the signed-in player (§3.2), or
   with an error when nobody is signed in.
3. The resolver exchanges the operator token for a GA player token and hands it to every widget of
   the page. It lives in the page's memory only — never in storage, cookies or the address bar — and
   is renewed in the background before it expires.

You tell us the path of the endpoint in the form (`players.token_endpoint`), e.g.
`/api/ga-promo/token`. It must be on the same origin as the page that shows the widgets.

## 3.2 Your token endpoint

```
GET /api/ga-promo/token
Accept: application/json
Cookie: <your session>
```

| Your answer | When |
| :--- | :--- |
| `200` `{"operatorToken":"<JWT>"}` | a player is signed in |
| `401` (any body) | nobody is signed in; the widgets show what a guest sees |

The JWT is signed **on your backend**, never in the browser:

- algorithm `HS256`, key: the same operator signing key you already hold for Operator API v2
  (the wallet signing key; its secret is shown once when you rotate it in the Operator Portal) —
  not a separate embed-only secret (§10.2); `none` and other algorithms are refused;
- the HMAC key is the secret string **exactly as issued, as its UTF-8 bytes** — do not
  base64-decode or hex-decode it, do not trim or add anything;
- no `kid` header is needed and none is read: we check the token against each of your active
  signing keys, so it keeps verifying while two keys overlap during a rotation. The key id is used
  by the wallet requests only;
- `operator_id` — your GA operator id, as registered for you;
- `external_player_id` — your player's id: **the same value you send as `player_ref`** in events
  (§5.1). Another value makes another player;
- `brand` — the code of the player's brand (§5.1). **Required** when you have several brands (a group
  operator): the brand ref, the same value as `x-brand-id`. **Must be absent** when you have one brand;
- `exp` — required; keep it a few minutes: the widgets exchange a token once; every game launch (§4.3)
  asks for a fresh one.

Optional: `currency`, `locale`, `ext_param` (your context for the game launch, §2.3).

```json
{
  "operator_id": "<your GA operator id>",
  "external_player_id": "u-123",
  "brand": "partner-sandbox-eu",
  "exp": 1790000000
}
```

The endpoint is the only place where your site vouches for a player. Answer only for the player of
the session the request carries.

## 3.2.1 A site with the session in an `Authorization` header

If your players sign in with a token in the `Authorization` header rather than a cookie, the
endpoint of §3.2 does not fit: the browser does not send it the session. Hand the token to the
widgets from your page's JavaScript instead.

`resolver.js` accepts a token provider. Set it in one of two ways:

- `window.Ludarium.setTokenProvider(fn)`;
- `window.LudariumConfig = { tokenProvider: fn }`, defined before `resolver.js` loads.

`fn` returns a string or a `Promise<string>`: the operator JWT, built as described in §3.2. With the
launch mode `sdk-modal` (§4.1) the same provider supplies the token of every game launch.

```html
<script>
  window.LudariumConfig = { tokenProvider: () => auth.getAccessToken() };
</script>
<script src="https://ludarium.rexplay.site/resolver.js" data-client-id="..."></script>
<script>
  window.Ludarium.setTokenProvider(async () => auth.getAccessToken());
</script>
```

- The provider is used instead of `GET` to the token endpoint; its result goes to `sessionUrl` as
  `operatorToken`.
- The provider is called again when the token expires.
- If it throws or returns an empty string, the widgets show what a guest sees, the same as for a
  `401` from the endpoint (§3.4).
- Without a provider nothing changes: the endpoint of §3.2 is used.
- The token is accepted only from your page's JavaScript, never from a slot's `data-*` attributes
  or from the manifest.
- Set the provider before the first widget mounts.

## 3.3 A player we have not seen yet

A player who signs in before their first bet is known from the first signed token: the widgets show
their own, empty progress. Events you send later (§5) count for the same player, because both name
them by the same `external_player_id` / `player_ref` under the same brand.

## 3.4 Guests

Without a token endpoint, when nobody is signed in, when the endpoint fails or takes longer than
1.5 s, or when the token is not valid, the widgets show what a guest sees: public campaigns, closed
items, game rows. Promo banners show the campaign without any progress, and their button reads
“Sign in to collect”. An action that needs a player — that button included — raises
`ig:login-required` on your page (§2.3). Nothing breaks and nothing waits.
