Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Oturumlar ve CSRF

Giriş yapan oyuncuya bir oturum verilir. Bu sayfa oturumun nasıl taşındığını, ne kadar yaşadığını, nasıl yenilenip kapatıldığını ve durum değiştiren isteklerde zorunlu olan CSRF (çapraz site istek sahteciliği) korumasını anlatır. SDK kullanıyorsanız çoğunu sizin yerinize yönetir; ham REST yazıyorsanız bu sayfa sözleşmedir.

Oturum nasıl taşınır?

Sunucu oturumu iki yerden birinden okur; önce çerez, yoksa Authorization başlığı:

KanalKim kullanır?Biçim
ÇerezTarayıcı oyunları__Secure-crx.session=<64 hex> (HttpOnly; Secure; SameSite=Lax; Path=/)
BearerÇerezsiz istemciler (komut satırı, bazı yerel istemciler)Authorization: Bearer <session_token> (giriş yanıtındaki opak belirteç)

Her oyuncu isteği ayrıca x-crx-env: <ortam-kimliği> taşır. Bearer yolunda taşınan opak oturum belirtecidir; erişim JWT’si değildir (o, oyun sunucunuzun çevrimdışı doğrulaması içindir: Sunucuda doğrulama).

Oturumu okuma

GET /v1/auth/session
x-crx-env: <ortam-kimliği>
{
  "environment_id": "0192…",
  "player_id": "0192…",
  "session_id": "0192…",
  "is_guest": false,
  "active_organization_id": null,
  "displayName": "Ayşe",
  "email": "[email protected]"
}

email misafir ve e-postasız (yalnız Google) hesaplarda null’dır. Yanıt kişisel veri taşır ve Cache-Control: private, no-store ile gelir. Oturum yoksa, iptal edilmişse, süresi dolmuşsa ya da hesap banlıysa 401 döner (ayrım sızmaz).

Süreler

ÖğeSüre
Oturum ve yenileme penceresi30 gün, kayan
Mutlak üst sınır180 gün (oturum ailesinin başlangıcından; hiçbir yolla aşılmaz)
Erişim JWT’si15 dakika
Çerez Max-Age15 552 000 sn (180 gün); yalnız üst sınırdır, karar sunucudadır
CSRF çerezi15 dakika
  • Kayma: oyuncu oturumla günde en az bir kez istek yaptığında oturum bitişi 30 gün ileri alınır (üst sınırı aşmadan). Her yenileme (refresh) de süreyi 30 güne çeker.
  • 30 gün hiç istek yapmayan oturum ölür. 180. günde aktif oturum bile biter; oyuncu yeniden giriş yapar (misafir, aynı sırla aynı hesaba döner).
  • Gerçek zamanlı bağlantıyı açık tutmak oturumu uzatmaz.

Yenileme (refresh)

POST /v1/auth/refresh
content-type: application/json
x-crx-env: <ortam-kimliği>
x-csrf-token: <csrf-belirteci>

{}

Yenileme belirteci gövdede ({"refresh_token":"…"}) ya da __Secure-crx.refresh çerezinde taşınır (gövde öncelikli; çerez varsa gövde boş olabilir). Yanıt 200 ve yeni oturum ve yenileme çifti (çerezler de yenilenir; ortak giriş zarfı). Eski yenileme belirteci kullanılmış sayılır.

  • Yeniden kullanım tespiti: kullanılmış bir yenileme belirtecinin tekrar sunulması tüm oturum ailesini iptal eder (çalıntı belirteç savunması).
  • Hata: 403 (belirteç geçersiz, süresi dolmuş, yeniden kullanılmış ya da CSRF reddi).
  • SDK’lar yenilemeyi gerektiğinde kendisi yapar; siz çağırmazsınız.

Çıkış

POST /v1/auth/logout
content-type: application/json
x-crx-env: <ortam-kimliği>
x-csrf-token: <csrf-belirteci>

{}

204; oturum iptal edilir ve çerezler silinir (Max-Age=0). Çağrı idempotenttir: bilinmeyen belirteç sessizce başarılı olur. Yenileme belirteci hiç yoksa (çerez ve gövde boşsa) 400 döner.

Parola değiştirme, parola sıfırlama ve operatör banı da oturumları iptal eder; ilgili sayfalara bakın.

Tek aktif oturum (isteğe bağlı)

Panelde Oyun ayarları › Giriş yöntemleri › Giriş ayarları › Tek aktif oturum anahtarı (yalnız süper yönetici), ortam başına açılır. Açıkken oyuncuya yeni oturum veren her giriş yolu, yeni oturum yazıldıktan sonra oyuncunun diğer oturumlarını iptal eder (“en son giriş kazanır”).

  • Anahtar açıldığı anda açık oturumları kapatmaz; yalnız bir sonraki girişten itibaren uygulanır.
  • Yenileme (refresh) bu politikayı tetiklemez.
  • Diğer cihazdaki oturum bir sonraki istekte 401 alır; SDK durumu reauthRequired olur. Oyununuz bu durumda “başka bir cihazdan giriş yapıldı” gibi bir ekran gösterebilir.

CSRF çift gönderim

Çerezle taşınan oturum, tarayıcı tarafından başka sitelerin isteklerine de otomatik eklenir. Bunu önlemek için durum değiştiren isteklerde çift gönderim (double-submit) CSRF koruması uygulanır: aynı belirteç hem çerezde hem başlıkta gelmelidir.

Kapsam. Aşağıdaki isteklerde zorunludur: /v1/auth/* altındaki tüm GET dışı istekler (giriş uçları dahil); oyuncu oturumuyla çağrılan eklenti komutları (ve POST ile çağrılan sorgular); oyuncu mağaza satın alma (yalnız çerez oturumunda; Authorization: Bearer oturumunda yalnız Origin ve Fetch Metadata denetimi uygulanır); gerçek zamanlı bilet isteği. GET istekleri muaftır. Sunucu anahtarıyla yapılan çağrılar CSRF’ye tabi değildir.

Akış.

  1. GET /v1/auth/csrf çağrılır. Yanıt: gövde {"token":"<hex>"}, başlık x-csrf-token: <aynı belirteç> ve Set-Cookie: __Secure-crx.csrf=<mühürlü grant>.
  2. Her durum değiştiren istek, çerezi (__Secure-crx.csrf) ve başlığı (x-csrf-token) birlikte gönderir. Tarayıcı çerezi kendisi ekler; başlığı kodunuz ekler.
  3. Başarılı her istekte belirteç döner (rotate): yanıt, yeni x-csrf-token başlığı ve yeni Set-Cookie taşır. Bir sonraki istekte yenisini kullanın.
  4. Belirteç tek kullanımlıktır. Eski belirteçle tekrar gönderilen istek 403 alır (yeniden oynatma koruması).
# Belirteç al
TOKEN="$(curl -sS -c "$JAR" "$OPEN_PLAY_URL/v1/auth/csrf" | jq -r .token)"

# Kullan; yanıt başlığındaki yeni belirteci bir sonraki istek için sakla
curl -sS -D headers.txt -b "$JAR" -c "$JAR" -X POST "$OPEN_PLAY_URL/v1/auth/logout" \
  -H "content-type: application/json" -H "x-crx-env: $OPEN_PLAY_ENV" -H "x-csrf-token: $TOKEN" -d '{}'
TOKEN="$(grep -i '^x-csrf-token:' headers.txt | awk '{print $2}' | tr -d '\r')"

Dikkat edilecekler:

  • Eşzamanlı iki mutasyonu aynı belirteçle göndermeyin; biri başarılı olunca diğeri 403 alır. İstekleri sıraya koyun. SDK’lar bunu tek bir kilitle yapar.
  • CSRF çerezi 15 dakika yaşar; süresi dolunca 403 alırsınız. Taze belirteç alıp bir kez yeniden deneyin. 403 düz metindir ve CSRF ile yetki reddi arasında ayrım yapmaz; yanıtta yeni bir x-csrf-token başlığı gelmediyse bunun CSRF katmanı reddi olduğunu anlarsınız.
  • Origin denetimi CSRF’nin ilk katmanıdır: tarayıcı isteğinin Origin alan adı ortama bağlı olmalıdır. Bkz. Mimari ve barındırma.
  • Çerezsiz (Bearer) istemciler de /v1/auth/* ve eklenti uçlarında CSRF çerezini ve başlığını taşımalıdır (mağaza satın almadaki Bearer kolu istisnadır). Unity SDK’sı bunu yerel derlemede çerezleri kendisi yöneterek, WebGL’de tarayıcı çerezleriyle yapar.

SDK ve oturum durumları

TypeScript session.state.status değerleri:

DurumAnlamı
signedOutOturum yok
authenticatingGiriş ya da geri yükleme sürüyor
authenticatedOturum açık; playerId, sessionId, isGuest, displayName, email dolu
refreshingYenileme sürüyor
reauthRequiredOturum kaybedildi ya da yenilenemedi; yeniden giriş gerekir
signingOut / logoutUncertainÇıkış sürüyor / sonucu belirsiz (yeniden denenebilir)
bannedHesap banlı; terminal

Unity’de karşılığı SessionStatus (SignedOut, Authenticating, Authenticated, Refreshing, ReauthRequired, SigningOut, LogoutUncertain, Banned, Disposed) değerleridir; durum değişimleri Session.StateChanged olayıyla gelir.

import { CrxError, ErrorKind, createPlayerClient, type PlayerClient, type SessionState } from "@ferdiunal/open-play-ts";

/** Oturum durumunu arayüze bağlar; `state.status` değerleri kapalı bir kümedir. */
export function watchSession(client: PlayerClient, render: (label: string) => void): () => void {
  const label = (state: SessionState): string => {
    switch (state.status) {
      case "authenticated": return state.isGuest === true ? "Misafir" : (state.displayName ?? "Oyuncu");
      case "banned": return "Hesap askıya alındı";
      case "reauthRequired": return "Yeniden giriş gerekiyor";
      case "signedOut": return "Giriş yapılmadı";
      default: return "…"; // authenticating, refreshing, signingOut, logoutUncertain
    }
  };
  render(label(client.session.state));
  return client.session.subscribe((state) => render(label(state))); // dönen işlev aboneliği kapatır
}

/** Sayfa kapanırken: sunucu oturumunu kapatmak için `logout`, yalnız yerel kaynakları bırakmak için `dispose`. */
export async function leave(client: PlayerClient, signOut: boolean): Promise<void> {
  try {
    if (signOut) await client.session.logout();
  } catch (error) {
    if (!(error instanceof CrxError && error.kind === ErrorKind.UnknownOutcome)) throw error;
    // Yanıt kaybolduysa sunucu oturumu kapanmamış olabilir (durum `logoutUncertain`); gerekirse logout'u yeniden deneyin.
  } finally {
    await client.dispose();
  }
}

export function createClient(environmentId: string): PlayerClient {
  return createPlayerClient({ baseUrl: window.location.origin, environmentId });
}

dispose() yalnızca yerel kaynakları bırakır; sunucu oturumunu kapatmak için önce logout() çağırın. AuthenticatedSession.email yalnız oyuncunun kendi ekranı içindir; günlüğe ve analitiğe yazmayın.