1
0
Fork 0
polaris-task-force/src/app/api/realtime/presence/route.ts
Z8MB1E c0d00fc113 fix(auth): reject MCP API-key sessions in user auth paths
With the MCP plugin enabled, payload.auth() can return a
payload-mcp-api-keys doc instead of a User. Add an isPayloadUser
type guard and use it in every server action, page, and route that
treats the auth result as a real user, so API-key sessions can no
longer pass user auth checks. hasRoles now explicitly requires a
users-collection doc.
2026-08-25 12:27:35 -04:00

39 lines
1.6 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server";
import config from "@payload-config";
import { isPayloadUser } from "@/utils/access-control/isPayloadUser";
import { getPayload } from "payload";
import { removePresence, touchPresence } from "@/lib/realtime/presence";
export const dynamic = "force-dynamic";
export const runtime = "nodejs";
async function authenticate(req: NextRequest) {
const payload = await getPayload({ config: await config });
return payload.auth({ headers: req.headers, canSetHeaders: false });
}
function channelFrom(req: NextRequest): string | null {
const channel = req.nextUrl.searchParams.get("channel")?.trim();
return channel && /^[a-z0-9:_-]{1,80}$/i.test(channel) ? channel : null;
}
export async function POST(req: NextRequest) {
const channel = channelFrom(req);
if (!channel) return NextResponse.json({ ok: false, error: "Invalid channel." }, { status: 400 });
const { user } = await authenticate(req);
if (!isPayloadUser(user)) return NextResponse.json({ ok: false }, { status: 401 });
touchPresence(channel, {
id: user.id as number,
name: user.payloadDisplayName || user.username || `Pilot #${user.id}`,
});
return NextResponse.json({ ok: true });
}
export async function DELETE(req: NextRequest) {
const channel = channelFrom(req);
if (!channel) return NextResponse.json({ ok: false, error: "Invalid channel." }, { status: 400 });
const { user } = await authenticate(req);
if (!isPayloadUser(user)) return NextResponse.json({ ok: false }, { status: 401 });
removePresence(channel, user.id as number);
return NextResponse.json({ ok: true });
}