FourPlay

Session API

Endpoints para criar sessões de jogo e validar tokens JWT

Visão Geral

A Session API possui dois endpoints:

MétodoPathAuth HMACDescrição
POST/api/operator/session/createSimCria uma sessão e retorna o token
POST/api/operator/session/validateNãoValida um token e retorna seus claims

Base URL: https://mines.fourplay.studio


POST /api/operator/session/create

Cria uma sessão de jogo para um jogador. Retorna um JWT que o jogador usa para acessar o jogo.

Request

Headers:

HeaderObrigatórioValor
Content-TypeSimapplication/json
X-Operator-IdSimSua API Key (op_live_...)
X-TimestampSimTimestamp em milissegundos
X-SignatureSimHMAC-SHA256 calculado

Body:

{
  "externalPlayerId": "player_123",
  "username": "João Silva"
}
CampoTipoObrigatórioDescrição
externalPlayerIdstringSimID do jogador no seu sistema (identificador único)
usernamestringNãoNome de exibição do jogador no jogo

Response

200 OK:

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwbGF5ZXJJZCI6IjAxOTM0..."
}
CampoTipoDescrição
tokenstringJWT de sessão — use para construir a URL do jogo

Após receber o token, construa a URL do jogo:

https://mines.fourplay.studio/?token={token}

Erros possíveis:

HTTPCorpoCausa
400{ "message": "Missing required fields" }externalPlayerId ausente no body
401{ "message": "Invalid signature" }HMAC incorreto, timestamp fora da janela de 5 min
401{ "message": "Operator not found" }API Key não reconhecida
403{ "message": "Operator is inactive" }Conta de operador inativa ou suspensa

Exemplos Completos

#!/bin/bash
API_KEY="op_live_..."
SECRET_KEY="sk_live_..."
BODY='{"externalPlayerId":"player_123","username":"João Silva"}'
TIMESTAMP=$(date +%s%3N)
DATA="${API_KEY}${TIMESTAMP}${BODY}"
SIGNATURE=$(echo -n "$DATA" | openssl dgst -sha256 -hmac "$SECRET_KEY" | cut -d' ' -f2)

curl -s -X POST "https://mines.fourplay.studio/api/operator/session/create" \
  -H "Content-Type: application/json" \
  -H "X-Operator-Id: $API_KEY" \
  -H "X-Timestamp: $TIMESTAMP" \
  -H "X-Signature: $SIGNATURE" \
  -d "$BODY"

# Resposta: {"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}
const crypto = require('crypto');

const API_KEY    = process.env.FOURPLAY_API_KEY;
const SECRET_KEY = process.env.FOURPLAY_SECRET_KEY;

async function createSession(externalPlayerId, username) {
  const payload   = { externalPlayerId, ...(username && { username }) };
  const body      = JSON.stringify(payload);
  const timestamp = Date.now().toString();
  const data      = API_KEY + timestamp + body;
  const signature = crypto.createHmac('sha256', SECRET_KEY).update(data).digest('hex');

  const res = await fetch('https://mines.fourplay.studio/api/operator/session/create', {
    method: 'POST',
    headers: {
      'Content-Type':  'application/json',
      'X-Operator-Id': API_KEY,
      'X-Timestamp':   timestamp,
      'X-Signature':   signature,
    },
    body,
  });

  const result = await res.json();
  if (!res.ok) throw new Error(result.message ?? 'Session creation failed');
  return result; // { token: "eyJ..." }
}

// Uso em Express:
app.get('/play', async (req, res) => {
  const { token } = await createSession(req.user.id, req.user.name);
  res.redirect(`https://mines.fourplay.studio/?token=${token}`);
});
<?php
function createSession(string $externalPlayerId, ?string $username = null): string {
    $apiKey    = getenv('FOURPLAY_API_KEY');
    $secretKey = getenv('FOURPLAY_SECRET_KEY');

    $payload  = ['externalPlayerId' => $externalPlayerId];
    if ($username !== null) $payload['username'] = $username;
    $body     = json_encode($payload, JSON_UNESCAPED_UNICODE);

    $timestamp = (string)(int)(microtime(true) * 1000);
    $data      = $apiKey . $timestamp . $body;
    $signature = hash_hmac('sha256', $data, $secretKey);

    $ch = curl_init('https://mines.fourplay.studio/api/operator/session/create');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => $body,
        CURLOPT_HTTPHEADER     => [
            'Content-Type: application/json',
            "X-Operator-Id: {$apiKey}",
            "X-Timestamp: {$timestamp}",
            "X-Signature: {$signature}",
        ],
    ]);

    $raw      = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $result = json_decode($raw, true);
    if ($httpCode !== 200) {
        throw new RuntimeException($result['message'] ?? 'Session creation failed');
    }
    return $result['token'];
}

// Uso:
$token   = createSession($_SESSION['user_id'], $_SESSION['user_name']);
$gameUrl = "https://mines.fourplay.studio/?token={$token}";
header("Location: {$gameUrl}");
exit;
import hmac, hashlib, time, json, os
import urllib.request

API_KEY    = os.environ['FOURPLAY_API_KEY']
SECRET_KEY = os.environ['FOURPLAY_SECRET_KEY']

def create_session(external_player_id: str, username: str | None = None) -> str:
    payload = {'externalPlayerId': external_player_id}
    if username:
        payload['username'] = username
    body      = json.dumps(payload, separators=(',', ':'))
    timestamp = str(int(time.time() * 1000))
    data      = API_KEY + timestamp + body
    signature = hmac.new(SECRET_KEY.encode(), data.encode(), hashlib.sha256).hexdigest()

    req = urllib.request.Request(
        'https://mines.fourplay.studio/api/operator/session/create',
        data=body.encode(),
        headers={
            'Content-Type':  'application/json',
            'X-Operator-Id': API_KEY,
            'X-Timestamp':   timestamp,
            'X-Signature':   signature,
        },
        method='POST',
    )
    with urllib.request.urlopen(req) as resp:
        return json.loads(resp.read())['token']

token    = create_session('player_123', 'João Silva')
game_url = f'https://mines.fourplay.studio/?token={token}'
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;

public class FourPlayClient(string apiKey, string secretKey, HttpClient http)
{
    public async Task<string> CreateSessionAsync(string externalPlayerId, string? username = null)
    {
        var payload = username != null
            ? (object)new { externalPlayerId, username }
            : new { externalPlayerId };
        var body      = JsonSerializer.Serialize(payload);
        var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString();
        var data      = apiKey + timestamp + body;
        using var mac = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey));
        var sig       = Convert.ToHexString(mac.ComputeHash(Encoding.UTF8.GetBytes(data))).ToLower();

        var req = new HttpRequestMessage(HttpMethod.Post,
            "https://mines.fourplay.studio/api/operator/session/create")
        {
            Content = new StringContent(body, Encoding.UTF8, "application/json")
        };
        req.Headers.Add("X-Operator-Id", apiKey);
        req.Headers.Add("X-Timestamp",   timestamp);
        req.Headers.Add("X-Signature",   sig);

        var resp   = await http.SendAsync(req);
        var json   = await resp.Content.ReadAsStringAsync();
        var result = JsonSerializer.Deserialize<JsonElement>(json);

        if (!resp.IsSuccessStatusCode)
            throw new Exception(result.TryGetProperty("message", out var msg)
                ? msg.GetString() : "Session creation failed");

        return result.GetProperty("token").GetString()!;
    }
}

// Uso em ASP.NET:
var token   = await fourplayClient.CreateSessionAsync(user.Id, user.Name);
var gameUrl = $"https://mines.fourplay.studio/?token={token}";
return Redirect(gameUrl);

POST /api/operator/session/validate

Valida um token JWT e retorna os claims internos. Útil para inspecionar uma sessão existente.

Este endpoint não requer autenticação HMAC — apenas o token no body.

Request

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
CampoTipoObrigatórioDescrição
tokenstringSimJWT de sessão

Response

200 OK:

{
  "playerId":   "01934c2e-789b-7000-bf1d-4f86ee0d6721",
  "operatorId": "01934c2e-5a1f-7000-9b3e-2d7c89f1a234",
  "username":   "João Silva",
  "isDemo":     false
}
CampoTipoDescrição
playerIdstringUUID interno do jogador no banco do Mines
operatorIdstringUUID interno do operador no banco do Mines
usernamestring?Nome de exibição (apenas se enviado na criação)
isDemobool?true para sessões de demonstração

Erros possíveis:

HTTPCorpoCausa
400{ "message": "Token is required" }Token ausente no body
401{ "message": "Invalid token" }Token malformado ou expirado

Exemplos

TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

curl -s -X POST "https://mines.fourplay.studio/api/operator/session/validate" \
  -H "Content-Type: application/json" \
  -d "{\"token\":\"$TOKEN\"}"
async function validateSession(token) {
  const res = await fetch('https://mines.fourplay.studio/api/operator/session/validate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ token }),
  });
  const result = await res.json();
  if (!res.ok) throw new Error(result.message ?? 'Validation failed');
  return result; // { playerId, operatorId, username?, isDemo? }
}
<?php
function validateSession(string $token): array {
    $ch = curl_init('https://mines.fourplay.studio/api/operator/session/validate');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => json_encode(['token' => $token]),
        CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
    ]);
    $raw      = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $result = json_decode($raw, true);
    if ($httpCode !== 200) {
        throw new RuntimeException($result['message'] ?? 'Validation failed');
    }
    return $result;
}
import json, urllib.request

def validate_session(token: str) -> dict:
    body = json.dumps({'token': token}).encode()
    req  = urllib.request.Request(
        'https://mines.fourplay.studio/api/operator/session/validate',
        data=body,
        headers={'Content-Type': 'application/json'},
        method='POST',
    )
    with urllib.request.urlopen(req) as resp:
        return json.loads(resp.read())
async Task<JsonElement> ValidateSessionAsync(string token)
{
    var body = JsonSerializer.Serialize(new { token });
    var req  = new HttpRequestMessage(HttpMethod.Post,
        "https://mines.fourplay.studio/api/operator/session/validate")
    {
        Content = new StringContent(body, Encoding.UTF8, "application/json")
    };
    var resp   = await http.SendAsync(req);
    var json   = await resp.Content.ReadAsStringAsync();
    var result = JsonSerializer.Deserialize<JsonElement>(json);
    if (!resp.IsSuccessStatusCode)
        throw new Exception(result.TryGetProperty("message", out var msg)
            ? msg.GetString() : "Validation failed");
    return result;
}

Notas Importantes

Token JWT (24 horas)

O token expira 24 horas após a criação. O jogo detecta automaticamente a expiração. Se o jogador quiser jogar após a expiração, o operador deve criar uma nova sessão.

Decodificando o JWT localmente

Se precisar inspecionar o token sem chamar o endpoint /validate:

function decodeJwt(token) {
  const [, payload] = token.split('.');
  return JSON.parse(Buffer.from(payload, 'base64url').toString());
}

const claims = decodeJwt(token);
// { playerId, operatorId, username?, isDemo?, iat, exp }

Decodificar localmente não valida a assinatura. Use apenas para leitura de claims — nunca para autorização.

On this page