Merge branch 'v27'

This commit is contained in:
evanpelle
2025-12-12 16:20:43 -08:00
22 changed files with 1025 additions and 709 deletions
-10
View File
@@ -138,8 +138,6 @@ export class MapPlaylist {
const ffa1: GameMapType[] = rand.shuffleArray([...maps]);
const team1: GameMapType[] = rand.shuffleArray([...maps]);
const ffa2: GameMapType[] = rand.shuffleArray([...maps]);
const team2: GameMapType[] = rand.shuffleArray([...maps]);
const ffa3: GameMapType[] = rand.shuffleArray([...maps]);
this.mapsPlaylist = [];
for (let i = 0; i < maps.length; i++) {
@@ -154,14 +152,6 @@ export class MapPlaylist {
if (!this.addNextMap(this.mapsPlaylist, ffa2, GameMode.FFA)) {
return false;
}
if (!this.disableTeams) {
if (!this.addNextMap(this.mapsPlaylist, team2, GameMode.Team)) {
return false;
}
}
if (!this.addNextMap(this.mapsPlaylist, ffa3, GameMode.FFA)) {
return false;
}
}
return true;
}
+19 -9
View File
@@ -337,9 +337,14 @@ export async function startWorker() {
// Verify token signature
const result = await verifyClientToken(clientMsg.token, config);
if (result === false) {
log.warn("Unauthorized: Invalid token");
ws.close(1002, "Unauthorized");
if (result.type === "error") {
log.warn(`Invalid token: ${result.message}`, {
clientID: clientMsg.clientID,
});
ws.close(
1002,
`Unauthorized: invalid token for client ${clientMsg.clientID}`,
);
return;
}
const { persistentId, claims } = result;
@@ -374,13 +379,18 @@ export async function startWorker() {
} else {
// Verify token and get player permissions
const result = await getUserMe(clientMsg.token, config);
if (result === false) {
log.warn("Unauthorized: Invalid session");
ws.close(1002, "Unauthorized");
if (result.type === "error") {
log.warn(`Unauthorized: ${result.message}`, {
clientID: clientMsg.clientID,
});
ws.close(
1002,
`Unauthorized: user me fetch failed for client ${clientMsg.clientID}`,
);
return;
}
roles = result.player.roles;
flares = result.player.flares;
roles = result.response.player.roles;
flares = result.response.player.flares;
if (allowedFlares !== undefined) {
const allowed =
@@ -422,7 +432,7 @@ export async function startWorker() {
clientID: clientMsg.clientID,
reason: turnstileResult.reason,
});
ws.close(1002, "Unauthorized");
ws.close(1002, "Unauthorized: Turnstile token rejected");
return;
case "error":
// Fail open, allow the client to join.
+35 -17
View File
@@ -11,17 +11,18 @@ import { PersistentIdSchema } from "../core/Schemas";
type TokenVerificationResult =
| {
type: "success";
persistentId: string;
claims: TokenPayload | null;
}
| false;
| { type: "error"; message: string };
export async function verifyClientToken(
token: string,
config: ServerConfig,
): Promise<TokenVerificationResult> {
if (PersistentIdSchema.safeParse(token).success) {
return { persistentId: token, claims: null };
return { type: "success", persistentId: token, claims: null };
}
try {
const issuer = config.jwtIssuer();
@@ -34,22 +35,33 @@ export async function verifyClientToken(
});
const result = TokenPayloadSchema.safeParse(payload);
if (!result.success) {
const error = z.prettifyError(result.error);
console.warn("Error parsing token payload", error);
return false;
return {
type: "error",
message: z.prettifyError(result.error),
};
}
const claims = result.data;
const persistentId = claims.sub;
return { persistentId, claims };
return { type: "success", persistentId, claims };
} catch (e) {
return false;
const message =
e instanceof Error
? e.message
: typeof e === "string"
? e
: "An unknown error occurred";
return { type: "error", message };
}
}
export async function getUserMe(
token: string,
config: ServerConfig,
): Promise<UserMeResponse | false> {
): Promise<
| { type: "success"; response: UserMeResponse }
| { type: "error"; message: string }
> {
try {
// Get the user object
const response = await fetch(config.jwtIssuer() + "/users/@me", {
@@ -57,19 +69,25 @@ export async function getUserMe(
authorization: `Bearer ${token}`,
},
});
if (response.status !== 200) return false;
if (response.status !== 200) {
return {
type: "error",
message: `Failed to fetch user me: ${response.statusText}`,
};
}
const body = await response.json();
const result = UserMeResponseSchema.safeParse(body);
if (!result.success) {
console.error(
"Invalid response",
JSON.stringify(body),
JSON.stringify(result.error),
);
return false;
return {
type: "error",
message: `Invalid response: ${z.prettifyError(result.error)}`,
};
}
return result.data;
return { type: "success", response: result.data };
} catch (e) {
return false;
return {
type: "error",
message: `Failed to fetch user me: ${e}`,
};
}
}