Twitch-VOD-Manager/src/main/domain/pkce.ts
xRangerDE 5a5d6f9c47 feat(auth): Twitch OAuth 2.1 Authorization Code Flow + PKCE + Loopback (21 tests)
3 new modules:
- src/main/domain/pkce.ts: PKCE pair (S256) + state (RFC 7636)
- src/main/infra/loopback-server.ts: ephemeral 127.0.0.1:PORT redirect capture
- src/main/domain/twitch-oauth.ts: startLoginFlow / awaitAuthorizationCode /
  exchangeCodeForToken / fetchTwitchUserInfo

Flow runs entirely scaffold-ready — IPC wiring + Client ID config and
shell.openExternal(authUrl) trigger come in a follow-on plan once the user
registers a Twitch dev app. 164 unit tests gruen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 23:46:22 +02:00

36 lines
1.1 KiB
TypeScript

import * as crypto from 'crypto';
/**
* PKCE (Proof Key for Code Exchange) Helper fuer OAuth 2.1 Authorization Code Flow.
* RFC 7636. Twitch unterstuetzt S256.
*/
export interface PkcePair {
codeVerifier: string; // 43-128 ASCII chars [A-Z a-z 0-9 - . _ ~]
codeChallenge: string; // base64url(sha256(codeVerifier))
codeChallengeMethod: 'S256';
}
function base64url(buf: Buffer): string {
return buf.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
export function createPkcePair(): PkcePair {
// 32 random bytes → 43-char base64url. Innerhalb der RFC-Range.
const verifier = base64url(crypto.randomBytes(32));
const challenge = base64url(crypto.createHash('sha256').update(verifier).digest());
return {
codeVerifier: verifier,
codeChallenge: challenge,
codeChallengeMethod: 'S256',
};
}
export function generateState(): string {
// 16 random bytes als base64url-State-Parameter (CSRF-Schutz).
return base64url(crypto.randomBytes(16));
}