feat(network): add ProxyCheck IP intelligence

This commit is contained in:
dmg
2026-08-01 14:32:54 -04:00
parent 10554eeaff
commit 40abab7abc
18 changed files with 539 additions and 26 deletions
+38 -3
View File
@@ -12,6 +12,7 @@ import {
import { and, eq, isNull, lt, sql } from "drizzle-orm";
import { NextResponse } from "next/server";
import { db } from "@/lib/database";
import { getIpIntelligence, toAuditIpData } from "@/lib/ip-intelligence";
const MAX_CLOCK_SKEW_MS = 45_000;
const DEFAULT_DENIAL_MESSAGE = "Please register your Minecraft account before joining.";
@@ -45,6 +46,35 @@ export async function POST(request: Request) {
const [settings] = await db.select().from(appSettings).where(eq(appSettings.id, "default")).limit(1);
const denialMessage = settings?.registrationMessage ?? DEFAULT_DENIAL_MESSAGE;
let [knownAccount] = await db
.select({ id: minecraftAccounts.id })
.from(minecraftAccounts)
.where(
and(
eq(minecraftAccounts.minecraftUuid, input.minecraftUuid),
isNull(minecraftAccounts.deletedAt),
),
)
.limit(1);
if (!knownAccount) {
[knownAccount] = await db
.select({ id: minecraftAccounts.id })
.from(minecraftAccounts)
.where(
and(
isNull(minecraftAccounts.minecraftUuid),
sql`lower(${minecraftAccounts.username}) = lower(${input.username})`,
isNull(minecraftAccounts.deletedAt),
),
)
.limit(1);
}
const intelligence = knownAccount
? await getIpIntelligence(input.ipAddress)
: { classification: "unknown" as const, provider: null };
const auditIpData = toAuditIpData(intelligence);
try {
const decision = await db.transaction(async (tx) => {
await tx.delete(pluginRequests).where(lt(pluginRequests.expiresAt, new Date()));
@@ -97,7 +127,11 @@ export async function POST(request: Request) {
type: "games.minecraft.account-manager.game.login.denied",
subject: `minecraft-account/${input.minecraftUuid}`,
time: occurredAt,
data: { username: input.username, reason: "not_registered" },
data: {
username: input.username,
reason: "not_registered",
ipIntelligence: auditIpData,
},
ipAddress: input.ipAddress,
correlationId: input.requestId,
});
@@ -106,7 +140,7 @@ export async function POST(request: Request) {
ipAddress: input.ipAddress,
minecraftUuid: input.minecraftUuid,
username: input.username,
classification: "unknown",
classification: intelligence.classification,
observedAt: occurredAt,
});
return { allowed: false as const, message: denialMessage };
@@ -145,7 +179,7 @@ export async function POST(request: Request) {
ipAddress: input.ipAddress,
minecraftUuid: input.minecraftUuid,
username: input.username,
classification: "unknown",
classification: intelligence.classification,
observedAt: occurredAt,
});
await tx.insert(events).values({
@@ -160,6 +194,7 @@ export async function POST(request: Request) {
minecraftUuid: input.minecraftUuid,
previousUsername: account.username === input.username ? null : account.username,
uuidBackfilled: account.minecraftUuid === null,
ipIntelligence: auditIpData,
},
ipAddress: input.ipAddress,
correlationId: input.requestId,