Pular para o conteúdo principal

Tokens de viewer

POST /battles/{id}/viewer-tokens escopo: viewer:mint → 200

Um token de viewer é a credencial que um browser apresenta para assistir a uma batalha ao vivo. É a única credencial do SlotBattle que chega a código não confiável, e por isso ela é pequena, curta e com escopo de exatamente uma batalha.

{
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsImtpZCI6ImsxIn0...",
"expires_at": "2026-08-03T03:55:42Z",
"ws_url": "wss://slotbattle.example.com/ws?battle_id=btl_CIkE8lctOrFDe1hJlG0T",
"ws_subprotocol": "slotbattle.jwt"
}

O que tem dentro

Um JWT assinado com Ed25519. Decodificado, de uma instância real:

// header
{ "alg": "EdDSA", "typ": "JWT", "kid": "k1" }

// claims
{
"iss": "slotbattle",
"aud": "slotbattle-ws",
"tenant_id": "acme",
"battle_id": "btl_CIkE8lctOrFDe1hJlG0T",
"sub": "docs verification",
"role": "viewer",
"iat": 1785728442,
"exp": 1785729342
}

O tempo de vida é de 15 minutos. battle_id está embutido, então um token de uma batalha é inútil em outra. O kid nomeia a chave de assinatura, que é o que torna a rotação sobrevivível. Veja Chaves e credenciais.

Quando você precisa desta rota

POST /battles e POST /battles/{id}/seats já retornam um token. Chame esta rota quando:

  • O token expirou. Um lobby que ficou vinte minutos aberto sobreviveu ao próprio token.
  • Um espectador novo chega. Alguém assistindo a uma batalha que não criou nem entrou.
  • Você está re-renderizando. Um reload de página passados os 15 minutos.

Emita um por espectador. Eles são baratos, e o token é a única granularidade de revogação disponível: um token compartilhado por uma plateia não pode ser retirado de uma pessoa só.

Nunca coloque o token numa URL

O token pertence a um corpo de resposta que o seu front end lê, e depois ao header de subprotocolo do WebSocket. Colocá-lo no ws_url como parâmetro de query, ou em qualquer URL, vaza para log de acesso, log de proxy e histórico do browser.

A API nunca faz isso: ws_url carrega só battle_id, e o token é um campo separado.

Falhas

StatusCódigoSignificado
403forbiddenA chave não tem viewer:mint
404not_foundBatalha inexistente para este casino
500internalA instância não tem chave de assinatura
503unavailableA instância não tem emissor de token, ou ainda está subindo

O 500 é problema de configuração, não bug

{ "error": { "code": "internal", "message": "failed to mint viewer token" } }

A instância não tem chave de assinatura de viewer. Batalhas continuam abrindo e assentos continuam enchendo; só a visualização ao vivo fica indisponível, e o ws_token fica ausente das respostas de criação e de assento porque o campo é opcional.

Reporte ao seu host, citando essa mensagem exata. A chave de assinatura tem escopo de plataforma, já que uma chave assina para todo casino da instância, então configurá-la é trabalho deles.

Lidando com expiração num front end

O token sobrevive à maioria dos lobbies, mas não a todos. Um padrão que funciona:

  1. Use o ws_token da resposta de criação ou de assento na primeira conexão.
  2. Guarde ws_token_expires_at. Reemita quando faltar menos de um minuto para expirar, ou quando o socket fechar e for preciso reconectar.
  3. Passe a emissão pelo seu backend, que guarda a chave de API. Nunca chame o SlotBattle do browser.

O tutorial implementa exatamente isso.

Próximo