GOG Login: Nahtloser Auth-Flow wie Steam (kein Code-Paste noetig)
- Electron: Fängt GOG Redirect automatisch ab (will-redirect Interceptor) - Code-Exchange passiert im Hintergrund, User sieht nur Erfolgs-Popup - GOG Auth-URLs (auth.gog.com, login.gog.com, embed.gog.com) in Popup erlaubt - Server: GET /gog/login Redirect + POST /gog/exchange Endpoint - Browser-Fallback: Code-Paste Dialog falls nicht in Electron - gog.ts: Feste redirect_uri (embed.gog.com/on_login_success) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
af2448a5bd
commit
3e8febb851
5 changed files with 281 additions and 34 deletions
|
|
@ -10,6 +10,7 @@ const GOG_CLIENT_SECRET =
|
|||
const GOG_AUTH_URL = 'https://auth.gog.com/auth';
|
||||
const GOG_TOKEN_URL = 'https://auth.gog.com/token';
|
||||
const GOG_EMBED_URL = 'https://embed.gog.com';
|
||||
const GOG_REDIRECT_URI = 'https://embed.gog.com/on_login_success?origin=client';
|
||||
|
||||
// ── Types ────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -37,13 +38,13 @@ export interface GogGame {
|
|||
|
||||
/**
|
||||
* Build the GOG OAuth authorization URL that the user should visit to grant
|
||||
* access. After approval, GOG will redirect to `redirectUri` with a `code`
|
||||
* query parameter.
|
||||
* access. After approval, GOG redirects to `embed.gog.com/on_login_success`
|
||||
* with a `code` query parameter that the user copies back into the app.
|
||||
*/
|
||||
export function getGogAuthUrl(redirectUri: string): string {
|
||||
export function getGogAuthUrl(): string {
|
||||
const params = new URLSearchParams({
|
||||
client_id: GOG_CLIENT_ID,
|
||||
redirect_uri: redirectUri,
|
||||
redirect_uri: GOG_REDIRECT_URI,
|
||||
response_type: 'code',
|
||||
layout: 'client2',
|
||||
});
|
||||
|
|
@ -53,10 +54,10 @@ export function getGogAuthUrl(redirectUri: string): string {
|
|||
|
||||
/**
|
||||
* Exchange an authorization code for GOG access + refresh tokens.
|
||||
* Uses the fixed GOG_REDIRECT_URI that matches the OAuth registration.
|
||||
*/
|
||||
export async function exchangeGogCode(
|
||||
code: string,
|
||||
redirectUri: string,
|
||||
): Promise<GogTokens> {
|
||||
console.log('[GOG] Exchanging authorization code for tokens...');
|
||||
|
||||
|
|
@ -65,7 +66,7 @@ export async function exchangeGogCode(
|
|||
client_secret: GOG_CLIENT_SECRET,
|
||||
grant_type: 'authorization_code',
|
||||
code,
|
||||
redirect_uri: redirectUri,
|
||||
redirect_uri: GOG_REDIRECT_URI,
|
||||
});
|
||||
|
||||
const res = await fetch(`${GOG_TOKEN_URL}?${params.toString()}`);
|
||||
|
|
|
|||
|
|
@ -613,32 +613,29 @@ const gameLibraryPlugin: Plugin = {
|
|||
res.json({ profiles });
|
||||
});
|
||||
|
||||
// ── GOG Login ──
|
||||
// ── GOG Login (redirect to GOG auth page) ──
|
||||
app.get('/api/game-library/gog/login', (req, res) => {
|
||||
const proto = req.headers['x-forwarded-proto'] || req.protocol || 'http';
|
||||
const host = req.headers['x-forwarded-host'] || req.headers.host || 'localhost';
|
||||
const realm = `${proto}://${host}`;
|
||||
const redirectUri = `${realm}/api/game-library/gog/callback`;
|
||||
const linkTo = req.query.linkTo ? `&state=${req.query.linkTo}` : '';
|
||||
res.redirect(getGogAuthUrl(redirectUri) + linkTo);
|
||||
const linkTo = req.query.linkTo ? String(req.query.linkTo) : '';
|
||||
// Pass linkTo as OAuth state param — GOG passes it back to redirect_uri
|
||||
let authUrl = getGogAuthUrl();
|
||||
if (linkTo) authUrl += `&state=${encodeURIComponent(linkTo)}`;
|
||||
res.redirect(authUrl);
|
||||
});
|
||||
|
||||
// ── GOG Callback ──
|
||||
app.get('/api/game-library/gog/callback', async (req, res) => {
|
||||
// ── GOG Code Exchange ──
|
||||
// User pastes the code from the GOG redirect URL. We exchange it for
|
||||
// tokens and fetch the game library.
|
||||
app.post('/api/game-library/gog/exchange', async (req, res) => {
|
||||
try {
|
||||
const code = String(req.query.code || '');
|
||||
const linkToProfileId = String(req.query.state || '');
|
||||
const code = String(req.body?.code || '');
|
||||
const linkToProfileId = String(req.body?.linkTo || '');
|
||||
if (!code) {
|
||||
res.status(400).send(errorPage('GOG-Authentifizierung fehlgeschlagen', 'Kein Authorization-Code erhalten.'));
|
||||
res.status(400).json({ error: 'Kein Authorization-Code angegeben.' });
|
||||
return;
|
||||
}
|
||||
|
||||
const proto = req.headers['x-forwarded-proto'] || req.protocol || 'http';
|
||||
const host = req.headers['x-forwarded-host'] || req.headers.host || 'localhost';
|
||||
const redirectUri = `${proto}://${host}/api/game-library/gog/callback`;
|
||||
|
||||
// Exchange code for tokens
|
||||
const tokens = await exchangeGogCode(code, redirectUri);
|
||||
const tokens = await exchangeGogCode(code);
|
||||
|
||||
// Fetch user info + games
|
||||
const [userInfo, games] = await Promise.all([
|
||||
|
|
@ -689,10 +686,10 @@ const gameLibraryPlugin: Plugin = {
|
|||
|
||||
console.log(`[GameLibrary] GOG verknuepft: ${profileName} (${userInfo.userId}) - ${games.length} Spiele`);
|
||||
|
||||
res.send(`<!DOCTYPE html><html><head><title>GOG verbunden</title><style>body{background:#1a1a2e;color:#fff;font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0}div{text-align:center}h2{color:#a855f7}</style></head><body><div><h2>GOG verbunden!</h2><p>${profileName}: ${games.length} Spiele geladen.</p><p>Du kannst dieses Fenster schliessen.</p><script>setTimeout(()=>window.close(),3000)</script></div></body></html>`);
|
||||
res.json({ ok: true, username: userInfo.username, gameCount: games.length, profileName });
|
||||
} catch (err) {
|
||||
console.error('[GameLibrary] GOG Callback error:', err);
|
||||
res.status(500).send(errorPage('GOG-Fehler', 'Ein unerwarteter Fehler ist aufgetreten.'));
|
||||
console.error('[GameLibrary] GOG exchange error:', err);
|
||||
res.status(500).json({ error: 'GOG-Authentifizierung fehlgeschlagen.' });
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue