Oyuncuyu oyun sunucunuzda doğrulama
Oyun sunucunuz, kendisine gelen bir istek ya da bağlantının hangi oyuncuya ait olduğunu bilmek ister. Bu sayfa iki yolu anlatır: Open Play’in imzaladığı erişim JWT’sini çevrimdışı doğrulamak (JWKS ile) ve oturumu Open Play’e sorarak doğrulamak. Hangi yolun sizin için uygun olduğunu sayfanın sonundaki karşılaştırma tablosu söyler.
Uyarı
Bugünkü SDK sınırı: TypeScript ve Unity SDK’ları erişim JWT’sini uygulamaya açmaz; belirteç yalnızca ham REST giriş yanıtının gövdesinde (
tokens.access_token) bulunur. SDK kullanan bir istemci JWT’yi sunucunuza veremez. SDK’lı istemciler için aşağıdaki Yol B (oturum doğrulama) kullanılabilir. Bu eksiklik kayıtlıdır.
Yol A: erişim JWT’sini çevrimdışı doğrulama
Belirteç
Giriş, kayıt, yenileme ve OAuth callback yanıtları tokens.access_token döndürür: EdDSA (Ed25519) ile imzalı, 15 dakikalık compact JWT. Belirteç yalnız gövdededir; çerezde taşınmaz. Süresi dolunca POST /v1/auth/refresh yenisini verir.
Başlık: {"alg":"EdDSA","typ":"JWT","kid":"<anahtar-kimliği>"}. Yük:
| Alan | Anlamı |
|---|---|
sub | Oyuncu kimliği (UUID) |
env | Ortam kimliği (UUID) |
sid | Oturum kimliği (UUID) |
org | Etkin organizasyon (yoksa alan yok) |
iat, exp | Üretim ve bitiş anı: Unix milisaniye (saniye değil) |
Uyarı
iatveexpmilisaniyedir ve belirteçteiss/audyoktur. Standart JWT kütüphaneleriexp’i saniye sayar; milisaniye değerini uzak gelecekte bir tarih sanıp süresi dolmuş belirteci kabul eder. Kütüphane kullanırsanız imzayı ona doğrulatın amaexpdenetimini kendiniz yapın (exp > Date.now()).
Anahtarlar (JWKS)
curl -sS "$OPEN_PLAY_URL/v1/auth/jwks"
{"keys":[{"kty":"OKP","crv":"Ed25519","kid":"<anahtar-kimliği>","use":"sig","alg":"EdDSA","x":"<base64url genel anahtar>"}]}
Kimlik gerekmez. Sunucu imzalayıcıyı yayınlamadıysa 404 döner. Yanıtı önbelleğe alın ve yalnızca belirteçteki kid listede yoksa yenileyin; her istekte çekmeyin.
Doğrulama adımları
- Belirteci üç parçaya ayırın; başlıktaki
algdeğeriEdDSAolmalı, aksi halde reddedin. kidile JWKS’ten anahtarı bulun; yoksa JWKS’i bir kez yenileyin.- İmzayı
header.payloadmetni üzerinde Ed25519 ile doğrulayın. exp > şimdi (ms)olduğunu denetleyin.envdeğerini kendi ortam kimliğinizle karşılaştırın.
Uyarı
Aynı Open Play sunucusundaki bütün oyunların belirteçleri aynı JWKS ile doğrulanır.
envkarşılaştırmasını atlarsanız, başka bir oyunun oyuncusu sizin sunucunuzda geçerli görünür. Bu adım zorunludur.
Node.js (yalnız yerleşik node:crypto, bağımlılık yok):
import { createPublicKey, verify, type JsonWebKey } from "node:crypto";
/** Open Play erişim JWT'sinin (EdDSA) yük alanları. `iat` ve `exp` saniye değil, Unix MİLİSANİYEDİR. */
export interface AccessClaims {
readonly sub: string; // oyuncu kimliği
readonly env: string; // ortam kimliği
readonly sid: string; // oturum kimliği
readonly org?: string;
readonly iat: number;
readonly exp: number;
}
interface Jwks {
readonly keys: ReadonlyArray<JsonWebKey & { readonly kid?: string }>;
}
function decode(part: string): Buffer {
return Buffer.from(part, "base64url");
}
/**
* Oyuncunun erişim JWT'sini çevrimdışı doğrular: imza, süre ve ortam eşleşmesi.
* JWKS'i her istekte çekmeyin; `jwks` parametresini önbellekten verin (anahtar döndürülürse bilinmeyen `kid` gelince yenileyin).
*/
export function verifyAccessToken(token: string, jwks: Jwks, expectedEnvironmentId: string, nowMs: number = Date.now()): AccessClaims {
const parts = token.split(".");
const [header, payload, signature] = parts;
if (parts.length !== 3 || header === undefined || payload === undefined || signature === undefined) {
throw new Error("Biçimi bozuk JWT.");
}
const head = JSON.parse(decode(header).toString("utf8")) as { alg?: string; kid?: string };
if (head.alg !== "EdDSA") throw new Error("Beklenmeyen algoritma.");
const jwk = jwks.keys.find((key) => key.kid === head.kid);
if (jwk === undefined) throw new Error("Bilinmeyen anahtar kimliği; JWKS'i yenileyin.");
const publicKey = createPublicKey({ key: jwk, format: "jwk" });
const signed = Buffer.from(`${header}.${payload}`, "utf8");
if (!verify(null, signed, publicKey, decode(signature))) throw new Error("İmza doğrulanamadı.");
const claims = JSON.parse(decode(payload).toString("utf8")) as AccessClaims;
if (claims.exp <= nowMs) throw new Error("Belirtecin süresi dolmuş.");
// JWKS tüm oyunlar için ortaktır: başka bir ortamın oyuncusunu kabul etmemek için `env` mutlaka karşılaştırılır.
if (claims.env !== expectedEnvironmentId) throw new Error("Belirteç başka bir ortama ait.");
return claims;
}
/** JWKS'i çeker (kimlik gerekmez). */
export async function fetchJwks(baseUrl: string): Promise<Jwks> {
const response = await fetch(`${baseUrl}/v1/auth/jwks`);
if (!response.ok) throw new Error(`JWKS okunamadı: ${response.status}`);
return (await response.json()) as Jwks;
}
Sınırlar
- İptal görmez. Çevrimdışı doğrulama oturumun kapatıldığını, iptal edildiğini ya da oyuncunun banlandığını bilemez; belirteç
exp’ine kadar (en çok 15 dakika) geçerli görünür. İptal duyarlı olması gereken işlemlerde Yol B’yi kullanın ya da bu 15 dakikalık pencereyi kabul edin. - Anahtar döndürme. Anahtar değişimi sunucuyu yeniden başlatır; yeni
kidJWKS’te görünür. Önbelleğiniz bilinmeyenkidgörünce yenilenmelidir.
Yol B: oturumu Open Play’e sorma
Oyuncunun oturum belirtecini (çerez değeri ya da Authorization: Bearer belirteci) ve ortam kimliğini Open Play’e ileterek oyuncuyu çözersiniz. Tarayıcı oyununda oyunun origin’ine giden isteklerde oturum çerezi (__Secure-crx.session) zaten gelir; sunucunuz bu değeri okuyup Open Play’e Bearer olarak iletebilir.
curl -sS "$OPEN_PLAY_URL/v1/auth/session" \
-H "Authorization: Bearer $PLAYER_SESSION_TOKEN" \
-H "x-crx-env: $OPEN_PLAY_ENV"
200 ve {"environment_id","player_id","session_id","is_guest","displayName","email",…}. İptal edilmiş, süresi dolmuş ya da banlı oturum 401 verir. GET olduğu için CSRF belirteci gerekmez.
Uyarı
Oturum belirteci bir yetkidir. Açık metin günlüğe yazmayın, başka servislere yaymayın ve yalnızca TLS üzerinden iletin.
Hangi yolu seçmeliyim?
| Yol A: JWT çevrimdışı | Yol B: oturum sorgusu | |
|---|---|---|
| Open Play’e istek | Yok (JWKS önbellekte) | Her doğrulamada bir istek |
| İptal/ban görür | Hayır (en çok 15 dk gecikme) | Evet |
| Belirteç kaynağı | Yalnız ham REST giriş yanıtı; SDK vermez | Çerez ya da Bearer oturum belirteci; SDK’lı istemciyle de çalışır (tarayıcıda çerez) |
| Ortam doğrulaması | env talebini siz karşılaştırırsınız | Ortam, istekteki x-crx-env ile sınanır |
| Ne zaman? | Çok yüksek istek hacmi, iptal gecikmesi kabul edilebilir | Varsayılan öneri; oyuncu kimliği kritik işlemler |
Sunucu-sunucu çağrılarında oyuncu yerine sunucu anahtarı kullanılır (Kavramlar).