GameVox ile Giriş Yap
İnsanların web sitene veya uygulamana GameVox hesaplarıyla giriş yapmasını sağla. Bu, Discord'unkiyle aynı yapıdaki standart OAuth2 authorization-code akışıdır ve bot gerektirmez: uygulaman kullanıcının hesabının belirli kısımlarını ister, kullanıcı onaylar ve backend'in bir token alır.
Ne işe yarar
- Giriş ve hesap bağlama. Kullanıcının kim olduğunu öğren (değişmeyen bir GameVox kullanıcı ID'si, kullanıcı adı, avatar) ve kullanıcı izin verirse doğrulanmış e-postasını al.
- Sunucu seçiciler ve paneller.
guildsile kullanıcının hangi sunucularda olduğunu ve hangilerinde ayarları yönetebildiğini gör. - Kullanıcı adına işlem yapmak için değil. Bir kullanıcı token'ı mesaj gönderemez, sese katılamaz veya GameVox'ta hiçbir şeyi değiştiremez. GameVox içinde işlem yapan her şey için bir bot gerekir.
Başlamadan önce
- developers.gamevox.com adresinde bir uygulama oluştur. OAuth2 sekmesindeki Client ID değeri, senin
client_iddeğerindir. - OAuth2 sekmesinde uygulamanın kullanacağı her redirect URI'yi ekle (en fazla 10). Bunlar
https://olmalı (geliştirme içinhttp://localhost/http://127.0.0.1da olur) ve#fragmentiçermemeli. Gönderdiğinredirect_uri, query string dahil bunlardan biriyle birebir eşleşmelidir. - Client secret'ı kopyala. Yalnızca bir kez, uygulama oluşturulduğunda veya secret sıfırlandığında gösterilir. Backend'inde sakla: her token isteği için gerekir.
Endpoint'ler
GET https://gamevox.com/oauth2/authorize/
POST https://app-api.gamevox.com/api/oauth2/token
POST https://app-api.gamevox.com/api/oauth2/token/revoke
GET https://app-api.gamevox.com/api/users/@me
GET https://app-api.gamevox.com/api/users/@me/guilds
GET https://app-api.gamevox.com/api/oauth2/@me
PUT https://api.gamevox.com/api/users/@me/applications/{application.id}/role-connection /oauth2/authorize/ sonundaki eğik çizgiyi silme. Token ve kullanıcı endpoint'leri /api/v10/ altında da yanıt verir; böylece yola sürüm ekleyen kütüphaneler değişiklik olmadan çalışır. Rol bağlantıları app-api üzerinde değil, api.gamevox.com üzerindedir.
1. Kullanıcıyı GameVox'a gönder
Tarayıcıyı authorize URL'sine yönlendir (popup veya iframe değil, normal bir sayfa geçişiyle):
https://gamevox.com/oauth2/authorize/
?response_type=code
&client_id=123456789012345678
&scope=identify%20email
&redirect_uri=https%3A%2F%2Fyourapp.example.com%2Fauth%2Fgamevox
&state=Zx81kq0vT2 | Parametre | Zorunlu | Açıklama |
|---|---|---|
response_type | Evet | Her zaman code. |
client_id | Evet | Uygulamanın OAuth2 sekmesindeki Client ID. |
scope | Evet | Boşlukla ayrılır (URL'de %20 veya + olarak kodlanır). Bkz. Kapsamlar. bot ekleme: bot eklenirse bu URL bunun yerine bir sunucu kurulumuna dönüşür. |
redirect_uri | Evet | Kayıtlı redirect URI'lerinden biri, birebir aynı haliyle. |
state | Hayır | Kullanıcının oturumuna bağlı, tahmin edilemeyen bir değer (en fazla 1024 karakter). Değişmeden geri gelir; siteler arası istek sahteciliğini (CSRF) önlemek için kontrol et. |
code_challenge | Hayır | İsteğe bağlı PKCE challenge'ı, 43 ile 128 karakter arası. |
code_challenge_method | Hayır | Challenge gönderiyorsan S256. plain reddedilir. |
prompt | Hayır | none, kullanıcı uygulamanı bu kapsamların tamamı için zaten onaylamışsa onay ekranını atlar. Yeni olan her şey yine sorulur. |
Kullanıcının gördüğü
- gamevox.com üzerinde bir onay ekranı: uygulamanın adı, simgesi ve açıklaması, kullanıcının giriş yaptığı hesap, neleri görebileceğinin sade bir dille listesi ve kullanıcının yönlendirileceği site.
- Kullanıcının oturumu kapalıysa Log in onu GameVox web uygulamasından geçirir ve doğrudan aynı ekrana geri getirir.
- Hizmet Şartları ve Gizlilik Politikası bağlantılarını Genel Bilgiler sekmesinde ayarlarsan bunlar düğmelerin altında görünür.
2. Callback'i işle
Kullanıcı Authorize'a tıkladıktan sonra GameVox onu bir code ile redirect URI'ne gönderir:
https://yourapp.example.com/auth/gamevox?code=3vR0b...&state=Zx81kq0vT2 Bunun yerine Cancel'a tıklarsa:
https://yourapp.example.com/auth/gamevox?error=access_denied
&error_description=The+user+denied+the+request&state=Zx81kq0vT2 state, bu tarayıcı için senin ürettiğin değer değilse callback'i reddet.- Bir code 10 dakika sonra geçerliliğini yitirir ve yalnızca bir kez çalışır. Hemen, backend'inden token'larla değiştir.
3. Code'u token'larla değiştir
Backend'inden token endpoint'ine form kodlu bir gövdeyle (JSON değil, application/x-www-form-urlencoded) POST isteği gönder:
curl -X POST https://app-api.gamevox.com/api/oauth2/token \
-u "123456789012345678:CS.your-client-secret" \
-d grant_type=authorization_code \
-d code=3vR0b... \
-d redirect_uri=https://yourapp.example.com/auth/gamevox {
"access_token": "GVO.mJ4c...",
"token_type": "Bearer",
"expires_in": 604800,
"refresh_token": "GVR.9tQe...",
"scope": "identify email"
} - Yukarıdaki gibi HTTP Basic (
client_id:client_secret) ile kimlik doğrula ya daclient_idveclient_secretdeğerlerini form alanı olarak gönder. redirect_uri, 1. adımda gönderdiğin değerle aynı olmalı. PKCE kullandıysancode_verifierekle.- Yanıtlar
Cache-Control: no-storeiçerir. Client secret'ı veya token'ları asla tarayıcı koduna ya da URL'lere koyma.
4. API'yi çağır
Access token'ı Authorization: Bearer olarak gönder. GET /users/@me için identify kapsamı gerekir:
curl https://app-api.gamevox.com/api/users/@me \
-H "Authorization: Bearer GVO.mJ4c..." {
"id": "1557709980780474368",
"gamevox_id": "edf412bc-a7b8-4f2a-a810-8b9eb8c02462",
"username": "alice",
"discriminator": "0",
"global_name": "alice",
"avatar": null,
"gamevox_avatar_url": "https://...",
"email": "alice@example.com",
"verified": true
} id, kullanıcının değişmeyen GameVox ID'sidir. Hesap kayıtlarını bu değere göre tut; kullanıcı adları değişebilir.gamevox_id, aynı kullanıcının GameVox'un yerel UUID biçimindeki karşılığıdır.emailveverifiedyalnızcaemailkapsamı varsa ve kullanıcının kayıtlı bir e-postası bulunuyorsa gelir. GameVox yalnızca doğrulanmış adresleri saklar, bu yüzdenemaildoluysaverifiedher zamantrueolur.avatarher zamannullgelir (Discord kütüphaneleri bundan bir Discord CDN bağlantısı oluştururdu). Bunun yerinegamevox_avatar_urlkullan; kullanıcının resmi yoksa bu alan hiç gönderilmez.
Kullanıcının sunucuları
GET /users/@me/guilds için guilds kapsamı gerekir:
[
{ "id": "1557445672742031360", "name": "Raid Night", "icon": null,
"owner": true, "permissions": "2251799813685247", "features": [] },
{ "id": "1557445672742031361", "name": "Guild Hall", "icon": null,
"owner": false, "permissions": "1056", "features": [] }
] permissionstam bir hesaplama değil, sunucu seçmek için bir özettir: sahip ve yöneticiler için her bit (ADMINISTRATORdahil), kullanıcının sunucu ayarlarını düzenleyebildiği yerlerdeMANAGE_GUILD(32) artıVIEW_CHANNEL(1024), diğer herkes içinVIEW_CHANNEL.- Yanıt başına en fazla
limitsunucu gelir (1 ile 200 arası, varsayılan 200), en eskiden başlayarak. Discord'daki gibi,afterveyabeforedeğerini önceki sayfadaki bir sunucununiddeğerine ayarlayarak sayfalama yap.
Geçerli yetkilendirme
GET /oauth2/@me her kapsamla çalışır ve token'ın hangi uygulamaya ait olduğunu, nelerin verildiğini ve ne zaman sona ereceğini söyler. identify varsa user da dahil edilir.
{
"application": { "id": "123456789012345678", "name": "RaidPlanner", ... },
"scopes": ["identify", "email"],
"expires": "2026-10-15T15:03:20Z",
"user": { "id": "1557709980780474368", "username": "alice", ... }
} Bağlı roller
role_connections.write ile kullanıcının senin tarafındaki hangi hesaba sahip olduğunu kaydedebilirsin. Host'a dikkat: api.gamevox.com.
curl -X PUT \
https://api.gamevox.com/api/users/@me/applications/123456789012345678/role-connection \
-H "Authorization: Bearer GVO.mJ4c..." \
-H "Content-Type: application/json" \
-d '{"platform_name": "RaidPlanner", "platform_username": "ShadowMage", "metadata": {}}' Kullanıcı bunu Settings > Authorized Apps altında “Linked as ShadowMage on RaidPlanner” olarak görür. metadata anahtarları, uygulamanın role-connection metadata'sında tanımladığın anahtarlardan olmalıdır. Aynı URL'ye yapılan GET isteği kaydı geri okur.
Yenileme
Access token'lar 7 gün geçerlidir (expires_in saniye cinsindendir). Refresh token ile yeni bir çift al:
curl -X POST https://app-api.gamevox.com/api/oauth2/token \
-u "123456789012345678:CS.your-client-secret" \
-d grant_type=refresh_token \
-d refresh_token=GVR.9tQe... - Refresh token'lar her kullanımda yenilenir. Yanıtta yeni bir refresh token bulunur; eski refresh token ve ona ait access token hemen çalışmayı bırakır, bu yüzden yeni çifti kullanmadan önce kaydet.
- Yeni token'ı daha az kapsamla sınırlamak için
scopegönderebilirsin. Kullanıcının hiç vermediği bir kapsamı istemekinvalid_scopehatasıyla başarısız olur. - Kullanılmayan bir refresh token 90 gün sonra geçerliliğini yitirir. Her kullanıcı, uygulama başına en fazla 25 aktif token çiftine sahip olabilir; 26. çift oluşturulduğunda en eskisi silinir.
İptal etme (çıkış yapma)
Kullanıcı uygulamandan çıkış yaptığında token'ı iptal et. Bir çiftteki token'lardan herhangi biri tüm çifti iptal eder. Endpoint, geçerli bir client için token var olsun ya da olmasın her zaman 200 döndürür.
curl -X POST https://app-api.gamevox.com/api/oauth2/token/revoke \
-u "123456789012345678:CS.your-client-secret" \
-d token=GVO.mJ4c... PKCE
İsteğe bağlıdır; authorize URL'si başkasının görebileceği bir yerde oluşturulabiliyorsa önerilir. GameVox yalnızca S256 destekler ve token isteği yine de client secret gerektirir: secret'sız public client yoktur.
// Before redirecting: keep the verifier with the user's pending login.
const verifier = base64url(crypto.randomBytes(32));
const challenge = base64url(crypto.createHash('sha256').update(verifier).digest());
// authorize URL: ...&code_challenge=CHALLENGE&code_challenge_method=S256
// token request: ...&code_verifier=VERIFIER (client secret still required) Kapsamlar
| Kapsam | Uygulamanın yapabildiği |
|---|---|
identify | Kullanıcının ID'sini, kullanıcı adını ve avatarını oku: GET /users/@me ve GET /oauth2/@me içindeki user. |
email | Kullanıcının doğrulanmış e-postasını /users/@me yanıtına ekle. |
guilds | Kullanıcının sunucu listesini oku: GET /users/@me/guilds. |
role_connections.write | Kullanıcının uygulamana ait bağlı rol bilgilerini oku ve yaz. |
connections, guilds.join ve bot olmadan applications.commands dahil diğer tüm kapsamlar, onay ekranında bunu belirten bir mesajla reddedilir.
Hatalar
Token ve revoke endpoint'leri hataları standart OAuth2 biçiminde, {"error": "...", "error_description": "..."} olarak döndürür; böylece OAuth kütüphaneleri bunları olduğu gibi okur:
| error | HTTP | Anlamı |
|---|---|---|
invalid_client | 401 | Yanlış client ID veya secret, silinmiş uygulama ya da uygulama sahibinin hesabı askıya alınmış. |
invalid_grant | 400 | Code veya refresh token bilinmiyor, süresi dolmuş, zaten kullanılmış ya da başka bir uygulamaya verilmiş; veya redirect_uri ya da code_verifier eşleşmiyor. Kullanıcı uygulamanın yetkisini kaldırdıktan sonra da döner. |
invalid_request | 400 | Zorunlu bir alan eksik ya da gövde form kodlu değil. |
invalid_scope | 400 | Bir yenileme isteği, ilk izinde bulunmayan bir kapsam istedi. |
unsupported_grant_type | 400 | Yalnızca authorization_code ve refresh_token desteklenir. |
slow_down | 429 | IP adresinden çok fazla başarısız client kimlik doğrulaması (dakikada 30). Bir dakika bekle. |
Hatalı, süresi dolmuş veya iptal edilmiş bir token ile yapılan API çağrıları 401 alır; gereken kapsama sahip olmayan bir token 403 (50001 Missing Access) alır. Yanıtlar, kullanıcı ve uygulama başına sayılan olağan X-RateLimit-* header'larını içerir.
Erişim ne zaman sona erer
Bir token, süresi dolmadan da çalışmayı bırakabilir. Bu şu durumlarda olur:
- kullanıcı Settings > Authorized Apps altında Deauthorize'a tıklar (onun için tuttuğun tüm token'lar anında geçersiz olur ve ona ait bağlı rol kaydın silinir)
- kullanıcının hesabı devre dışı bırakılır
- uygulaman silinir
- geliştirici hesabın askıya alınır
401 veya invalid_grant yanıtını “oturum kapandı” olarak değerlendir ve kullanıcıyı yeniden 1. adımdan geçir.
Tam örnek (Node.js + Express)
State kontrolüyle giriş, code değişimi ve kullanıcının okunması. Yerleşik fetch için Node 18+ gerekir.
import express from 'express';
import crypto from 'node:crypto';
const CLIENT_ID = process.env.GAMEVOX_CLIENT_ID;
const CLIENT_SECRET = process.env.GAMEVOX_CLIENT_SECRET;
const REDIRECT_URI = 'https://yourapp.example.com/auth/gamevox';
// state must be tied to THIS browser, or someone can hand a victim a callback
// URL carrying the attacker's own code and sign them in as the attacker.
// A short-lived HttpOnly cookie does that without a session store.
const STATE_COOKIE = 'gv_oauth_state';
const readCookie = (req, name) =>
(req.headers.cookie || '').split(';').map((c) => c.trim().split('='))
.find(([k]) => k === name)?.[1] || '';
const app = express();
app.get('/login', (req, res) => {
const state = crypto.randomBytes(16).toString('hex');
res.cookie(STATE_COOKIE, state, {
httpOnly: true, secure: true, sameSite: 'lax', maxAge: 10 * 60 * 1000, path: '/auth/gamevox',
});
const url = new URL('https://gamevox.com/oauth2/authorize/');
url.search = new URLSearchParams({
response_type: 'code',
client_id: CLIENT_ID,
scope: 'identify email',
redirect_uri: REDIRECT_URI,
state,
}).toString();
res.redirect(url.toString());
});
app.get('/auth/gamevox', async (req, res) => {
const { code, state, error } = req.query;
const expected = readCookie(req, STATE_COOKIE);
res.clearCookie(STATE_COOKIE, { path: '/auth/gamevox' });
if (!expected || typeof state !== 'string' || state !== expected) {
return res.status(400).send('Login expired or started in another browser');
}
if (error) return res.redirect('/?login=cancelled');
if (typeof code !== 'string') return res.status(400).send('Missing code');
const tokenRes = await fetch('https://app-api.gamevox.com/api/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: 'Basic ' + Buffer.from(CLIENT_ID + ':' + CLIENT_SECRET).toString('base64'),
},
body: new URLSearchParams({ grant_type: 'authorization_code', code, redirect_uri: REDIRECT_URI }),
});
if (!tokenRes.ok) return res.status(502).send('GameVox login failed');
const tokens = await tokenRes.json();
const me = await fetch('https://app-api.gamevox.com/api/users/@me', {
headers: { Authorization: 'Bearer ' + tokens.access_token },
}).then((r) => r.json());
// me.id is the stable GameVox user id: key your account record on it.
// Store tokens.refresh_token server-side if you need access later.
res.send('Signed in as ' + me.username);
});
app.listen(3000); Discord'dan farklar
- Yalnızca authorization-code grant'i var. Implicit grant (
response_type=token) veclient_credentialsyok. - Public client yok: PKCE olsun ya da olmasın her token isteği client secret gerektirir.
- Kullanıcı kurulumları (
botolmadanapplications.commands,integration_type=1) henüz kullanılamıyor. connections,guilds.join,guilds.members.readveyarpckapsamları yok./users/@me/guildsiçindekipermissionsalanı tam bir hesaplama değil, sunucu seçmek için bir özettir (yukarıya bak).