mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-24 13:52:45 +00:00
Turnstile: require token before joining a multiplayer game (#2572)
When user tries to join either a public or private multiplayer game, the turnstile callback is triggered, and the turnstile token is passed to the server when joining a game. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: evan
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
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" };
|
||||
}
|
||||
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 3000);
|
||||
|
||||
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,
|
||||
},
|
||||
);
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok) {
|
||||
return {
|
||||
status: "error",
|
||||
reason: `Turnstile API 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" };
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.name === "AbortError") {
|
||||
return {
|
||||
status: "error",
|
||||
reason: "Turnstile token validation timed out after 3 seconds",
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: "error",
|
||||
reason: `Turnstile token validation failed, ${e}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,10 @@ import { GameManager } from "./GameManager";
|
||||
import { getUserMe, verifyClientToken } from "./jwt";
|
||||
import { logger } from "./Logger";
|
||||
|
||||
import { GameEnv } from "../core/configuration/Config";
|
||||
import { MapPlaylist } from "./MapPlaylist";
|
||||
import { PrivilegeRefresher } from "./PrivilegeRefresher";
|
||||
import { verifyTurnstileToken } from "./Turnstile";
|
||||
import { initWorkerMetrics } from "./WorkerMetrics";
|
||||
|
||||
const config = getServerConfigFromServer();
|
||||
@@ -406,6 +408,31 @@ export async function startWorker() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.env() !== GameEnv.Dev) {
|
||||
const turnstileResult = await verifyTurnstileToken(
|
||||
ip,
|
||||
clientMsg.turnstileToken,
|
||||
config.turnstileSecretKey(),
|
||||
);
|
||||
switch (turnstileResult.status) {
|
||||
case "approved":
|
||||
break;
|
||||
case "rejected":
|
||||
log.warn("Unauthorized: Turnstile token rejected", {
|
||||
clientID: clientMsg.clientID,
|
||||
reason: turnstileResult.reason,
|
||||
});
|
||||
ws.close(1002, "Unauthorized");
|
||||
return;
|
||||
case "error":
|
||||
// Fail open, allow the client to join.
|
||||
log.error("Turnstile token error", {
|
||||
clientID: clientMsg.clientID,
|
||||
reason: turnstileResult.reason,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Create client and add to game
|
||||
const client = new Client(
|
||||
clientMsg.clientID,
|
||||
|
||||
Reference in New Issue
Block a user