Files
OpenFrontIO/src/server/Turnstile.ts
T
Evan 3314ca16ce 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
2025-12-08 16:16:31 -08:00

74 lines
1.8 KiB
TypeScript

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}`,
};
}
}