Mit GameVox anmelden
Lass Personen sich mit ihrem GameVox-Konto bei deiner Website oder App anmelden. Das ist der übliche OAuth2-Ablauf mit Autorisierungscode, aufgebaut wie bei Discord, und er braucht keinen Bot: Deine App fragt nach Teilen des Kontos, die Person stimmt zu, und dein Server erhält ein Token.
Wofür es gedacht ist
- Anmeldung und Kontoverknüpfung. Erfahre, wer die Person ist (eine dauerhafte GameVox-Nutzer-ID, Benutzername, Avatar) und, mit ihrer Erlaubnis, ihre bestätigte E-Mail-Adresse.
- Serverauswahl und Dashboards. Mit
guildssiehst du, in welchen Servern die Person ist und wo sie Einstellungen verwalten kann. - Nicht, um im Namen der Person zu handeln. Ein Nutzer-Token kann keine Nachrichten senden, keinem Sprachkanal beitreten und nichts auf GameVox ändern. Alles, was innerhalb von GameVox handelt, braucht einen Bot.
Bevor du anfängst
- Erstelle eine Anwendung auf developers.gamevox.com. Die Client-ID im Tab OAuth2 ist deine
client_id. - Trage im Tab OAuth2 jede Redirect-URI ein, die deine App verwendet (bis zu 10). Sie müssen
https://sein, oderhttp://localhost/http://127.0.0.1für die Entwicklung, ohne#fragment. Dieredirect_uri, die du sendest, muss exakt einer davon entsprechen, einschließlich Query-String. - Kopiere das Client-Secret. Es wird einmal angezeigt, wenn die App erstellt oder das Secret zurückgesetzt wird. Bewahre es auf deinem Server auf: Jede Token-Anfrage braucht es.
Endpunkte
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 Behalte den abschließenden Schrägstrich bei /oauth2/authorize/. Die Token- und Nutzer-Endpunkte antworten auch unter /api/v10/, sodass Bibliotheken, die eine Version in den Pfad setzen, unverändert funktionieren. Rollenverbindungen liegen auf api.gamevox.com, nicht auf app-api.
1. Schick die Person zu GameVox
Leite den Browser (per normaler Navigation, nicht in einem Popup oder Iframe) zur Autorisierungs-URL weiter:
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 | Parameter | Erforderlich | Beschreibung |
|---|---|---|
response_type | Ja | Immer code. |
client_id | Ja | Die Client-ID aus dem OAuth2-Tab deiner App. |
scope | Ja | Durch Leerzeichen getrennt (URL-kodiert als %20 oder +). Siehe Scopes. Lass bot weg: Mit bot wird diese URL stattdessen zu einer Serverinstallation. |
redirect_uri | Ja | Exakt eine deiner registrierten Redirect-URIs. |
state | Nein | Ein nicht erratbarer Wert, der an die Sitzung der Person gebunden ist (bis zu 1024 Zeichen). Er kommt unverändert zurück; prüfe ihn, um Cross-Site-Request-Forgery zu verhindern. |
code_challenge | Nein | Optionale PKCE-Challenge, 43 bis 128 Zeichen. |
code_challenge_method | Nein | S256, wenn du eine Challenge sendest. plain wird abgelehnt. |
prompt | Nein | none überspringt den Zustimmungsbildschirm, wenn die Person deine App bereits für alle diese Scopes freigegeben hat. Alles Neue wird weiterhin abgefragt. |
Was die Person sieht
- Einen Zustimmungsbildschirm auf gamevox.com mit Name, Symbol und Beschreibung deiner App, dem Konto, mit dem sie angemeldet ist, einer verständlichen Liste dessen, was du sehen kannst, und der Website, zu der sie weitergeleitet wird.
- Ist sie abgemeldet, führt „Log in“ sie durch die GameVox-Web-App und direkt zurück zum selben Bildschirm.
- Die Links zu deinen Nutzungsbedingungen und deiner Datenschutzerklärung erscheinen unter den Buttons, wenn du sie im Tab Allgemeine Informationen hinterlegst.
2. Verarbeite den Callback
Nachdem die Person auf „Authorize“ geklickt hat, schickt GameVox sie mit einem Code an deine Redirect-URI:
https://yourapp.example.com/auth/gamevox?code=3vR0b...&state=Zx81kq0vT2 Klickt sie stattdessen auf „Cancel“:
https://yourapp.example.com/auth/gamevox?error=access_denied
&error_description=The+user+denied+the+request&state=Zx81kq0vT2 - Lehne den Callback ab, wenn
statenicht der Wert ist, den du für diesen Browser ausgegeben hast. - Ein Code läuft nach 10 Minuten ab und funktioniert nur einmal. Tausche ihn sofort ein, von deinem Server aus.
3. Tausche den Code gegen Tokens
Sende von deinem Server per POST einen formularkodierten Body (application/x-www-form-urlencoded, kein JSON) an den Token-Endpunkt:
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"
} - Authentifiziere dich wie oben per HTTP Basic (
client_id:client_secret) oder sendeclient_idundclient_secretals Formularfelder. redirect_urimuss derselbe Wert sein, den du in Schritt 1 gesendet hast. Fügecode_verifierhinzu, wenn du PKCE verwendet hast.- Antworten enthalten
Cache-Control: no-store. Lege das Client-Secret oder Tokens niemals in Browser-Code oder URLs ab.
4. Ruf die API auf
Sende das Access-Token als Authorization: Bearer. GET /users/@me braucht den Scope identify:
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
} idist die dauerhafte GameVox-ID der Person. Verknüpfe deine Kontodatensätze damit; Benutzernamen können sich ändern.gamevox_idist dieselbe Person in der nativen UUID-Form von GameVox.emailundverifiedsind nur mit dem Scopeemailvorhanden, und nur, wenn die Person eine E-Mail-Adresse hinterlegt hat. GameVox speichert nur bestätigte Adressen, daher istverifiedimmertrue, wennemailgesetzt ist.avatarist immernull(Discord-Bibliotheken würden daraus einen Link auf das Discord-CDN bauen). Nutzegamevox_avatar_url; das Feld fehlt, wenn die Person kein Bild hat.
Die Server der Person
GET /users/@me/guilds braucht den Scope guilds:
[
{ "id": "1557445672742031360", "name": "Raid Night", "icon": null,
"owner": true, "permissions": "2251799813685247", "features": [] },
{ "id": "1557445672742031361", "name": "Guild Hall", "icon": null,
"owner": false, "permissions": "1056", "features": [] }
] permissionsist eine Zusammenfassung für die Serverauswahl, keine vollständige Berechnung: alle Bits (einschließlichADMINISTRATOR) für Eigentümer und Admins,MANAGE_GUILD(32) plusVIEW_CHANNEL(1024), wo die Person Servereinstellungen bearbeiten kann, undVIEW_CHANNELfür alle anderen.- Bis zu
limitServer pro Antwort (1 bis 200, Standard 200), die ältesten zuerst. Blättere weiter, indem duafteroderbeforeauf dieideines Servers aus der vorherigen Seite setzt, wie bei Discord.
Die aktuelle Autorisierung
GET /oauth2/@me funktioniert mit jedem Scope und sagt dir, zu welcher App das Token gehört, was gewährt wurde und wann es abläuft. user ist mit identify enthalten.
{
"application": { "id": "123456789012345678", "name": "RaidPlanner", ... },
"scopes": ["identify", "email"],
"expires": "2026-10-15T15:03:20Z",
"user": { "id": "1557709980780474368", "username": "alice", ... }
} Verknüpfte Rollen
Mit role_connections.write kannst du festhalten, welches Konto die Person bei dir hat. Achte auf den Host: 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": {}}' Die Person sieht das unter Settings > Authorized Apps als “Linked as ShadowMage on RaidPlanner”. Die Schlüssel in metadata müssen in den Rollenverbindungs-Metadaten deiner App definiert sein. Ein GET auf dieselbe URL liest den Eintrag zurück.
Erneuern
Access-Tokens gelten 7 Tage (expires_in ist in Sekunden). Hol dir mit dem Refresh-Token ein neues Paar:
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-Tokens rotieren. Die Antwort enthält ein neues Refresh-Token, und das alte Refresh-Token und sein Access-Token funktionieren sofort nicht mehr. Speichere also das neue Paar, bevor du es verwendest.
- Du kannst
scopemitsenden, um das neue Token auf weniger Scopes einzuschränken. Die Anfrage nach einem Scope, den die Person nie gewährt hat, schlägt mitinvalid_scopefehl. - Ein ungenutztes Refresh-Token läuft nach 90 Tagen ab. Pro Person und App gibt es höchstens 25 aktive Token-Paare; ein 26. verdrängt das älteste.
Widerrufen (Abmelden)
Widerrufe, wenn sich die Person von deiner App abmeldet. Jedes der beiden Tokens eines Paars widerruft das ganze Paar. Der Endpunkt antwortet bei einem gültigen Client immer mit 200, unabhängig davon, ob das Token existierte.
curl -X POST https://app-api.gamevox.com/api/oauth2/token/revoke \
-u "123456789012345678:CS.your-client-secret" \
-d token=GVO.mJ4c... PKCE
Optional, und empfohlen, wenn die Autorisierungs-URL an einer Stelle gebaut wird, an der andere sie sehen könnten. GameVox unterstützt nur S256, und eine Token-Anfrage braucht trotzdem dein Client-Secret: Es gibt keine öffentlichen Clients ohne Secret.
// 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) Scopes
| Scope | Deine App kann |
|---|---|
identify | ID, Benutzernamen und Avatar der Person lesen: GET /users/@me sowie user in GET /oauth2/@me. |
email | Die bestätigte E-Mail-Adresse der Person zu /users/@me hinzufügen. |
guilds | Die Serverliste der Person lesen: GET /users/@me/guilds. |
role_connections.write | Die Angaben zu verknüpften Rollen der Person für deine App lesen und schreiben. |
Jeder andere Scope, einschließlich connections, guilds.join und applications.commands ohne bot, wird auf dem Zustimmungsbildschirm mit einem entsprechenden Hinweis abgelehnt.
Fehler
Die Token- und Widerrufs-Endpunkte liefern Fehler in der üblichen OAuth2-Form, {"error": "...", "error_description": "..."}, sodass OAuth-Bibliotheken sie direkt verarbeiten:
| error | HTTP | Bedeutung |
|---|---|---|
invalid_client | 401 | Falsche Client-ID oder falsches Secret, gelöschte App, oder das Konto, dem die App gehört, ist gesperrt. |
invalid_grant | 400 | Der Code oder das Refresh-Token ist unbekannt, abgelaufen, bereits verwendet oder an eine andere App ausgegeben, oder redirect_uri bzw. code_verifier stimmen nicht überein. Kommt auch, nachdem die Person deine App deautorisiert hat. |
invalid_request | 400 | Ein Pflichtfeld fehlt, oder der Body ist nicht formularkodiert. |
invalid_scope | 400 | Eine Erneuerung hat einen Scope angefordert, den die ursprüngliche Freigabe nicht enthält. |
unsupported_grant_type | 400 | Nur authorization_code und refresh_token werden unterstützt. |
slow_down | 429 | Zu viele fehlgeschlagene Client-Authentifizierungen von deiner IP (30 pro Minute). Warte eine Minute. |
API-Aufrufe mit einem ungültigen, abgelaufenen oder widerrufenen Token erhalten 401; ein Token ohne den nötigen Scope erhält 403 (50001 Missing Access). Antworten enthalten die üblichen X-RateLimit-*-Header, gezählt pro Person und App.
Wann der Zugriff endet
Ein Token kann schon vor seinem Ablauf ungültig werden. Das passiert, wenn:
- die Person unter Settings > Authorized Apps auf Deauthorize klickt (alle Tokens, die du für sie hast, werden sofort ungültig, und dein Eintrag für verknüpfte Rollen zu ihr wird gelöscht)
- das Konto der Person deaktiviert wird
- deine Anwendung gelöscht wird
- dein Entwicklerkonto gesperrt wird
Behandle ein 401 oder invalid_grant als “abgemeldet” und schick die Person erneut durch Schritt 1.
Vollständiges Beispiel (Node.js + Express)
Anmeldung mit State-Prüfung, Code-Austausch und Abruf der Personendaten. Node 18+ für das eingebaute fetch.
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); Unterschiede zu Discord
- Nur der Authorization-Code-Grant. Kein Implicit Grant (
response_type=token) und keinclient_credentials. - Keine öffentlichen Clients: Jede Token-Anfrage braucht das Client-Secret, mit oder ohne PKCE.
- Nutzerinstallationen (
applications.commandsohnebot,integration_type=1) sind noch nicht verfügbar. - Keine Scopes
connections,guilds.join,guilds.members.readoderrpc. - In
/users/@me/guildsistpermissionseine Zusammenfassung für die Serverauswahl, keine vollständige Berechnung (siehe oben).