Server-side discord id enforcement

This commit is contained in:
Scott Anderson
2025-07-13 02:53:32 -04:00
parent 4bbd9e6742
commit 828e3a9345
10 changed files with 103 additions and 41 deletions
+4 -6
View File
@@ -178,7 +178,7 @@ class Client {
loginDiscordButton.addEventListener("click", discordLogin);
const onUserMe = async (userMeResponse: UserMeResponse | false) => {
const config = await getServerConfigFromClient();
if (!hasAllowedFlare(userMeResponse, config)) {
if (!hasAllowedId(userMeResponse, config)) {
if (userMeResponse === false) {
// Login is required
document.body.innerHTML = `
@@ -502,14 +502,12 @@ function getPersistentIDFromCookie(): string {
return newID;
}
function hasAllowedFlare(
function hasAllowedId(
userMeResponse: UserMeResponse | false,
config: ServerConfig,
) {
const allowed = config.allowedFlares();
const allowed = config.allowedDiscordIds();
if (allowed === undefined) return true;
if (userMeResponse === false) return false;
const flares = userMeResponse.player.flares;
if (flares === undefined) return false;
return allowed.length === 0 || allowed.some((f) => flares.includes(f));
return allowed.includes(userMeResponse.user.id);
}
-8
View File
@@ -1,7 +1,6 @@
import { decodeJwt } from "jose";
import { z } from "zod/v4";
import {
discordIdHashes,
RefreshResponseSchema,
TokenPayload,
TokenPayloadSchema,
@@ -9,7 +8,6 @@ import {
UserMeResponseSchema,
} from "../core/ApiSchemas";
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
import { simpleHash } from "../core/Util";
function getAudience() {
const { hostname } = new URL(window.location.href);
@@ -235,12 +233,6 @@ export async function getUserMe(): Promise<UserMeResponse | false> {
console.error("Invalid response", error);
return false;
}
if (!discordIdHashes.includes(simpleHash(result.data.user.id))) {
console.error("Unauthorized: User not in allowed list");
clearToken();
window.location.href = "https://www.youtube.com/watch?v=dQw4w9WgXcQ";
return false;
}
return result.data;
} catch (e) {
__isLoggedIn = false;
-17
View File
@@ -47,20 +47,3 @@ export const UserMeResponseSchema = z.object({
}),
});
export type UserMeResponse = z.infer<typeof UserMeResponseSchema>;
export const discordIdHashes = [
1661393335, 1851863381, 1003936817, 653544870, 1557759837, 1042722497,
65439800, 155156957, 1218512162, 426095068, 416547875, 1798289200, 1052294911,
1083112658, 1233079359, 774244699, 952418618, 1215601556, 1716860484,
1853687192, 247930283, 247930283, 2132635226, 1869027023, 2001674915,
766449506, 153828422, 145274786, 2046238715, 253694988, 1507817897,
1646270753, 542766539, 351814673, 365160220, 828883677, 2033416503, 37123544,
961728307, 94814174, 1304192493, 1414342672, 2137156610, 1894770459,
103555344, 174993017, 1205888044, 1252537152, 1469901896, 1457907846,
388895429, 1201515958, 1460910617, 504159584, 1988458085, 938609279,
151073647, 1638157021, 608899192, 699791521, 940653944, 191262706, 1433232464,
949130070, 985289297, 160421750, 423901399, 2053086809, 813603540, 262358277,
240859002, 1788524537, 717454965, 1896548568, 1624081483, 135101486,
1673600636, 523037306, 1713725183, 602978453, 951411520, 644853372,
2031630654, 199624311, 1740705355,
];
+1 -1
View File
@@ -63,7 +63,7 @@ export interface ServerConfig {
cloudflareConfigPath(): string;
cloudflareCredsPath(): string;
stripePublishableKey(): string;
allowedFlares(): string[] | undefined;
allowedDiscordIds(): string[] | undefined;
}
export interface NukeMagnitude {
+1 -1
View File
@@ -68,7 +68,7 @@ const numPlayersConfig = {
} as const satisfies Record<GameMapType, [number, number, number]>;
export abstract class DefaultServerConfig implements ServerConfig {
allowedFlares(): string[] | undefined {
allowedDiscordIds(): string[] | undefined {
return;
}
stripePublishableKey(): string {
+1 -1
View File
@@ -46,7 +46,7 @@ export class DevServerConfig extends DefaultServerConfig {
subdomain(): string {
return "";
}
allowedFlares(): string[] | undefined {
allowedDiscordIds(): string[] | undefined {
return [
// Require login but do not rqeuire any flares
];
+1 -1
View File
@@ -11,7 +11,7 @@ export const preprodConfig = new (class extends DefaultServerConfig {
jwtAudience(): string {
return "openfront.dev";
}
allowedFlares(): string[] | undefined {
allowedDiscordIds(): string[] | undefined {
return [
// "access:openfront.dev"
];
+89
View File
@@ -11,4 +11,93 @@ export const prodConfig = new (class extends DefaultServerConfig {
jwtAudience(): string {
return "openfront.io";
}
allowedDiscordIds(): string[] | undefined {
return [
"801201988980834374",
"383908937407135744",
"991040538142715914",
"109208187495796736",
"524916260480221184",
"923944328945098792",
"1248130385230037125",
"1042867662692302878",
"1231353285798002711",
"1380825798956548217",
"321330861305757696",
"1231132235793694762",
"518092307765919755",
"818164543477645324",
"1056637784556843009",
"748290919886749828",
"1207048759616217101",
"1332571420701757441",
"402217202113052682",
"207467536432889857",
"1068599025932193904",
"1068599025932193904",
"717364086874046584",
"470638985870442516",
"257866880402849792",
"863823197035823125",
"662278693179097088",
"1371692527702380616",
"296454138877968385",
"337118939421212673",
"719142768693346315",
"537013169546067974",
"1237133105215574056",
"1149773842877796423",
"229985855077351424",
"536446199012655104",
"302797599952470016",
"269534621719330816",
"1125081721507958904",
"735445052461023302",
"515926547786629142",
"1388899072034738176",
"1260964720824025088",
"1042598121429028944",
"1086680982712758312",
"439390916676026378",
"1233539002171392052",
"748439392489701396",
"1134460734923882506",
"1120408570668789941",
"969552442704412672",
"928360146583842896",
"376153617306091533",
"420169470942904320",
"642727461171232794",
"1136031095515783229",
"207082417217994754",
"377107679975309313",
"250918787178561537",
"792380506939457536",
"239571828325089280",
"307514132846870546",
"162984858811891715",
"928735342469726298",
"516828441065160714",
"1334886860538904679",
"1060632477489713212",
"658879672851103755",
"1035677402606600252",
"874951032319397918",
"384767022560575496",
"814085898932715560",
"327950020831870987",
"292684898324709377",
"905159200462753874",
"1165558245096706129",
"1088886751810027600",
"943482851628826635",
"640771103425429525",
"1313943792948350976",
"1054834071789908019",
"277841234133778432",
"260802472421883906",
"230415855614296075",
"251738465643921409",
];
}
})();
+5 -5
View File
@@ -321,9 +321,9 @@ export function startWorker() {
let roles: string[] | undefined;
let flares: string[] | undefined;
const allowedFlares = config.allowedFlares();
const allowedIds = config.allowedDiscordIds();
if (claims === null) {
if (allowedFlares !== undefined) {
if (allowedIds !== undefined) {
log.warn("Unauthorized: Anonymous user attempted to join game");
ws.close(1002, "Unauthorized");
return;
@@ -339,10 +339,10 @@ export function startWorker() {
roles = result.player.roles;
flares = result.player.flares;
if (allowedFlares !== undefined) {
if (allowedIds !== undefined) {
const allowed =
allowedFlares.length === 0 ||
allowedFlares.some((f) => flares?.includes(f));
allowedIds.length === 0 ||
allowedIds.includes(result.user.id);
if (!allowed) {
log.warn(
"Forbidden: player without an allowed flare attempted to join game",
+1 -1
View File
@@ -4,7 +4,7 @@ import { GameMapType } from "../../src/core/game/Game";
import { GameID } from "../../src/core/Schemas";
export class TestServerConfig implements ServerConfig {
allowedFlares(): string[] | undefined {
allowedDiscordIds(): string[] | undefined {
throw new Error("Method not implemented.");
}
stripePublishableKey(): string {