diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts index 751ccdb85..e0cacf57b 100644 --- a/src/core/configuration/Config.ts +++ b/src/core/configuration/Config.ts @@ -25,7 +25,6 @@ export enum GameEnv { export interface ServerConfig { turnstileSiteKey(): string; - turnstileSecretKey(): string; turnIntervalMs(): number; gameCreationRate(): number; numWorkers(): number; diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index 281e46024..e59653365 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -43,9 +43,6 @@ const JwksSchema = z.object({ }); export abstract class DefaultServerConfig implements ServerConfig { - turnstileSecretKey(): string { - return Env.TURNSTILE_SECRET_KEY ?? ""; - } abstract turnstileSiteKey(): string; allowedFlares(): string[] | undefined { return; diff --git a/src/core/configuration/DevConfig.ts b/src/core/configuration/DevConfig.ts index 09c0adaa6..8ead8c8e5 100644 --- a/src/core/configuration/DevConfig.ts +++ b/src/core/configuration/DevConfig.ts @@ -8,10 +8,6 @@ export class DevServerConfig extends DefaultServerConfig { return "1x00000000000000000000AA"; } - turnstileSecretKey(): string { - return "1x0000000000000000000000000000000AA"; - } - adminToken(): string { return "WARNING_DEV_ADMIN_KEY_DO_NOT_USE_IN_PRODUCTION"; } diff --git a/src/core/configuration/Env.ts b/src/core/configuration/Env.ts index 655202b34..b5dfe2111 100644 --- a/src/core/configuration/Env.ts +++ b/src/core/configuration/Env.ts @@ -62,9 +62,6 @@ export const Env = { return getEnv("GAME_ENV") ?? "dev"; }, - get TURNSTILE_SECRET_KEY() { - return getEnv("TURNSTILE_SECRET_KEY"); - }, get STRIPE_PUBLISHABLE_KEY() { return getEnv("STRIPE_PUBLISHABLE_KEY"); }, diff --git a/src/server/Turnstile.ts b/src/server/Turnstile.ts index 5362672b0..961c54514 100644 --- a/src/server/Turnstile.ts +++ b/src/server/Turnstile.ts @@ -1,68 +1,44 @@ +import { ServerConfig } from "../core/configuration/Config"; + +type TurnstileResponse = + | { status: "approved" } + | { status: "rejected"; reason: string } + | { status: "error"; reason: string }; + export async function verifyTurnstileToken( ip: string, turnstileToken: string | null, - turnstileSecret: string, -): Promise< - | { status: "approved" } - | { status: "rejected"; reason: string } - | { status: "error"; reason: string } -> { - if (!turnstileToken) { - return { status: "rejected", reason: "No turnstile token provided" }; - } - + config: ServerConfig, +): Promise { try { const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), 3000); + const timeoutId = setTimeout(() => controller.abort(), 5000); - const response = await fetch( - "https://challenges.cloudflare.com/turnstile/v0/siteverify", - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - secret: turnstileSecret, - response: turnstileToken, - remoteip: ip, - }), - signal: controller.signal, + const response = await fetch(`${config.jwtIssuer()}/turnstile`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-api-key": config.apiKey(), }, - ); + body: JSON.stringify({ ip, token: turnstileToken }), + signal: controller.signal, + }); clearTimeout(timeoutId); if (!response.ok) { return { status: "error", - reason: `Turnstile API returned ${response.status}`, + reason: `api-worker returned ${response.status}`, }; } - const result = (await response.json()) as { - success: boolean; - challenge_ts?: string; - hostname?: string; - "error-codes"?: string[]; - action?: string; - cdata?: string; - }; - - if (!result.success) { - const codes = result["error-codes"]?.join(", ") ?? "unknown"; - return { - status: "rejected", - reason: `Turnstile token validation failed: ${codes}`, - }; - } - - return { status: "approved" }; + return (await response.json()) as TurnstileResponse; } catch (e) { if (e instanceof Error && e.name === "AbortError") { return { status: "error", - reason: "Turnstile token validation timed out after 3 seconds", + reason: "Turnstile token validation timed out after 5 seconds", }; } return { diff --git a/src/server/Worker.ts b/src/server/Worker.ts index d8d43433b..13cf1f32c 100644 --- a/src/server/Worker.ts +++ b/src/server/Worker.ts @@ -40,10 +40,6 @@ const workerId = parseInt(process.env.WORKER_ID ?? "0"); const log = logger.child({ comp: `w_${workerId}` }); const playlist = new MapPlaylist(); -// TEMPORARY: Turnstile validation disabled while we diagnose intermittent -// invalid-input-response rejections in v31. Flip back to true to re-enable. -const TURNSTILE_ENABLED = false; - // Worker setup export async function startWorker() { log.info(`Worker starting...`); @@ -432,14 +428,11 @@ export async function startWorker() { return; } - // TEMPORARY: Turnstile validation disabled while we diagnose - // intermittent invalid-input-response rejections in v31. - // Re-enable by flipping TURNSTILE_ENABLED back to true. - if (TURNSTILE_ENABLED && config.env() !== GameEnv.Dev) { + if (config.env() !== GameEnv.Dev) { const turnstileResult = await verifyTurnstileToken( ip, clientMsg.turnstileToken, - config.turnstileSecretKey(), + config, ); switch (turnstileResult.status) { case "approved": diff --git a/tests/util/TestServerConfig.ts b/tests/util/TestServerConfig.ts index 4b34f133f..c213b1709 100644 --- a/tests/util/TestServerConfig.ts +++ b/tests/util/TestServerConfig.ts @@ -7,9 +7,6 @@ export class TestServerConfig implements ServerConfig { turnstileSiteKey(): string { throw new Error("Method not implemented."); } - turnstileSecretKey(): string { - throw new Error("Method not implemented."); - } apiKey(): string { throw new Error("Method not implemented."); }